Skip to content

Commit 3086957

Browse files
committed
feat: add the nonDep field of Expr.letE to the C++ data model
This PR adds the `nonDep` field of `Expr.letE` to the C++ data model. Previously this was unused, and in followup PRs the elaborator will use it to encode `have` expressions (non-dependent `let`s). The kernel does not verify that `nonDep` is correctly applied during typechecking. Breaking change: now `Expr.updateLet!` is `Expr.updateLetE!`. This PR also fixes a bug in `Expr.letFun?` and `Expr.letFunAppArgs?` when the body is not a lambda. In any case, these functions will be removed once the `Expr.letE (nonDep := true)` encoding of `have` expressions is complete.
1 parent faffe86 commit 3086957

File tree

24 files changed

+169
-55
lines changed

24 files changed

+169
-55
lines changed

src/Lean/Compiler/LCNF/Level.lean

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ partial def normExpr (e : Expr) : M Expr := do
6767
| .const _ us => return e.updateConst! (← us.mapM normLevel)
6868
| .sort u => return e.updateSort! (← normLevel u)
6969
| .app f a => return e.updateApp! (← normExpr f) (← normExpr a)
70-
| .letE _ t v b _ => return e.updateLet! (← normExpr t) (← normExpr v) (← normExpr b)
70+
| .letE _ t v b _ => return e.updateLetE! (← normExpr t) (← normExpr v) (← normExpr b)
7171
| .forallE _ d b _ => return e.updateForallE! (← normExpr d) (← normExpr b)
7272
| .lam _ d b _ => return e.updateLambdaE! (← normExpr d) (← normExpr b)
7373
| .mdata _ b => return e.updateMData! (← normExpr b)

src/Lean/Elab/PreDefinition/WF/Preprocess.lean

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ builtin_dsimproc paramLet (_) := fun e => do
117117
let u ← getLevel e.letType!
118118
let body' := e.letBody!.instantiate1 <|
119119
mkApp2 (.const ``wfParam [u]) e.letType! (.bvar 0)
120-
return .continue <| e.updateLet! e.letType! v body'
120+
return .continue <| e.updateLetE! e.letType! v body'
121121

122122
def preprocess (e : Expr) : MetaM Simp.Result := do
123123
unless wf.preprocess.get (← getOptions) do

src/Lean/Expr.lean

Lines changed: 40 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -391,20 +391,19 @@ inductive Expr where
391391
/--
392392
Let-expressions.
393393
394-
**IMPORTANT**: The `nonDep` flag is for "local" use only. That is, a module should not "trust" its value for any purpose.
395-
In the intended use-case, the compiler will set this flag, and be responsible for maintaining it.
396-
Other modules may not preserve its value while applying transformations.
394+
The let-expression `let x : Nat := 2; Nat.succ x` is represented as
395+
```
396+
Expr.letE `x (.const `Nat []) (.lit (.natVal 2)) (.app (.const `Nat.succ []) (.bvar 0)) true
397+
```
397398
399+
If the `nonDep` flag is `true`, then the elaborator treats this as a *non-dependent `let`* (known as a *`have` expression*).
398400
Given an environment, a metavariable context, and a local context,
399401
we say a let-expression `let x : t := v; e` is non-dependent when it is equivalent
400402
to `(fun x : t => e) v`. In contrast, the dependent let-expression
401403
`let n : Nat := 2; fun (a : Array Nat n) (b : Array Nat 2) => a = b` is type correct,
402404
but `(fun (n : Nat) (a : Array Nat n) (b : Array Nat 2) => a = b) 2` is not.
403405
404-
The let-expression `let x : Nat := 2; Nat.succ x` is represented as
405-
```
406-
Expr.letE `x (.const `Nat []) (.lit (.natVal 2)) (.app (.const `Nat.succ []) (.bvar 0)) true
407-
```
406+
The kernel does not pay attention to `nonDep` when type checking.
408407
-/
409408
| letE (declName : Name) (type : Expr) (value : Expr) (body : Expr) (nonDep : Bool)
410409

@@ -671,12 +670,14 @@ def mkSimpleThunkType (type : Expr) : Expr :=
671670
def mkSimpleThunk (type : Expr) : Expr :=
672671
mkLambda `_ BinderInfo.default (mkConst `Unit) type
673672

674-
/--
675-
`.letE x t v b nonDep` is now the preferred form.
676-
-/
677-
def mkLet (x : Name) (t : Expr) (v : Expr) (b : Expr) (nonDep : Bool := false) : Expr :=
673+
/-- Returns `let x : t := v; b`, a dependent `let` expression. If `nonDep := true`, then returns a `have`. -/
674+
@[inline] def mkLet (x : Name) (t : Expr) (v : Expr) (b : Expr) (nonDep : Bool := false) : Expr :=
678675
.letE x t v b nonDep
679676

677+
/-- Returns `have x : t := v; b`, a non-dependent `let` expression. -/
678+
@[inline] def mkHave (x : Name) (t : Expr) (v : Expr) (b : Expr) : Expr :=
679+
.letE x t v b true
680+
680681
@[match_pattern] def mkAppB (f a b : Expr) := mkApp (mkApp f a) b
681682
@[match_pattern] def mkApp2 (f a b : Expr) := mkAppB f a b
682683
@[match_pattern] def mkApp3 (f a b c : Expr) := mkApp (mkAppB f a b) c
@@ -723,7 +724,7 @@ def mkStrLit (s : String) : Expr :=
723724
@[export lean_expr_mk_app] def mkAppEx : Expr → Expr → Expr := mkApp
724725
@[export lean_expr_mk_lambda] def mkLambdaEx (n : Name) (d b : Expr) (bi : BinderInfo) : Expr := mkLambda n bi d b
725726
@[export lean_expr_mk_forall] def mkForallEx (n : Name) (d b : Expr) (bi : BinderInfo) : Expr := mkForall n bi d b
726-
@[export lean_expr_mk_let] def mkLetEx (n : Name) (t v b : Expr) : Expr := mkLet n t v b
727+
@[export lean_expr_mk_let] def mkLetEx (n : Name) (t v b : Expr) (nonDep : Bool) : Expr := mkLet n t v b nonDep
727728
@[export lean_expr_mk_lit] def mkLitEx : Literal → Expr := mkLit
728729
@[export lean_expr_mk_mdata] def mkMDataEx : MData → Expr → Expr := mkMData
729730
@[export lean_expr_mk_proj] def mkProjEx : Name → Nat → Expr → Expr := mkProj
@@ -867,11 +868,18 @@ def isBinding : Expr → Bool
867868
| forallE .. => true
868869
| _ => false
869870

870-
/-- Return `true` if the given expression is a let-expression. -/
871+
/-- Return `true` if the given expression is a let-expression or a have-expression. -/
871872
def isLet : Expr → Bool
872873
| letE .. => true
873874
| _ => false
874875

876+
/-- Return `true` if the given expression is a non-dependent let-expression (a have-expression). -/
877+
def isHave : Expr → Bool
878+
| letE (nonDep := nonDep) .. => nonDep
879+
| _ => false
880+
881+
@[export lean_expr_isHave] def isHaveEx : Expr → Bool := isHave
882+
875883
/-- Return `true` if the given expression is a metadata. -/
876884
def isMData : Expr → Bool
877885
| mdata .. => true
@@ -1012,6 +1020,10 @@ def letBody! : Expr → Expr
10121020
| letE _ _ _ b .. => b
10131021
| _ => panic! "let expression expected"
10141022

1023+
def letNonDep! : Expr → Bool
1024+
| letE _ _ _ _ nonDep => nonDep
1025+
| _ => panic! "let expression expected"
1026+
10151027
def consumeMData : Expr → Expr
10161028
| mdata _ e => consumeMData e
10171029
| e => e
@@ -1842,21 +1854,28 @@ def updateLambda! (e : Expr) (newBinfo : BinderInfo) (newDomain : Expr) (newBody
18421854
| lam n d b bi => updateLambda! (lam n d b bi) bi newDomain newBody
18431855
| _ => panic! "lambda expected"
18441856

1845-
@[inline] private unsafe def updateLet!Impl (e : Expr) (newType : Expr) (newVal : Expr) (newBody : Expr) : Expr :=
1857+
@[inline] private unsafe def updateLet!Impl (e : Expr) (newType : Expr) (newVal : Expr) (newBody : Expr) (newNonDep : Bool) : Expr :=
18461858
match e with
18471859
| letE n t v b nonDep =>
1848-
if ptrEq t newType && ptrEq v newVal && ptrEq b newBody then
1860+
if ptrEq t newType && ptrEq v newVal && ptrEq b newBody && nonDep == newNonDep then
18491861
e
18501862
else
1851-
letE n newType newVal newBody nonDep
1863+
letE n newType newVal newBody newNonDep
18521864
| _ => panic! "let expression expected"
18531865

18541866
@[implemented_by updateLet!Impl]
1855-
def updateLet! (e : Expr) (newType : Expr) (newVal : Expr) (newBody : Expr) : Expr :=
1867+
def updateLet! (e : Expr) (newType : Expr) (newVal : Expr) (newBody : Expr) (newNonDep : Bool) : Expr :=
18561868
match e with
1857-
| letE n _ _ _ c => letE n newType newVal newBody c
1869+
| letE n _ _ _ _ => letE n newType newVal newBody newNonDep
18581870
| _ => panic! "let expression expected"
18591871

1872+
/-- Like `Expr.updateLet!` but preserves the `nonDep` flag. -/
1873+
@[inline]
1874+
def updateLetE! (e : Expr) (newType : Expr) (newVal : Expr) (newBody : Expr) : Expr :=
1875+
match e with
1876+
| letE n t v b nonDep => updateLet! (letE n t v b nonDep) newType newVal newBody nonDep
1877+
| _ => panic! "let expression expected"
1878+
18601879
def updateFn : Expr → Expr → Expr
18611880
| e@(app f a), g => e.updateApp! (updateFn f g) a
18621881
| _, g => g
@@ -1965,7 +1984,7 @@ def letFun? (e : Expr) : Option (Name × Expr × Expr × Expr) :=
19651984
| .app (.app (.app (.app (.const ``letFun _) t) _β) v) f =>
19661985
match f with
19671986
| .lam n _ b _ => some (n, t, v, b)
1968-
| _ => some (.anonymous, t, v, .app f (.bvar 0))
1987+
| _ => some (.anonymous, t, v, .app (f.liftLooseBVars 0 1) (.bvar 0))
19691988
| _ => none
19701989

19711990
/--
@@ -1982,15 +2001,15 @@ def letFunAppArgs? (e : Expr) : Option (Array Expr × Name × Expr × Expr × Ex
19822001
let rest := args.extract 4 args.size
19832002
match f with
19842003
| .lam n _ b _ => some (rest, n, t, v, b)
1985-
| _ => some (rest, .anonymous, t, v, .app f (.bvar 0))
2004+
| _ => some (rest, .anonymous, t, v, .app (f.liftLooseBVars 0 1) (.bvar 0))
19862005

19872006
/-- Maps `f` on each immediate child of the given expression. -/
19882007
@[specialize]
19892008
def traverseChildren [Applicative M] (f : Expr → M Expr) : Expr → M Expr
19902009
| e@(forallE _ d b _) => pure e.updateForallE! <*> f d <*> f b
19912010
| e@(lam _ d b _) => pure e.updateLambdaE! <*> f d <*> f b
19922011
| e@(mdata _ b) => e.updateMData! <$> f b
1993-
| e@(letE _ t v b _) => pure e.updateLet! <*> f t <*> f v <*> f b
2012+
| e@(letE _ t v b _) => pure e.updateLetE! <*> f t <*> f v <*> f b
19942013
| e@(app l r) => pure e.updateApp! <*> f l <*> f r
19952014
| e@(proj _ _ b) => e.updateProj! <$> f b
19962015
| e => pure e

src/Lean/Meta/AbstractMVars.lean

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ partial def abstractExprMVars (e : Expr) : M Expr := do
7878
| e@(Expr.mdata _ b) => return e.updateMData! (← abstractExprMVars b)
7979
| e@(Expr.lam _ d b _) => return e.updateLambdaE! (← abstractExprMVars d) (← abstractExprMVars b)
8080
| e@(Expr.forallE _ d b _) => return e.updateForallE! (← abstractExprMVars d) (← abstractExprMVars b)
81-
| e@(Expr.letE _ t v b _) => return e.updateLet! (← abstractExprMVars t) (← abstractExprMVars v) (← abstractExprMVars b)
81+
| e@(Expr.letE _ t v b _) => return e.updateLetE! (← abstractExprMVars t) (← abstractExprMVars v) (← abstractExprMVars b)
8282
| e@(Expr.mvar mvarId) =>
8383
let decl := (← getMCtx).getDecl mvarId
8484
if decl.depth != (← getMCtx).depth then

src/Lean/Meta/Closure.lean

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ partial def collectExprAux (e : Expr) : ClosureM Expr := do
193193
| Expr.proj _ _ s => return e.updateProj! (← collect s)
194194
| Expr.forallE _ d b _ => return e.updateForallE! (← collect d) (← collect b)
195195
| Expr.lam _ d b _ => return e.updateLambdaE! (← collect d) (← collect b)
196-
| Expr.letE _ t v b _ => return e.updateLet! (← collect t) (← collect v) (← collect b)
196+
| Expr.letE _ t v b _ => return e.updateLetE! (← collect t) (← collect v) (← collect b)
197197
| Expr.app f a => return e.updateApp! (← collect f) (← collect a)
198198
| Expr.mdata _ b => return e.updateMData! (← collect b)
199199
| Expr.sort u => return e.updateSort! (← collectLevel u)

src/Lean/Meta/ExprDefEq.lean

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -851,7 +851,7 @@ mutual
851851
| .proj _ _ s => return e.updateProj! (← check s)
852852
| .lam _ d b _ => return e.updateLambdaE! (← check d) (← check b)
853853
| .forallE _ d b _ => return e.updateForallE! (← check d) (← check b)
854-
| .letE _ t v b _ => return e.updateLet! (← check t) (← check v) (← check b)
854+
| .letE _ t v b _ => return e.updateLetE! (← check t) (← check v) (← check b)
855855
| .bvar .. => return e
856856
| .sort .. => return e
857857
| .const .. => return e

src/Lean/Meta/ExprLens.lean

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ private def lensCoord (g : Expr → M Expr) (n : Nat) (e : Expr) : M Expr := do
3636
| 1, .lam n y b c => withLocalDecl n c y fun x => do mkLambdaFVars #[x] <|← g <| b.instantiateRev #[x]
3737
| 0, .forallE _ y b _ => return e.updateForallE! (← g y) b
3838
| 1, .forallE n y b c => withLocalDecl n c y fun x => do mkForallFVars #[x] <|← g <| b.instantiateRev #[x]
39-
| 0, .letE _ y a b _ => return e.updateLet! (← g y) a b
40-
| 1, .letE _ y a b _ => return e.updateLet! y (← g a) b
39+
| 0, .letE _ y a b _ => return e.updateLetE! (← g y) a b
40+
| 1, .letE _ y a b _ => return e.updateLetE! y (← g a) b
4141
| 2, .letE n y a b _ => withLetDecl n y a fun x => do mkLetFVars #[x] <|← g <| b.instantiateRev #[x]
4242
| 0, .proj _ _ b => e.updateProj! <$> g b
4343
| n, .mdata _ a => e.updateMData! <$> lensCoord g n a

src/Lean/Meta/InferType.lean

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ where
3838
match e with
3939
| .forallE _ d b _ => return e.updateForallE! (← visit d offset) (← visit b (offset+1))
4040
| .lam _ d b _ => return e.updateLambdaE! (← visit d offset) (← visit b (offset+1))
41-
| .letE _ t v b _ => return e.updateLet! (← visit t offset) (← visit v offset) (← visit b (offset+1))
41+
| .letE _ t v b _ => return e.updateLetE! (← visit t offset) (← visit v offset) (← visit b (offset+1))
4242
| .mdata _ b => return e.updateMData! (← visit b offset)
4343
| .proj _ _ b => return e.updateProj! (← visit b offset)
4444
| .app .. =>

src/Lean/Meta/KAbstract.lean

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ def kabstract (e : Expr) (p : Expr) (occs : Occurrences := .all) : MetaM Expr :=
3636
| .app f a => return e.updateApp! (← visit f offset) (← visit a offset)
3737
| .mdata _ b => return e.updateMData! (← visit b offset)
3838
| .proj _ _ b => return e.updateProj! (← visit b offset)
39-
| .letE _ t v b _ => return e.updateLet! (← visit t offset) (← visit v offset) (← visit b (offset+1))
39+
| .letE _ t v b _ => return e.updateLetE! (← visit t offset) (← visit v offset) (← visit b (offset+1))
4040
| .lam _ d b _ => return e.updateLambdaE! (← visit d offset) (← visit b (offset+1))
4141
| .forallE _ d b _ => return e.updateForallE! (← visit d offset) (← visit b (offset+1))
4242
| e => return e

src/Lean/Meta/SynthInstance.lean

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ partial def normExpr (e : Expr) : M Expr := do
135135
| .const _ us => return e.updateConst! (← us.mapM normLevel)
136136
| .sort u => return e.updateSort! (← normLevel u)
137137
| .app f a => return e.updateApp! (← normExpr f) (← normExpr a)
138-
| .letE _ t v b _ => return e.updateLet! (← normExpr t) (← normExpr v) (← normExpr b)
138+
| .letE _ t v b _ => return e.updateLetE! (← normExpr t) (← normExpr v) (← normExpr b)
139139
| .forallE _ d b _ => return e.updateForallE! (← normExpr d) (← normExpr b)
140140
| .lam _ d b _ => return e.updateLambdaE! (← normExpr d) (← normExpr b)
141141
| .mdata _ b => return e.updateMData! (← normExpr b)

0 commit comments

Comments
 (0)