This PR fixes an issue where nested `let rec` declarations within `match` expressions or tactic blocks failed to compile if they were nested within, and recursively called, a `let rec` that referenced a variable bound by a containing declaration. Closes #6927 --------- Co-authored-by: Joachim Breitner <mail@joachim-breitner.de>
93 lines
2.7 KiB
Text
93 lines
2.7 KiB
Text
import Lean
|
||
/-
|
||
# Nested `let rec` declarations in the presence of delayed metavariable assignments
|
||
|
||
https://github.com/leanprover/lean4/issues/6927
|
||
|
||
Nested declarations using `let rec` should compile correctly even when nested within expressions
|
||
that are elaborated using delayed metavariable assignments, such as `match` expressions and tactic
|
||
blocks. See the Note [Delayed-Assigned Metavariables in Free Variable Collection] in
|
||
`MutualDef.lean` for additional details.
|
||
-/
|
||
|
||
inductive Tree : Type where
|
||
| leaf
|
||
| node (p : Nat) (brs : List Tree)
|
||
def Tree.substNats (M : Tree) (σ : Nat → Nat) : Tree :=
|
||
let rec go M : Tree := match M with
|
||
| .leaf => leaf
|
||
| .node p brs =>
|
||
let rec goList : List Tree → List Tree
|
||
| [] => []
|
||
| M :: Ms => go M :: goList Ms
|
||
.node (σ p) (goList brs)
|
||
go M
|
||
|
||
|
||
/-
|
||
Previously, free variable dependencies in `inner` were not correctly detected, leading that
|
||
declaration to contain free variables when sent to the kernel.
|
||
-/
|
||
namespace Ex1
|
||
def outer (f : Nat → Nat) :=
|
||
let rec middle n := match n with
|
||
| Nat.zero => 0 | .succ _ =>
|
||
let rec inner := middle n
|
||
f 0
|
||
()
|
||
end Ex1
|
||
|
||
/-
|
||
`by` blocks induced behavior similar to the above, since they also produce metavariables during
|
||
elaboration.
|
||
-/
|
||
def tac (f : Nat → Nat) :=
|
||
let rec middle n := by
|
||
let rec inner := middle (Nat.succ n)
|
||
exact f 0
|
||
()
|
||
|
||
/- Previously, `inner` was incorrectly compiled as an `axiom`, rendering `outer` noncomputable. -/
|
||
namespace Ex2
|
||
/--
|
||
error: fail to show termination for
|
||
Ex2.outer.middle.inner
|
||
Ex2.outer.middle
|
||
with errors
|
||
failed to infer structural recursion:
|
||
Not considering parameter f of Ex2.outer.middle.inner:
|
||
it is unchanged in the recursive calls
|
||
Not considering parameter f of Ex2.outer.middle:
|
||
it is unchanged in the recursive calls
|
||
Cannot use parameters n✝ of Ex2.outer.middle.inner and n of Ex2.outer.middle:
|
||
failed to eliminate recursive application
|
||
outer.middle f✝ (n + 1)
|
||
Cannot use parameters n of Ex2.outer.middle.inner and n of Ex2.outer.middle:
|
||
failed to eliminate recursive application
|
||
outer.middle f✝ (n + 1)
|
||
|
||
|
||
Could not find a decreasing measure.
|
||
The basic measures relate at each recursive call as follows:
|
||
(<, ≤, =: relation proved, ? all proofs failed, _: no proof attempted)
|
||
Call from Ex2.outer.middle.inner to Ex2.outer.middle at 89:6-20:
|
||
n✝ n
|
||
n ? ?
|
||
Call from Ex2.outer.middle to Ex2.outer.middle.inner at 91:4-11:
|
||
n
|
||
n✝ _
|
||
n _
|
||
|
||
Please use `termination_by` to specify a decreasing measure.
|
||
-/
|
||
#guard_msgs in
|
||
def outer (f : Nat → Nat) (n : Nat) : Nat :=
|
||
let rec middle n : Nat := match n with
|
||
| 0 => 1
|
||
| n + 1 =>
|
||
let rec inner (n : Nat) : Nat :=
|
||
middle (n + 1)
|
||
let _ := f
|
||
inner n
|
||
middle n
|
||
end Ex2
|