Skip to content

Commit 0b63061

Browse files
committed
fix handling of lets in WF after discussion with Joachim
1 parent e424fc9 commit 0b63061

File tree

5 files changed

+92
-15
lines changed

5 files changed

+92
-15
lines changed

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

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,6 @@ where
8585
withLocalDecl n c (← loop F d) fun x => do
8686
mkForallFVars #[x] (← loop F (b.instantiate1 x))
8787
| 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
9188
mapLetDecl n (← loop F type) (← loop F val) (nondep := nondep) (usedLetOnly := false) fun x => do
9289
loop F (body.instantiate1 x)
9390
| Expr.mdata d b =>

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

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -244,8 +244,6 @@ where
244244
| Expr.letE n type val body nondep =>
245245
loop param type
246246
loop param val
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
249247
withLetDecl n type val (nondep := nondep) fun x => do
250248
loop param (body.instantiate1 x)
251249
| Expr.mdata _d b =>

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

Lines changed: 66 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -110,16 +110,68 @@ 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]`
114-
Also accepts non-Prop `have`s as well. -/
113+
private def anyLetValueIsWfParam (e : Expr) : Bool :=
114+
match e with
115+
| .letE _ _ v b _ => (isWfParam? v).isSome || anyLetValueIsWfParam b
116+
| _ => false
117+
118+
private def numLetsWithValueNotIsWfParam (e : Expr) (acc := 0) : Nat :=
119+
match e with
120+
| .letE _ _ v b _ => if (isWfParam? v).isSome then acc else numLetsWithValueNotIsWfParam b (acc + 1)
121+
| _ => acc
122+
123+
private partial def processParamLet (e : Expr) : MetaM Expr := do
124+
if let .letE _ t v b _ := e then
125+
if let some v := isWfParam? v then
126+
if ← Meta.isProp t then
127+
processParamLet <| e.updateLetE! t v b
128+
else
129+
let u ← getLevel t
130+
let b' := b.instantiate1 <| mkApp2 (.const ``wfParam [u]) t (.bvar 0)
131+
processParamLet <| e.updateLetE! t v b'
132+
else
133+
let num := numLetsWithValueNotIsWfParam e
134+
assert! num > 0
135+
letBoundedTelescope e num fun xs b => do
136+
let b' ← processParamLet b
137+
mkLetFVars (usedLetOnly := false) (generalizeNondepLet := false) xs b'
138+
else
139+
return e
140+
141+
/--
142+
`let x : T := (wfParam e); body[x] ==> let x : T := e; body[wfParam y]` if `T` is not a proposition,
143+
otherwise `... ==> let x : T := e; body[x]`. (Applies to `have`s too.)
144+
145+
Note: simprocs are provided the head of a let telescope, but not intermediate lets.
146+
-/
115147
builtin_dsimproc paramLet (_) := fun e => do
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'
148+
unless e.isLet || anyLetValueIsWfParam e do return .continue
149+
return .continue (← processParamLet e)
150+
151+
/--
152+
Transforms non-Prop `have`s to `let`s, so that the values can be used in GuessLex and decreasing-by proofs.
153+
These `have`s may have been introdued by `simp`, which converts `let`s to `have`s.
154+
-/
155+
private def nonPropHaveToLet (e : Expr) : MetaM Expr := do
156+
Meta.transform e (pre := fun e => do
157+
if (← Meta.isProof e) then
158+
return .done e
159+
else if e.isLet then
160+
-- Recall that `Meta.transform` processes entire let telescopes,
161+
-- so we need to handle the telescope all at once.
162+
let lctx ← getLCtx
163+
let e' ← letTelescope e fun xs b => do
164+
let lctx' ← xs.foldlM (init := lctx) fun lctx' x => do
165+
let decl ← x.fvarId!.getDecl
166+
-- Clear the flag if it's not a prop.
167+
let decl' := decl.setNondep <| ← pure decl.isNondep <&&> Meta.isProp decl.type
168+
pure <| lctx'.addDecl decl'
169+
withLCtx' lctx' do
170+
mkLetFVars (usedLetOnly := false) (generalizeNondepLet := false) xs b
171+
return .continue e'
172+
else
173+
return .continue
174+
)
123175

124176
def preprocess (e : Expr) : MetaM Simp.Result := do
125177
unless wf.preprocess.get (← getOptions) do
@@ -143,9 +195,13 @@ def preprocess (e : Expr) : MetaM Simp.Result := do
143195
if h : as.size ≥ 2 then
144196
return .continue (mkAppN as[1] as[2:])
145197
return .continue
198+
199+
-- Transform `have`s to `let`s for non-propositions.
200+
let e'' ← nonPropHaveToLet e''
201+
146202
let result := { result with expr := e'' }
147203

148-
trace[Elab.definition.wf] "Attach-introduction:{indentExpr e'}\nto{indentExpr result.expr}\ncleaned up as{indentExpr e''}"
204+
trace[Elab.definition.wf] "Attach-introduction:{indentExpr e'}\nto{indentExpr result.expr}"
149205
result.addLambdas xs
150206

151207
end Lean.Elab.WF

src/Lean/LocalContext.lean

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,11 @@ def setNondep : LocalDecl → Bool → LocalDecl
176176
| ldecl idx id n t v _ k, nd => ldecl idx id n t v nd k
177177
| d, _ => d
178178

179+
/-- Returns `true` if this is an `ldecl` with `nondep := true`. -/
180+
def isNondep : LocalDecl → Bool
181+
| ldecl (nondep := nondep) .. => nondep
182+
| _ => false
183+
179184
def setUserName : LocalDecl → Name → LocalDecl
180185
| cdecl index id _ type bi k, userName => cdecl index id userName type bi k
181186
| ldecl index id _ type val nd k, userName => ldecl index id userName type val nd k

tests/lean/run/wf_preprocess.lean

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,27 @@ def Tree.map (f : α → β) (t : Tree α) : Tree β :=
1919
termination_by t
2020
decreasing_by trace_state; cases t; decreasing_tactic
2121

22+
/-!
23+
Checking that the attaches make their way through `let`s.
24+
-/
25+
/--
26+
trace: α : Type u_1
27+
t : Tree α
28+
cs : List (Tree α) := t.cs
29+
t' : Tree α
30+
h✝ : t' ∈ cs
31+
⊢ sizeOf t' < sizeOf t
32+
-/
33+
#guard_msgs(trace) in
34+
def Tree.map' (f : α → β) (t : Tree α) : Tree β :=
35+
have n := 22
36+
let v := t.val
37+
let cs := t.cs
38+
have : n = n := rfl
39+
⟨f v, cs.map (fun t' => t'.map' f)⟩
40+
termination_by t
41+
decreasing_by trace_state; cases t; decreasing_tactic
42+
2243
/--
2344
info: equations:
2445
theorem Tree.map.eq_1.{u_1, u_2} : ∀ {α : Type u_1} {β : Type u_2} (f : α → β) (t : Tree α),

0 commit comments

Comments
 (0)