Skip to content

Commit 6448547

Browse files
authored
fix: instantiateTheorem in grind (#8369)
This PR fixes a type error at `instantiateTheorem` function used in `grind`. It was failing to instantiate theorems such as ```lean theorem getElem_reverse {xs : Array α} {i : Nat} (hi : i < xs.reverse.size) : (xs.reverse)[i] = xs[xs.size - 1 - i]'(by simp at hi; omega) ``` in examples such as ```lean example (xs : Array Nat) (w : xs.reverse = xs) (j : Nat) (hj : 0 ≤ j) (hj' : j < xs.size / 2) : xs[j] = xs[xs.size - 1 - j] ``` generating the issue ```lean [issue] type error constructing proof for Array.getElem_reverse when assigning metavariable ?hi with ‹j < xs.toList.length› has type j < xs.toList.length : Prop but is expected to have type j < xs.reverse.size : Prop ```
1 parent 632b688 commit 6448547

File tree

4 files changed

+111
-97
lines changed

4 files changed

+111
-97
lines changed

src/Lean/Meta/Tactic/Grind/EMatch.lean

Lines changed: 62 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,9 @@ private def assign? (c : Choice) (bidx : Nat) (e : Expr) : OptionT GoalM Choice
8989
-- `Choice` was not properly initialized
9090
unreachable!
9191

92+
private def unassign (c : Choice) (bidx : Nat) : Choice :=
93+
{ c with assignment := c.assignment.set! bidx unassigned }
94+
9295
/--
9396
Returns `true` if the function `pFn` of a pattern is equivalent to the function `eFn`.
9497
Recall that we ignore universe levels in patterns.
@@ -290,6 +293,63 @@ private def addNewInstance (thm : EMatchTheorem) (proof : Expr) (generation : Na
290293
trace_goal[grind.ematch.instance] "{← thm.origin.pp}: {prop}"
291294
addTheoremInstance thm proof prop (generation+1)
292295

296+
private def synthesizeInsts (mvars : Array Expr) (bis : Array BinderInfo) : OptionT M Unit := do
297+
let thm := (← read).thm
298+
for mvar in mvars, bi in bis do
299+
if bi.isInstImplicit && !(← mvar.mvarId!.isAssigned) then
300+
let type ← inferType mvar
301+
unless (← synthesizeInstanceAndAssign mvar type) do
302+
reportIssue! "failed to synthesize instance when instantiating {← thm.origin.pp}{indentExpr type}"
303+
failure
304+
305+
private def applyAssignment (mvars : Array Expr) : OptionT (StateT Choice M) Unit := do
306+
let thm := (← read).thm
307+
let numParams := thm.numParams
308+
for h : i in [:mvars.size] do
309+
let bidx := numParams - i - 1
310+
let mut v := (← get).assignment[bidx]!
311+
unless isSameExpr v unassigned do
312+
let mvarId := mvars[i].mvarId!
313+
let mvarIdType ← mvarId.getType
314+
let vType ← inferType v
315+
let unassignOrFail : OptionT (StateT Choice M) Unit := do
316+
/-
317+
If there is type error and `vType` is a proposition, we can still instantiate the
318+
theorem by unassigning `v` and using it as an extra hypothesis.
319+
Here is an example to motivate the unassignment.
320+
```
321+
example (xs : Array Nat) (w : xs.reverse = xs) (j : Nat) (hj : 0 ≤ j) (hj' : j < xs.size / 2)
322+
: xs[j] = xs[xs.size - 1 - j] := by
323+
grind
324+
```
325+
Without the unassignment we get a type error while trying to instantiate the theorem
326+
```
327+
theorem getElem_reverse {xs : Array α} {i : Nat} (hi : i < xs.reverse.size) :
328+
(xs.reverse)[i] = xs[xs.size - 1 - i]'(by simp at hi; omega)
329+
```
330+
The pattern for this theorem is `xs.reverese[i]`. Note that `hi` occurs there as an implicit argument.
331+
The term `xs[j]` in our goal e-matches the pattern because we have the equality `xs.reverse = xs`.
332+
However, the implicit proof at `xs[j]` has type `j < xs.size` instead of `j < xs.reverse.size`.
333+
-/
334+
if (← isProp vType) then
335+
modify (unassign · bidx)
336+
else
337+
reportIssue! "type error constructing proof for {← thm.origin.pp}\nwhen assigning metavariable {mvars[i]} with {indentExpr v}\n{← mkHasTypeButIsExpectedMsg vType mvarIdType}"
338+
failure
339+
unless (← withDefault <| isDefEq mvarIdType vType) do
340+
if let some heq ← withoutReportingMVarIssues <| proveEq? vType mvarIdType (abstract := true) then
341+
/-
342+
Some of the `cast`s will appear inside the `Grind.MatchCond` binders, and
343+
we want their proofs to be properly wrapped.
344+
-/
345+
let heq := mkApp2 (mkConst ``Grind.nestedProof) (← mkEq vType mvarIdType) heq
346+
v ← mkAppM ``cast #[heq, v]
347+
else
348+
unassignOrFail
349+
continue
350+
unless (← mvarId.checkedAssign v) do
351+
unassignOrFail
352+
293353
/--
294354
After processing a (multi-)pattern, use the choice assignment to instantiate the proof.
295355
Missing parameters are synthesized using type inference and type class synthesis."
@@ -306,35 +366,8 @@ private partial def instantiateTheorem (c : Choice) : M Unit := withDefault do w
306366
if mvars.size != thm.numParams then
307367
reportIssue! "unexpected number of parameters at {← thm.origin.pp}"
308368
return ()
309-
-- Apply assignment
310-
for h : i in [:mvars.size] do
311-
let mut v := c.assignment[numParams - i - 1]!
312-
unless isSameExpr v unassigned do
313-
let mvarId := mvars[i].mvarId!
314-
let mvarIdType ← mvarId.getType
315-
let vType ← inferType v
316-
let report : M Unit := do
317-
reportIssue! "type error constructing proof for {← thm.origin.pp}\nwhen assigning metavariable {mvars[i]} with {indentExpr v}\n{← mkHasTypeButIsExpectedMsg vType mvarIdType}"
318-
unless (← withDefault <| isDefEq mvarIdType vType) do
319-
let some heq ← withoutReportingMVarIssues <| proveEq? vType mvarIdType (abstract := true)
320-
| report
321-
return ()
322-
/-
323-
Some of the `cast`s will appear inside the `Grind.MatchCond` binders, and
324-
we want their proofs to be properly wrapped.
325-
-/
326-
let heq := mkApp2 (mkConst ``Grind.nestedProof) (← mkEq vType mvarIdType) heq
327-
v ← mkAppM ``cast #[heq, v]
328-
unless (← mvarId.checkedAssign v) do
329-
report
330-
return ()
331-
-- Synthesize instances
332-
for mvar in mvars, bi in bis do
333-
if bi.isInstImplicit && !(← mvar.mvarId!.isAssigned) then
334-
let type ← inferType mvar
335-
unless (← synthesizeInstanceAndAssign mvar type) do
336-
reportIssue! "failed to synthesize instance when instantiating {← thm.origin.pp}{indentExpr type}"
337-
return ()
369+
let (some _, c) ← applyAssignment mvars |>.run c | return ()
370+
let some _ ← synthesizeInsts mvars bis | return ()
338371
let proof := mkAppN proof mvars
339372
if (← mvars.allM (·.mvarId!.isAssigned)) then
340373
addNewInstance thm proof c.gen

tests/lean/grind/palindrome.lean

Lines changed: 0 additions & 68 deletions
This file was deleted.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
set_option grind.warning false
2+
3+
example (xs : Array Nat) (w : xs.reverse = xs) (j : Nat) (hj : 0 ≤ j) (hj' : j < xs.size / 2) :
4+
xs[j] = xs[xs.size - 1 - j] := by
5+
grind
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
set_option grind.warning false
2+
3+
def IsPalindrome (xs : Array Nat) : Prop := xs.reverse = xs
4+
5+
def checkPalin1 (xs : Array Nat) : Bool :=
6+
go 0
7+
where
8+
go (i : Nat) :=
9+
if h : i < xs.size / 2 then
10+
if xs[i] = xs[xs.size - 1 - i] then
11+
go (i + 1)
12+
else
13+
false
14+
else
15+
true
16+
17+
example (xs : Array Nat) (w : xs.reverse = xs) (j : Nat) (hj : 0 ≤ j) (hj' : j < xs.size / 2) :
18+
xs[j] = xs[xs.size - 1 - j] := by
19+
grind
20+
21+
theorem checkPalin1_correct' : checkPalin1 xs = true ↔ IsPalindrome xs := by
22+
unfold checkPalin1
23+
suffices ∀ i, checkPalin1.go xs i = true ↔ ∀ j, i ≤ j → (_ : j < xs.size / 2) → xs[j] = xs[xs.size - 1 - j] by
24+
rw [this, IsPalindrome]
25+
constructor
26+
· intro w
27+
ext i hi₁ hi₂
28+
· grind
29+
· by_cases h : i < xs.size / 2 <;> grind
30+
· intro w
31+
intro j hj hj'
32+
grind
33+
intro i
34+
fun_induction checkPalin1.go
35+
case case1 j h₁ h₂ ih =>
36+
constructor
37+
· intro w j'
38+
by_cases j' = j <;> grind
39+
· grind
40+
case case2 j h₁ h₂ =>
41+
simp only [Bool.false_eq_true, false_iff, Classical.not_forall]
42+
refine ⟨j, by grind⟩
43+
case case3 x h =>
44+
grind

0 commit comments

Comments
 (0)