Skip to content

Commit 8c4d1cf

Browse files
committed
Joachim review
1 parent c529bac commit 8c4d1cf

File tree

5 files changed

+49
-23
lines changed

5 files changed

+49
-23
lines changed

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

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,8 +134,6 @@ private partial def replaceRecApps (recArgInfos : Array RecArgInfo) (positions :
134134
withLocalDecl n c (← loop below d) fun x => do
135135
mkForallFVars #[x] (← loop below (b.instantiate1 x))
136136
| 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
139137
mapLetDecl n (← loop below type) (← loop below val) (nondep := nondep) (usedLetOnly := false) fun x => do
140138
loop below (body.instantiate1 x)
141139
| Expr.mdata d b =>

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -122,13 +122,13 @@ private def numLetsWithValueNotIsWfParam (e : Expr) (acc := 0) : Nat :=
122122

123123
private partial def processParamLet (e : Expr) : MetaM Expr := do
124124
if let .letE _ t v b _ := e then
125-
if let some v := isWfParam? v then
125+
if let some v' := isWfParam? v then
126126
if ← Meta.isProp t then
127-
processParamLet <| e.updateLetE! t v b
127+
processParamLet <| e.updateLetE! t v' b
128128
else
129129
let u ← getLevel t
130130
let b' := b.instantiate1 <| mkApp2 (.const ``wfParam [u]) t (.bvar 0)
131-
processParamLet <| e.updateLetE! t v b'
131+
processParamLet <| e.updateLetE! t v' b'
132132
else
133133
let num := numLetsWithValueNotIsWfParam e
134134
assert! num > 0

src/Lean/LocalContext.lean

Lines changed: 39 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ inductive LocalDecl where
7171
have type-incorrect values. This design decision allows metaprograms to not have to think about nondep `ldecl`s,
7272
so long as `LocalDecl` values are consumed through `LocalDecl.isLet` and `LocalDecl.value?` with `(allowNondep := false)`.
7373
**Rule:** never use `(generalizeNondepLet := false)` in `mkBinding`-family functions within a local context you do not own.
74+
See `LocalDecl.setNondep` for some additional discussion.
7475
- Where then do nondep ldecls come from? Common functions are `Meta.mapLetDecl`, `Meta.withLetDecl`, and `Meta.letTelescope`.
7576
The `have` term syntax makes use of a nondep ldecl as well.
7677
@@ -91,7 +92,11 @@ def LocalDecl.binderInfoEx : LocalDecl → BinderInfo
9192
| _ => BinderInfo.default
9293
namespace LocalDecl
9394

94-
/-- Returns true if this is an `ldecl`. If `allowNondep` is false (the default), then requires that `nondep` be false. -/
95+
/--
96+
Returns true if this is an `ldecl` with a visible value.
97+
98+
If `allowNondep` is true then includes `ldecl`s with `nondep := true`, whose values are normally hidden.
99+
-/
95100
def isLet : LocalDecl → (allowNondep : Bool := false) → Bool
96101
| cdecl .., _ => false
97102
| ldecl (nondep := false) .., _ => true
@@ -143,17 +148,19 @@ def isImplementationDetail (d : LocalDecl) : Bool :=
143148
d.kind != .default
144149

145150
/--
146-
Returns the value of the `ldecl`,
147-
but if the `ldecl` is nondependent and `allowNondep` is false, returns `none`.
151+
Returns the value of the `ldecl` if it has a visible value.
152+
153+
If `allowNondep` is true, then allows nondependent `ldecl`s, whose values are normally hidden.
148154
-/
149155
def value? : LocalDecl → (allowNondep : Bool := false) → Option Expr
150156
| ldecl (nondep := false) (value := v) .., _ => some v
151157
| ldecl (nondep := true) (value := v) .., true => some v
152158
| _, _ => none
153159

154160
/--
155-
Returns the value of the `ldecl`,
156-
but if the `ldecl` is nondependent and `allowNondep` is false, panics.
161+
Returns the value of the `ldecl` if it has a visible value.
162+
163+
If `allowNondep` is true, then allows nondependent `ldecl`s, whose values are normally hidden.
157164
-/
158165
def value : LocalDecl → (allowNondep : Bool := false) → Expr
159166
| cdecl .., _ => panic! "let declaration expected"
@@ -175,17 +182,36 @@ def setValue : LocalDecl → Expr → LocalDecl
175182

176183
/--
177184
Sets the `nondep` flag of an `ldecl`, otherwise returns `cdecl`s unchanged.
178-
It is the responsibility of the caller to ensure that transitions are correct.
185+
186+
This is a low-level function, and it is the responsibility of the caller to ensure that
187+
transitions of `nondep` are valid.
179188
180189
Rules:
181-
- Setting `nondep := true` is a way to convert the ldecl into a cdecl.
182-
- Caution: be sure any relevant caches are cleared so that the value does not leak.
183-
- Caution: be sure that metavariables from before and after the transition are not mixed,
190+
- If the declaration is not under the caller's control, then setting `nondep := false` must not be done.
191+
General nondependent `ldecl`s should be treated like `cdecl`s.
192+
See also the docstring for `LocalDecl.ldecl` about the `value` not necessarily being type correct.
193+
- Setting `nondep := true` is usually fine.
194+
- Caution: be sure any relevant caches are cleared so that the value associated to this `FVarId` does not leak.
195+
- Caution: be sure that metavariables dependent on this declaration created before and after the transition are not mixed,
184196
since unification does not check "`nondep`-compatibility" of local contexts when assigning metavariables.
185-
- If the declaration is for a variable whose dependencies are not completely under the caller's control,
186-
then setting `nondep := false` must not be done.
187-
- Even if the caller does have complete control, setting `nondep := false` should not be done.
188-
The cautions about caches and metavariables still apply.
197+
198+
For example, setting `nondep := false` is fine from within a telescope combinator, to update the local context
199+
right before calling `mkLetFVars`:
200+
```lean
201+
let lctx ← getLCtx
202+
letTelescope e fun xs b => do
203+
let lctx' ← xs.foldlM (init := lctx) fun lctx' x => do
204+
let decl ← x.fvarId!.getDecl
205+
-- Clear the flag if it's not a prop.
206+
let decl' := decl.setNondep <| ← pure decl.isNondep <&&> Meta.isProp decl.type
207+
pure <| lctx'.addDecl decl'
208+
withLCtx' lctx' do
209+
mkLetFVars (usedLetOnly := false) (generalizeNondepLet := false) xs b
210+
```
211+
1. The declarations for `xs` are in the control of this metaprogram.
212+
2. `mkLetFVars` does make use of `MetaM` caches.
213+
3. Even if `e` has metavariables, these do not include `xs` in their contexts,
214+
so the change of the `nondep` flag does not cause any issues in the `abstractM` system used by `mkLetFVars`.
189215
-/
190216
def setNondep : LocalDecl → Bool → LocalDecl
191217
| ldecl idx id n t v _ k, nd => ldecl idx id n t v nd k

src/Lean/Meta/Basic.lean

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -979,9 +979,10 @@ def _root_.Lean.FVarId.getBinderInfo (fvarId : FVarId) : MetaM BinderInfo :=
979979
return (← fvarId.getDecl).binderInfo
980980

981981
/--
982-
Returns `some value` if the given free variable is a let-declaration, and `none` otherwise.
982+
Returns `some value` if the given free let-variable has a visible local definition in the current local context
983+
(using `Lean.LocalDecl.value?`), and `none` otherwise.
983984
984-
If `allowNondep := false` (the default) then returns `none` if it is a nondependent let-declaration.
985+
Setting `allowNondep := true` allows access of the normally hidden value of a nondependent let declaration.
985986
-/
986987
def _root_.Lean.FVarId.getValue? (fvarId : FVarId) (allowNondep : Bool := false) : MetaM (Option Expr) :=
987988
return (← fvarId.getDecl).value? allowNondep
@@ -991,9 +992,10 @@ def _root_.Lean.FVarId.getUserName (fvarId : FVarId) : MetaM Name :=
991992
return (← fvarId.getDecl).userName
992993

993994
/--
994-
Returns `true` is the free variable is a let-variable.
995+
Returns `true` if the free variable is a let-variable with a visible local definition in the current local context
996+
(using `Lean.LocalDecl.isLet`).
995997
996-
If `allowNondep := false` (the default) then returns `false` if it is a nondependent let-declaration.
998+
Setting `allowNondep := true` includes nondependent let declarations, whose values are normally hidden.
997999
-/
9981000
def _root_.Lean.FVarId.isLetVar (fvarId : FVarId) (allowNondep : Bool := false) : MetaM Bool :=
9991001
return (← fvarId.getDecl).isLet allowNondep

src/Lean/Meta/Tactic/Lets.lean

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ partial def extractCore (fvars : List Expr) (e : Expr) (topLevel : Bool := false
224224
match e with
225225
| .bvar .. | .fvar .. | .mvar .. | .sort .. | .const .. | .lit .. => unreachable!
226226
| .mdata _ e' => return e.updateMData! (← extractCore fvars e' (topLevel := topLevel))
227-
| .letE n t v b nondep => extractLetLike (!nondep) n t v b (fun t v b => pure <| e.updateLetE! t v b) (topLevel := topLevel)
227+
| .letE n t v b nondep => extractLetLike !nondep n t v b (fun t v b => pure <| e.updateLetE! t v b) (topLevel := topLevel)
228228
| .app .. =>
229229
if e.isLetFun then
230230
extractLetFun e (topLevel := topLevel)

0 commit comments

Comments
 (0)