Skip to content

Commit 36dc822

Browse files
authored
fix: loose bound variables in Sym.dsimp binder telescopes (#14029)
This PR fixes a `Sym.dsimp` bug that could produce an ill-typed term, causing the kernel to reject the resulting goal with errors such as `application type mismatch` or `function expected`. It triggered when a `let`/`λ`/`∀` binder's type or value referenced an earlier binder in the same telescope. A regression test `tests/elab/sym_dsimp_bug.lean` reproduces the original kernel error and confirms `dsimp` now produces a well-typed goal.
1 parent 60967a7 commit 36dc822

4 files changed

Lines changed: 50 additions & 3 deletions

File tree

src/Lean/Meta/Sym/DSimp/Forall.lean

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,11 @@ where
2222
go (e : Expr) (fvars : Array Expr) (modified : Bool) : DSimpM Result := do
2323
match e with
2424
| .forallE n d b c =>
25-
match (← dsimp (← instantiateRevBetaS d fvars)) with
25+
-- Instantiate the binder domain against the already-introduced `fvars` *before* creating
26+
-- the local declaration, otherwise the decl retains loose bound variables referring to
27+
-- earlier binders in this telescope.
28+
let d ← instantiateRevBetaS d fvars
29+
match (← dsimp d) with
2630
| .rfl _ => withLocalDecl n c d fun x => go b (fvars.push x) modified
2731
| .step d' _ => withLocalDecl n c d' fun x => go b (fvars.push x) true
2832
| _ =>

src/Lean/Meta/Sym/DSimp/Lambda.lean

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,11 @@ where
2222
go (e : Expr) (fvars : Array Expr) (modified : Bool) : DSimpM Result := do
2323
match e with
2424
| .lam n d b c =>
25-
match (← dsimp (← instantiateRevBetaS d fvars)) with
25+
-- Instantiate the binder domain against the already-introduced `fvars` *before* creating
26+
-- the local declaration, otherwise the decl retains loose bound variables referring to
27+
-- earlier binders in this telescope.
28+
let d ← instantiateRevBetaS d fvars
29+
match (← dsimp d) with
2630
| .rfl _ => withLocalDecl n c d fun x => go b (fvars.push x) modified
2731
| .step d' _ => withLocalDecl n c d' fun x => go b (fvars.push x) true
2832
| _ =>

src/Lean/Meta/Sym/DSimp/Let.lean

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,13 @@ where
1919
go (e : Expr) (fvars : Array Expr) (modified : Bool) : DSimpM Result := do
2020
match e with
2121
| .letE n t v b nd =>
22-
match (← dsimp (← instantiateRevBetaS t fvars)), (← dsimp (← instantiateRevBetaS v fvars)) with
22+
-- Instantiate the binder type and value against the already-introduced `fvars` *before*
23+
-- creating the local declaration. Using the raw `t`/`v` would leave loose bound variables
24+
-- (referring to earlier binders in this telescope) in the decl, which get misinterpreted
25+
-- once the decl is zeta-expanded under a different binder.
26+
let t ← instantiateRevBetaS t fvars
27+
let v ← instantiateRevBetaS v fvars
28+
match (← dsimp t), (← dsimp v) with
2329
| .rfl _, .rfl _ =>
2430
withLetDecl n t v (nondep := nd) fun x => go b (fvars.push x) modified
2531
| .step t' _, .rfl _ =>

tests/elab/sym_dsimp_bug.lean

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/-! # Regression test for `Sym.dsimp` binder handling
2+
3+
`dsimp` introduces telescope binders by re-instantiating each binder's type/value
4+
against the free variables already introduced. A previous version created the local
5+
declarations from the *raw* binder type/value, leaving loose bound variables that were
6+
later misinterpreted when the declaration was zeta-expanded under a different binder
7+
(here the `_unit` lambda), producing an ill-typed term the kernel rejected. -/
8+
9+
inductive Effects where
10+
| done_val : BitVec 64 → Effects
11+
| other : Effects
12+
| require_exec_access : (Unit → Effects) → Effects
13+
14+
def Effects.All (post : Effects → Prop) : Effects → Prop
15+
| .done_val v => post (.done_val v)
16+
| .other => False
17+
| .require_exec_access cont => (cont ()).All post
18+
19+
structure MyState where
20+
zf : Bool
21+
22+
register_sym_dsimp myVariant where
23+
pre := proj
24+
25+
set_option warn.sorry false
26+
27+
theorem test_error (x : BitVec 64) (post : Effects → Prop) :
28+
let v := x + 1
29+
let status := MyState.mk (v == 0)
30+
Effects.All post (Effects.require_exec_access (fun _unit => if status.zf then Effects.done_val v else Effects.other)) := by
31+
sym =>
32+
dsimp myVariant
33+
sorry

0 commit comments

Comments
 (0)