Skip to content

Commit 873bbe2

Browse files
committed
feat: use nondep flag in Expr.letE and LocalContext.ldecl
This PR implements first-class support for nondependent let expressions in the elaborator; recall that a let expression `let x := v; b` is called *nondependent* if `fun x => b` typechecks, and the notation for a nondependent let expression is `have x := v; b`. Previously we encoded `have` using the `letFun` function, but now we make use of the `nondep` flag in the `Expr.letE` constructor for the encoding. This has been fully supported throughout the metaprogramming interface and the elaborator. Note that nondependence of lets is not checked by the kernel. Follows up from leanprover#8751, which made sure the nondep flag was preserved in the C++ interface.
1 parent e2a947c commit 873bbe2

40 files changed

+475
-258
lines changed

src/Lean/Elab/Binders.lean

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -741,18 +741,12 @@ def elabLetDeclAux (id : Syntax) (binders : Array Syntax) (typeStx : Syntax) (va
741741
pure (type, val, binders)
742742
let kind := kindOfBinderName id.getId
743743
trace[Elab.let.decl] "{id.getId} : {type} := {val}"
744-
let result ← if useLetExpr then
745-
withLetDecl id.getId (kind := kind) type val fun x => do
744+
let result ←
745+
withLetDecl id.getId (kind := kind) type val (nondep := !useLetExpr) fun x => do
746746
addLocalVarInfo id x
747747
let body ← elabTermEnsuringType body expectedType?
748748
let body ← instantiateMVars body
749-
mkLetFVars #[x] body (usedLetOnly := usedLetOnly)
750-
else
751-
withLocalDecl id.getId (kind := kind) .default type fun x => do
752-
addLocalVarInfo id x
753-
let body ← elabTermEnsuringType body expectedType?
754-
let body ← instantiateMVars body
755-
mkLetFun x val body
749+
mkLetFVars #[x] body (usedLetOnly := usedLetOnly) (generalizeNondepLet := false)
756750
if elabBodyFirst then
757751
forallBoundedTelescope type binders.size fun xs type => do
758752
-- the original `fvars` from above are gone, so add back info manually

src/Lean/Elab/Match.lean

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -670,7 +670,7 @@ where
670670
match p with
671671
| .forallE n d b bi => withLocalDecl n bi (← go d) fun x => do mkForallFVars #[x] (← go (b.instantiate1 x))
672672
| .lam n d b bi => withLocalDecl n bi (← go d) fun x => do mkLambdaFVars #[x] (← go (b.instantiate1 x))
673-
| .letE n t v b .. => withLetDecl n (← go t) (← go v) fun x => do mkLetFVars #[x] (← go (b.instantiate1 x))
673+
| .letE n t v b nondep => mapLetDecl n (← go t) (← go v) (nondep := nondep) fun x => go (b.instantiate1 x)
674674
| .app f a => return mkApp (← go f) (← go a)
675675
| .proj _ _ b => return p.updateProj! (← go b)
676676
| .mdata k b =>

src/Lean/Elab/MutualDef.lean

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -824,10 +824,10 @@ private partial def mkClosureForAux (toProcess : Array FVarId) : StateRefT Closu
824824
| .cdecl _ _ userName type bi k =>
825825
let toProcess ← pushLocalDecl toProcess fvarId userName type bi k
826826
mkClosureForAux toProcess
827-
| .ldecl _ _ userName type val _ k =>
827+
| .ldecl _ _ userName type val nondep k =>
828828
let zetaDeltaFVarIds ← getZetaDeltaFVarIds
829-
if !zetaDeltaFVarIds.contains fvarId then
830-
/- Non-dependent let-decl. See comment at src/Lean/Meta/Closure.lean -/
829+
if nondep || !zetaDeltaFVarIds.contains fvarId then
830+
/- Nondependent let-decl. See comment at src/Lean/Meta/Closure.lean -/
831831
let toProcess ← pushLocalDecl toProcess fvarId userName type .default k
832832
mkClosureForAux toProcess
833833
else

src/Lean/Elab/PreDefinition/Main.lean

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ private partial def ensureNoUnassignedLevelMVarsAtPreDef (preDef : PreDefinition
9393
checkCache { val := e : ExprStructEq } fun _ => do
9494
match e with
9595
| .forallE n d b c | .lam n d b c => withExpr e do visit d; withLocalDecl n c d fun x => visit (b.instantiate1 x)
96-
| .letE n t v b _ => withExpr e do visit t; visit v; withLetDecl n t v fun x => visit (b.instantiate1 x)
96+
| .letE n t v b nondep => withExpr e do visit t; visit v; withLetDecl n t v (nondep := nondep) fun x => visit (b.instantiate1 x)
9797
| .mdata _ b => withExpr e do visit b
9898
| .proj _ _ b => withExpr e do visit b
9999
| .sort u => visitLevel u (← read)

src/Lean/Elab/PreDefinition/Structural/BRecOn.lean

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -133,9 +133,11 @@ private partial def replaceRecApps (recArgInfos : Array RecArgInfo) (positions :
133133
| Expr.forallE n d b c =>
134134
withLocalDecl n c (← loop below d) fun x => do
135135
mkForallFVars #[x] (← loop below (b.instantiate1 x))
136-
| Expr.letE n type val body _ =>
137-
withLetDecl n (← loop below type) (← loop below val) fun x => do
138-
mkLetFVars #[x] (← loop below (body.instantiate1 x)) (usedLetOnly := false)
136+
| Expr.letE n type val body nondep =>
137+
-- Turn `have`s into `let`s if they are not propositions.
138+
let nondep ← pure nondep <&&> not <$> Meta.isProp type
139+
mapLetDecl n (← loop below type) (← loop below val) (nondep := nondep) (usedLetOnly := false) fun x => do
140+
loop below (body.instantiate1 x)
139141
| Expr.mdata d b =>
140142
if let some stx := getRecAppSyntax? e then
141143
withRef stx <| loop below b

src/Lean/Elab/PreDefinition/Structural/IndPred.lean

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,9 @@ private partial def replaceIndPredRecApps (recArgInfo : RecArgInfo) (funType : E
5050
| Expr.forallE n d b c =>
5151
withLocalDecl n c (← loop d) fun x => do
5252
mkForallFVars #[x] (← loop (b.instantiate1 x))
53-
| Expr.letE n type val body _ =>
54-
withLetDecl n (← loop type) (← loop val) fun x => do
55-
mkLetFVars #[x] (← loop (body.instantiate1 x))
53+
| Expr.letE n type val body nondep =>
54+
mapLetDecl n (← loop type) (← loop val) (nondep := nondep) fun x => do
55+
loop (body.instantiate1 x)
5656
| Expr.mdata d b => do
5757
if let some stx := getRecAppSyntax? e then
5858
withRef stx <| loop b

src/Lean/Elab/PreDefinition/Structural/SmartUnfolding.lean

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,9 @@ where
3232
match e with
3333
| Expr.lam .. => lambdaTelescope e fun xs b => do mkLambdaFVars xs (← visit b)
3434
| Expr.forallE .. => forallTelescope e fun xs b => do mkForallFVars xs (← visit b)
35-
| Expr.letE n type val body _ =>
36-
withLetDecl n type (← visit val) fun x => do mkLetFVars #[x] (← visit (body.instantiate1 x))
35+
| Expr.letE n type val body nondep =>
36+
mapLetDecl n type (← visit val) (nondep := nondep) fun x => do
37+
visit (body.instantiate1 x)
3738
| Expr.mdata d b => return mkMData d (← visit b)
3839
| Expr.proj n i s => return mkProj n i (← visit s)
3940
| Expr.app .. =>

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

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,9 +84,12 @@ where
8484
| Expr.forallE n d b c =>
8585
withLocalDecl n c (← loop F d) fun x => do
8686
mkForallFVars #[x] (← loop F (b.instantiate1 x))
87-
| Expr.letE n type val body _ =>
88-
withLetDecl n (← loop F type) (← loop F val) fun x => do
89-
mkLetFVars #[x] (← loop F (body.instantiate1 x)) (usedLetOnly := false)
87+
| Expr.letE n type val body nondep =>
88+
-- Convert `have`s to `let`s if they aren't propositions so that the values
89+
-- of local declarations can be used within decreasing-by proofs.
90+
let nondep ← pure nondep <&&> Meta.isProp type
91+
mapLetDecl n (← loop F type) (← loop F val) (nondep := nondep) (usedLetOnly := false) fun x => do
92+
loop F (body.instantiate1 x)
9093
| Expr.mdata d b =>
9194
if let some stx := getRecAppSyntax? e then
9295
withRef stx <| loop F b

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -241,10 +241,12 @@ where
241241
loop param d
242242
withLocalDecl n c d fun x => do
243243
loop param (b.instantiate1 x)
244-
| Expr.letE n type val body _ =>
244+
| Expr.letE n type val body nondep =>
245245
loop param type
246246
loop param val
247-
withLetDecl n type val fun x => do
247+
-- Convert `have`s to `let`s if they aren't propositions so that the values of local declarations can be used.
248+
let nondep ← pure nondep <&&> Meta.isProp type
249+
withLetDecl n type val (nondep := nondep) fun x => do
248250
loop param (body.instantiate1 x)
249251
| Expr.mdata _d b =>
250252
if let some stx := getRecAppSyntax? e then

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

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -110,14 +110,16 @@ builtin_dsimproc paramMatcher (_) := fun e => do
110110
let matcherApp' := { matcherApp with discrs := discrs', alts := alts' }
111111
return .continue <| matcherApp'.toExpr
112112

113-
/-- `let x := (wfParam e); body[x] ==> let x := e; body[wfParam y] -/
113+
/-- `let x := (wfParam e); body[x] ==> let x := e; body[wfParam y]`
114+
Also accepts non-Prop `have`s as well. -/
114115
builtin_dsimproc paramLet (_) := fun e => do
115-
unless e.isLet do return .continue
116-
let some v := isWfParam? e.letValue! | return .continue
117-
let u ← getLevel e.letType!
118-
let body' := e.letBody!.instantiate1 <|
119-
mkApp2 (.const ``wfParam [u]) e.letType! (.bvar 0)
120-
return .continue <| e.updateLetE! e.letType! v body'
116+
let .letE _ t v b _ := e | return .continue
117+
let some v := isWfParam? v | return .continue
118+
if ← Meta.isProp t then return .continue <| e.updateLetE! t v b
119+
let u ← getLevel t
120+
let body' := b.instantiate1 <|
121+
mkApp2 (.const ``wfParam [u]) t (.bvar 0)
122+
return .continue <| e.updateLetE! t v body'
121123

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

0 commit comments

Comments
 (0)