Skip to content

Commit 2dc65ff

Browse files
kmillJovanGerb
authored andcommitted
fix: process delayed assignment metavariables correctly in Lean.Meta.Closure (leanprover#6414)
This PR fixes a bug in `Lean.Meta.Closure` that would introduce under-applied delayed assignment metavariables, which would keep them from ever getting instantiated. This bug affected `match` elaboration when the expected type contained postponed elaboration problems, for example tactic blocks. Closes leanprover#5925, closes leanprover#6354
1 parent 29fc85c commit 2dc65ff

File tree

2 files changed

+150
-1
lines changed

2 files changed

+150
-1
lines changed

Diff for: src/Lean/Meta/Closure.lean

+26-1
Original file line numberDiff line numberDiff line change
@@ -203,9 +203,34 @@ partial def collectExprAux (e : Expr) : ClosureM Expr := do
203203
let type ← collect type
204204
let newFVarId ← mkFreshFVarId
205205
let userName ← mkNextUserName
206+
/-
207+
Recall that delayed assignment metavariables must always be applied to at least
208+
`a.fvars.size` arguments (where `a : DelayedMetavarAssignment` is its record).
209+
This assumption is used in `lean::instantiate_mvars_fn::visit_app` for example, where there's a comment
210+
about how under-applied delayed assignments are an error.
211+
212+
If we were to collect the delayed assignment metavariable itself and push it onto the `exprMVarArgs` list,
213+
then `exprArgs` returned by `Lean.Meta.Closure.mkValueTypeClosure` would contain underapplied delayed assignment metavariables.
214+
This leads to kernel 'declaration has metavariables' errors, as reported in https://github.com/leanprover/lean4/issues/6354
215+
216+
The straightforward solution to this problem (implemented below) is to eta expand the delayed assignment metavariable
217+
to ensure it is fully applied. This isn't full eta expansion; we only need to eta expand the first `fvars.size` arguments.
218+
219+
Note: there is the possibility of handling special cases to create more-efficient terms.
220+
For example, if the delayed assignment metavariable is applied to fvars, we could avoid eta expansion for those arguments
221+
since the fvars are being collected anyway. It's not clear that the additional implementation complexity is worth it,
222+
and it is something we can evaluate later. In any case, the current solution is necessary as the generic case.
223+
-/
224+
let e' ←
225+
if let some { fvars, .. } ← getDelayedMVarAssignment? mvarId then
226+
-- Eta expand `e` for the requisite number of arguments.
227+
forallBoundedTelescope mvarDecl.type fvars.size fun args _ => do
228+
mkLambdaFVars args <| mkAppN e args
229+
else
230+
pure e
206231
modify fun s => { s with
207232
newLocalDeclsForMVars := s.newLocalDeclsForMVars.push $ .cdecl default newFVarId userName type .default .default,
208-
exprMVarArgs := s.exprMVarArgs.push e
233+
exprMVarArgs := s.exprMVarArgs.push e'
209234
}
210235
return mkFVar newFVarId
211236
| Expr.fvar fvarId =>

Diff for: tests/lean/run/6354.lean

+124
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
import Lean
2+
/-!
3+
# Proper handling of delayed assignment metavariables in `match` elaboration
4+
5+
https://github.com/leanprover/lean4/issues/5925
6+
https://github.com/leanprover/lean4/issues/6354
7+
8+
These all had the error `(kernel) declaration has metavariables '_example'`
9+
due to underapplied delayed assignment metavariables never being instantiated.
10+
-/
11+
12+
namespace Test1
13+
/-!
14+
Simplified version of example from issue 6354.
15+
-/
16+
17+
structure A where
18+
p: Prop
19+
q: True
20+
21+
example := (λ ⟨_,_⟩ ↦ True.intro : (A.mk (And True True) (by exact True.intro)).p → True)
22+
23+
end Test1
24+
25+
26+
namespace Test2
27+
/-!
28+
Example from issue 6354 (by @roos-j)
29+
-/
30+
31+
structure A where
32+
p: Prop
33+
q: True
34+
35+
structure B extends A where
36+
q': p → True
37+
38+
example: B where
39+
p := True ∧ True
40+
q := by exact True.intro
41+
q' := λ ⟨_,_⟩ ↦ True.intro
42+
43+
end Test2
44+
45+
46+
namespace Test3
47+
/-!
48+
Example from issue 6354 (by @b-mehta)
49+
-/
50+
51+
class Preorder (α : Type) extends LE α, LT α where
52+
le_refl : ∀ a : α, a ≤ a
53+
lt := fun a b => a ≤ b ∧ ¬b ≤ a
54+
55+
class PartialOrder (α : Type) extends Preorder α where
56+
le_antisymm : ∀ a b : α, a ≤ b → b ≤ a → a = b
57+
58+
inductive MyOrder : Nat × Nat → Nat × Nat → Prop
59+
| within {x u m : Nat} : x ≤ u → MyOrder (x, m) (u, m)
60+
61+
instance : PartialOrder (Nat × Nat) where
62+
le := MyOrder
63+
le_refl x := .within (Nat.le_refl _)
64+
le_antisymm | _, _, .within _, .within _ => Prod.ext (Nat.le_antisymm ‹_› ‹_›) rfl
65+
66+
end Test3
67+
68+
69+
namespace Test4
70+
/-!
71+
Example from issue 5925 (by @Komyyy)
72+
-/
73+
74+
def Injective (f : α → β) : Prop :=
75+
∀ ⦃a₁ a₂⦄, f a₁ = f a₂ → a₁ = a₂
76+
77+
axiom my_mul_right_injective {M₀ : Type} [Mul M₀] [Zero M₀] {a : M₀} (ha : a ≠ 0) :
78+
Injective fun (x : M₀) ↦ a * x
79+
80+
def mul2 : { f : Nat → Nat // Injective f } := ⟨fun x : Nat ↦ 2 * x, my_mul_right_injective nofun⟩
81+
82+
end Test4
83+
84+
85+
namespace Test5
86+
/-!
87+
Example from issue 5925 (by @mik-jozef)
88+
-/
89+
90+
structure ValVar (D: Type) where
91+
d: D
92+
x: Nat
93+
94+
def Set (T : Type) := T → Prop
95+
96+
structure Salg where
97+
D: Type
98+
I: Nat
99+
100+
def natSalg: Salg := { D := Nat, I := 42 }
101+
102+
inductive HasMem (salg: Salg): Set (Set (ValVar salg.D))
103+
| hasMem
104+
(set: Set (ValVar salg.D))
105+
(isElem: set ⟨d, x⟩)
106+
:
107+
HasMem salg set
108+
109+
def valVarSet: Set (ValVar Nat) :=
110+
fun ⟨d, x⟩ => x = 0 ∧ d = 0 ∧ d ∉ []
111+
112+
-- Needed to add a unification hint to this test
113+
-- because of https://github.com/leanprover/lean4/pull/6024
114+
unif_hint (s : Salg) where
115+
s =?= natSalg
116+
|-
117+
Salg.D s =?= Nat
118+
119+
def valVarSetHasMem: HasMem natSalg valVarSet :=
120+
HasMem.hasMem
121+
valVarSet
122+
(show valVarSet _ from ⟨rfl, rfl, nofun⟩)
123+
124+
end Test5

0 commit comments

Comments
 (0)