Skip to content

Commit 4348c65

Browse files
authored
Denotational Semantics for Lambda (strata-org#865)
*Issue #, if available:* *Description of changes:* This PR adds a denotational semantics for Lambda and proofs of consistency between this semantics and the existing small-step operational semantics. The structure of the added files is described in `Strata/DL/Lambda/Denote/README.md`. Some tests using the semantics are in `StrataTest/DL/Lambda/Denote/Tautologies.lean`. Beyond the denotational semantics itself, this PR includes several changes to existing Lambda infrastructure: ### Factory (`Factory.lean`) - Added a `nameMapConsistent` field ensuring that each key in the name map matches the name of the element it points to. This is needed to prove that factory lookups return the expected function. - Added several utility theorems (`push_mem_iff`, `mem_iff_mem_names`, `getElem?_some_implies_mem`, `mem_name_eq_getElem`, etc.) used throughout the denotation proofs. ### Type substitution (`LTyUnify.lean`, `LTy.lean`) - Added `LMonoTy.subst_unfold`, which gives a clean unfolding lemma for `LMonoTy.subst` that eliminates the `hasEmptyScopes` guard. This is essential for reasoning about type substitution in the denotation proofs. - Added `LMonoTy.mkArrow'_injective` and supporting lemmas (`subst_bitvec`, `subst_nil`, `hasEmptyScopes_false_of_find`, etc.). ### TypeFactory derived functions (`TypeFactory.lean`, `TypeFactoryWF.lean`) - Fixed type annotations in `testerFuncBody` and `testerFunc`: the eliminator op and free variable references now carry their types (`some elimType`, `some (dataDefault d)`) instead of `none`. This is required for the well-typedness assumptions used by the denotational semantics. - Added `genRecTyDirect` to compute concrete case argument types for tester functions. - Added `TypeFactoryWF` structure capturing well-formedness of a `TypeFactory` (currently: unique datatype names across mutual blocks). ### Other changes - `Semantics.lean`: Simplified `callOfLFunc_getLFuncCall` and `computeTypeSubst_eraseMetadata_congr` proofs. - `LExprWF.lean`: Added `liftBVars_eq_of_lcAt` and `@[expose]` on `substK`/`subst`. - `Strata/DL/Util/`: Added utility lemmas for `Map`, `ListMap`, `Maps`, and `List` used by the denotation proofs. This PR introduces several `sorry`s in a test file because `HashMaps` do not reduce in the Lean kernel. I will try to fix these in a future PR. By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice. --------- Co-authored-by: Josh Cohen <cohenjo@amazon.com>
1 parent 6c9424f commit 4348c65

29 files changed

Lines changed: 8941 additions & 102 deletions
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
/-
2+
Copyright Strata Contributors
3+
4+
SPDX-License-Identifier: Apache-2.0 OR MIT
5+
-/
6+
module
7+
8+
import all Strata.DL.Lambda.Denote.LExprAnnotated
9+
import all Strata.DL.Lambda.Semantics
10+
import all Strata.DL.Lambda.TypeFactoryWF
11+
import all Strata.DL.Lambda.FactoryWF
12+
13+
/-!
14+
## Typing Assumptions
15+
16+
Collects all well-formedness and typing assumptions used by the denotational
17+
semantics proofs. Semantic (denotation-level) assumptions live in
18+
`LExprSemanticsConsistent`.
19+
20+
### Expression-level predicates (TODO: prove satisfied after `LExpr.resolve`)
21+
- `fvars_annotated_by` — free variable annotations match a type map
22+
- `OpsConsistent` — operator type annotations are valid factory instantiations
23+
24+
### Factory assumptions (TODO: prove satisfied after `Function.typeCheck`)
25+
- `Factory.WellTyped` / `Factory.EvalWellTyped` — bodies and evaluators preserve typing
26+
- `Factory.ConstrWellFormed` — constructor functions match the type factory
27+
- `Factory.BodyOpsConsistent` / `Factory.EvalOpsConsistent` — bodies and evaluators preserve `OpsConsistent`
28+
- `Factory.BodyAnnotated` / `Factory.EvalAnnotated` — bodies and evaluators preserve annotations
29+
30+
### Environment assumptions (TODO: prove satisfied by `Program.typeCheck`)
31+
- `Env.Typed` — environment values are well-typed
32+
33+
### Bundled assumptions
34+
- `Env.StepWF` — environment well-formedness for step preservation
35+
- `Factory.StepWF` — factory well-formedness for step preservation
36+
- `Factory.WF` — factory and type-factory well-formedness
37+
-/
38+
39+
namespace Lambda
40+
41+
variable {T : LExprParams} [DecidableEq T.IDMeta]
42+
43+
/-! ### Expression-level predicates -/
44+
45+
/-- Every fvar in `e` whose name is in `tyMap` is annotated with the
46+
corresponding type. -/
47+
def fvars_annotated_by [DecidableEq T.IDMeta]
48+
(tyMap : Map T.Identifier LMonoTy) : LExpr T.mono → Prop
49+
| .fvar _ name (some ty) =>
50+
∀ ty', Map.find? tyMap name = some ty' → ty = ty'
51+
| .fvar _ _ none => False
52+
| .const _ _ => True
53+
| .bvar _ _ => True
54+
| .op _ _ _ => True
55+
| .app _ fn arg => fvars_annotated_by tyMap fn ∧ fvars_annotated_by tyMap arg
56+
| .abs _ _ _ body => fvars_annotated_by tyMap body
57+
| .ite _ c t e => fvars_annotated_by tyMap c ∧ fvars_annotated_by tyMap t ∧ fvars_annotated_by tyMap e
58+
| .eq _ e1 e2 => fvars_annotated_by tyMap e1 ∧ fvars_annotated_by tyMap e2
59+
| .quant _ _ _ _ tr body => fvars_annotated_by tyMap tr ∧ fvars_annotated_by tyMap body
60+
61+
/-- Every `.op` node in `e` whose name is in the factory has a type annotation
62+
that is a valid instantiation of the function's generic type (via `opTypeSubst`).
63+
This is checked at every `.op` node directly, not just at complete calls. -/
64+
def OpsConsistent (F : @Factory T) : LExpr T.mono → Prop := fun e =>
65+
match e with
66+
| .op _ name ty =>
67+
match F[name.name]? with
68+
| some fn =>
69+
match LFunc.opTypeSubst fn e with
70+
| some tySubst =>
71+
match ty with
72+
| some ty_op =>
73+
ty_op = (LMonoTy.mkArrow' fn.output (fn.inputs.map Prod.snd)).subst tySubst
74+
| none => False
75+
| none => False
76+
| none => True
77+
| .app _ fn arg => OpsConsistent F fn ∧ OpsConsistent F arg
78+
| .abs _ _ _ body => OpsConsistent F body
79+
| .ite _ c t f => OpsConsistent F c ∧ OpsConsistent F t ∧ OpsConsistent F f
80+
| .eq _ e1 e2 => OpsConsistent F e1 ∧ OpsConsistent F e2
81+
| .quant _ _ _ _ tr body => OpsConsistent F tr ∧ OpsConsistent F body
82+
| _ => True
83+
84+
/-! ### Factory assumptions -/
85+
86+
/-- A factory is well-typed when every function body type-checks at the
87+
function's declared output type. -/
88+
def Factory.WellTyped [DecidableEq T.IDMeta] (F : @Factory T) : Prop :=
89+
∀ (f : String), (hf : f ∈ F) → ∀ body, (F[f]).body = some body →
90+
LExpr.HasTypeA [] body (F[f]).output ∧
91+
fvars_annotated_by (F[f]).inputs body
92+
93+
/-- A factory's concrete evaluators preserve well-typedness: if `ceval` returns
94+
a result and the arguments are well-typed at the instantiated input types,
95+
then the result is well-typed at the instantiated output type. -/
96+
def Factory.EvalWellTyped [DecidableEq T.IDMeta] (F : @Factory T) : Prop :=
97+
∀ (f : String), (hf : f ∈ F) → ∀ ceval, (F[f]).concreteEval = some ceval →
98+
∀ (md : T.Metadata) (args : List (LExpr T.mono)) (result : LExpr T.mono) (tySubst : Subst),
99+
ceval md args = some result →
100+
List.Forall₂ (LExpr.HasTypeA []) args ((F[f]).inputs.map Prod.snd |>.map (LMonoTy.subst tySubst)) →
101+
LExpr.HasTypeA [] result (LMonoTy.subst tySubst (F[f]).output)
102+
103+
/-- `isConstr` faithfulness: `f.isConstr = true` implies `f` was generated
104+
from a constructor in the TypeFactory. -/
105+
def Factory.ConstrWellFormed (F : @Factory T) (tf : @TypeFactory T.IDMeta) : Prop :=
106+
∀ (f : LFunc T),
107+
f ∈ F.toArray →
108+
f.isConstr = true →
109+
∃ (d : LDatatype T.IDMeta) (_ : d ∈ tf.allDatatypes)
110+
(c : LConstr T.IDMeta) (_ : c ∈ d.constrs),
111+
f = constrFunc c d
112+
113+
/-- Every function body in the factory satisfies `OpsConsistent` after type
114+
instantiation via `applySubst`. -/
115+
def Factory.BodyOpsConsistent (F : @Factory T) : Prop :=
116+
∀ (f : String), (hf : f ∈ F) → ∀ body S, (F[f]).body = some body →
117+
OpsConsistent F (body.applySubst S)
118+
119+
/-- Every concrete evaluator in the factory returns results that satisfy
120+
`OpsConsistent`. -/
121+
def Factory.EvalOpsConsistent (F : @Factory T) : Prop :=
122+
∀ (f : String), (hf : f ∈ F) → ∀ ceval md args result, (F[f]).concreteEval = some ceval →
123+
.some result = ceval md args → OpsConsistent F result
124+
125+
/-- Every function body in the factory, after type instantiation, has fvar
126+
annotations consistent with `tyMap`. -/
127+
def Factory.BodyAnnotated [DecidableEq T.IDMeta] (F : @Factory T)
128+
(tyMap : Map T.Identifier LMonoTy) : Prop :=
129+
∀ (f : String), (hf : f ∈ F) → ∀ body S, (F[f]).body = some body →
130+
fvars_annotated_by tyMap (body.applySubst S)
131+
132+
/-- Every concrete evaluator in the factory returns results with fvar
133+
annotations consistent with `tyMap`. -/
134+
def Factory.EvalAnnotated [DecidableEq T.IDMeta] (F : @Factory T)
135+
(tyMap : Map T.Identifier LMonoTy) : Prop :=
136+
∀ (f : String), (hf : f ∈ F) → ∀ ceval md args result, (F[f]).concreteEval = some ceval →
137+
.some result = ceval md args → fvars_annotated_by tyMap result
138+
139+
/-! ### Environment assumptions -/
140+
141+
/-- Environment values are well-typed: there is a type map `tyMap` such that
142+
every env value is well-typed at its corresponding type, and every env key
143+
has a type in the map. -/
144+
structure Env.Typed [DecidableEq T.IDMeta] (env : Env T) where
145+
/-- The type map assigning types to environment keys. -/
146+
tyMap : Map T.Identifier LMonoTy
147+
/-- Every env value is well-typed at the type given by `tyMap`. -/
148+
wt : ∀ (x : T.Identifier) (e : LExpr T.mono) (ty : LMonoTy),
149+
env x = some e → Map.find? tyMap x = some ty → LExpr.HasTypeA [] e ty
150+
/-- Every env key has a type in `tyMap`. -/
151+
cover : ∀ (x : T.Identifier) (e : LExpr T.mono),
152+
env x = some e → ∃ ty, Map.find? tyMap x = some ty
153+
154+
/-! ### Bundled assumptions -/
155+
156+
/-- Bundled well-formedness assumptions on the environment for step preservation.
157+
- `typed` : `Env.Typed env` — environment maps identifiers to well-typed expressions
158+
- `lc` : environment values are locally closed (`lcAt 0`)
159+
- `ops` : environment values satisfy `OpsConsistent F`
160+
- `annot` : environment values satisfy `fvars_annotated_by typed.tyMap`
161+
-/
162+
structure Env.StepWF (F : @Factory T) (env : Env T) where
163+
typed : Env.Typed env
164+
lc : ∀ (x : T.Identifier) (e : LExpr T.mono), env x = some e → LExpr.lcAt 0 e = true
165+
ops : ∀ (x : T.Identifier) (e : LExpr T.mono), env x = some e → OpsConsistent F e
166+
annot : ∀ (x : T.Identifier) (e : LExpr T.mono), env x = some e →
167+
fvars_annotated_by typed.tyMap e
168+
169+
/-- Bundled well-formedness assumptions on the factory for step preservation.
170+
- `wt` : `Factory.WellTyped F` — factory function bodies are well-typed
171+
- `evalWt` : `Factory.EvalWellTyped F` — concrete evaluators return well-typed results
172+
- `bodyOps` : `Factory.BodyOpsConsistent F` — factory bodies satisfy `OpsConsistent` after substitution
173+
- `evalOps` : `Factory.EvalOpsConsistent F` — concrete evaluators return `OpsConsistent` results
174+
- `bodyAnnot` : `Factory.BodyAnnotated F tyMap` — factory bodies satisfy `fvars_annotated_by` after substitution
175+
- `evalAnnot` : `Factory.EvalAnnotated F tyMap` — concrete evaluators return annotated results
176+
-/
177+
structure Factory.StepWF (F : @Factory T) (tyMap : Map T.Identifier LMonoTy) where
178+
wt : Factory.WellTyped F
179+
evalWt : Factory.EvalWellTyped F
180+
bodyOps : Factory.BodyOpsConsistent F
181+
evalOps : Factory.EvalOpsConsistent F
182+
bodyAnnot : Factory.BodyAnnotated F tyMap
183+
evalAnnot : Factory.EvalAnnotated F tyMap
184+
185+
/-- Bundled well-formedness assumptions on the factory and type factory.
186+
- `factoryWF` : `FactoryWF F` — factory is well-formed
187+
- `constrWF` : `Factory.ConstrWellFormed F tf` — constructors are well-formed w.r.t. type factory
188+
- `tfWF` : `TypeFactoryWF tf` — type factory is well-formed
189+
-/
190+
structure Factory.WF (F : @Factory T) (tf : @TypeFactory T.IDMeta) where
191+
factoryWF : FactoryWF F
192+
constrWF : Factory.ConstrWellFormed F tf
193+
tfWF : TypeFactoryWF tf
194+
195+
end Lambda

0 commit comments

Comments
 (0)