Skip to content

Commit a157abb

Browse files
authored
fix: E-matching patterns containing ground universe polymorphic patterns in grind (leanprover#9857)
This PR ensures `grind` can E-match patterns containing universe polymorphic ground sub-patterns. For example, given ``` set_option pp.universes true in attribute [grind?] Id.run_pure ``` the pattern ``` Id.run_pure.{u_1}: [@Id.run.{u_1} #1 (@pure.{u_1, u_1} `[Id.{u_1}] `[Applicative.toPure.{u_1, u_1}] _ #0)] ``` contains two nested universe polymorphic ground patterns - `Id.{u_1}` - `Applicative.toPure.{u_1, u_1}` This kind of pattern is not common, but it occurs in core.
1 parent 5abf4bb commit a157abb

File tree

3 files changed

+50
-21
lines changed

3 files changed

+50
-21
lines changed

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

Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -131,30 +131,41 @@ protected def _root_.Lean.Meta.Grind.GenPatternInfo.assign? (genInfo : GenPatter
131131
let c ← assignDelayedEqProof? c genInfo.hIdx
132132
return c
133133

134+
private def matchGroundPattern (pArg eArg : Expr) : GoalM Bool := do
135+
/-
136+
1) Remark:
137+
We need to use `withReducibleAndInstances` because ground patterns are often instances.
138+
Here is an example
139+
```
140+
instance : Max Nat where
141+
max := Nat.max -- Redefined the instance
142+
143+
example (a : Nat) : max a a = a := by
144+
grind
145+
```
146+
Possible future improvements:
147+
- When `diagnostics` is true, try with `withDefault` and report issue if it succeeds.
148+
- (minor) Only use `withReducibleAndInstances` if the argument is an implicit instance.
149+
Potential issue: some user write `{_ : Class α}` when the instance can be inferred from
150+
explicit arguments.
151+
2) Remark:
152+
If `pArg` contains universe metavariables, we use `withoutModifyingMCtx` to ensure the metavariables
153+
are not assigned. These universe metavariables are created at `internalizePattern` for universe polymorphic
154+
ground patterns. They are not common, but they occur in practice.
155+
-/
156+
if pArg.hasLevelMVar then
157+
withoutModifyingMCtx <| withReducibleAndInstances <| isDefEq pArg eArg
158+
else
159+
isEqv pArg eArg <||> withReducibleAndInstances (isDefEq pArg eArg)
160+
134161
/-- Matches a pattern argument. See `matchArgs?`. -/
135162
private def matchArg? (c : Choice) (pArg : Expr) (eArg : Expr) : OptionT GoalM Choice := do
136163
if isPatternDontCare pArg then
137164
return c
138165
else if pArg.isBVar then
139166
assign? c pArg.bvarIdx! eArg
140167
else if let some pArg := groundPattern? pArg then
141-
/-
142-
We need to use `withReducibleAndInstances` because ground patterns are often instances.
143-
Here is an example
144-
```
145-
instance : Max Nat where
146-
max := Nat.max -- Redefined the instance
147-
148-
example (a : Nat) : max a a = a := by
149-
grind
150-
```
151-
Possible future improvements:
152-
- When `diagnostics` is true, try with `withDefault` and report issue if it succeeds.
153-
- (minor) Only use `withReducibleAndInstances` if the argument is an implicit instance.
154-
Potential issue: some user write `{_ : Class α}` when the instance can be inferred from
155-
explicit arguments.
156-
-/
157-
guard (← isEqv pArg eArg <||> withReducibleAndInstances (isDefEq pArg eArg))
168+
guard (← matchGroundPattern pArg eArg)
158169
return c
159170
else if let some (pArg, k) := isOffsetPattern? pArg then
160171
assert! Option.isNone <| isOffsetPattern? pArg
@@ -165,7 +176,7 @@ private def matchArg? (c : Choice) (pArg : Expr) (eArg : Expr) : OptionT GoalM C
165176
let c ← assign? c pArg.bvarIdx! eArg
166177
genInfo.assign? c eArg
167178
else if let some pArg := groundPattern? pArg then
168-
guard (← isEqv pArg eArg <||> withReducibleAndInstances (isDefEq pArg eArg))
179+
guard (← matchGroundPattern pArg eArg)
169180
genInfo.assign? c eArg
170181
else if let some (pArg, k) := isOffsetPattern? pArg then
171182
return { c with cnstrs := .offset (some genInfo) pArg k eArg :: c.cnstrs }

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

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ public import Lean.Meta.LitValues
1212
public import Lean.Meta.Match.MatcherInfo
1313
public import Lean.Meta.Match.MatchEqsExt
1414
public import Lean.Meta.Match.MatchEqs
15+
public import Lean.Util.CollectLevelParams
1516
public import Lean.Meta.Tactic.Grind.Types
1617
public import Lean.Meta.Tactic.Grind.Util
1718
public import Lean.Meta.Tactic.Grind.Canon
@@ -151,7 +152,7 @@ private def mkENode' (e : Expr) (generation : Nat) : GoalM Unit :=
151152
mkENodeCore e (ctor := false) (interpreted := false) (generation := generation)
152153

153154
/-- Internalizes the nested ground terms in the given pattern. -/
154-
private partial def internalizePattern (pattern : Expr) (generation : Nat) : GoalM Expr := do
155+
private partial def internalizePattern (pattern : Expr) (generation : Nat) (origin : Origin) : GoalM Expr := do
155156
-- Recall that it is important to ensure patterns are maximally shared since
156157
-- we assume that in functions such as `getAppsOf` in `EMatch.lean`
157158
go (← shareCommon pattern)
@@ -161,7 +162,21 @@ where
161162
return pattern
162163
else if let some e := groundPattern? pattern then
163164
let e ← preprocessLight e
164-
internalize e generation none
165+
let e ← if e.hasLevelParam && origin matches .decl _ then
166+
/-
167+
If `e` has universe parameters and it is **not** local. That is,
168+
it contains the universe parameters of some global theorem.
169+
Then, we convert `e`'s universe parameters into universe meta-variables.
170+
Remark: it is pointless to internalize the result because it contains these helper meta-variables.
171+
Remark: universe polymorphic ground patterns are not common, but they do occur in the
172+
core library.
173+
-/
174+
let ps := collectLevelParams {} e |>.params
175+
let us ← ps.mapM fun _ => mkFreshLevelMVar
176+
pure <| e.instantiateLevelParamsArray ps us
177+
else
178+
internalize e generation none
179+
pure e
165180
return mkGroundPattern e
166181
else pattern.withApp fun f args => do
167182
return mkAppN f (← args.mapM go)
@@ -203,7 +218,7 @@ def activateTheorem (thm : EMatchTheorem) (generation : Nat) : GoalM Unit := do
203218
-- Recall that we use the proof as part of the key for a set of instances found so far.
204219
-- We don't want to use structural equality when comparing keys.
205220
let proof ← shareCommon thm.proof
206-
let thm := { thm with proof, patterns := (← thm.patterns.mapM (internalizePattern · generation)) }
221+
let thm := { thm with proof, patterns := (← thm.patterns.mapM (internalizePattern · generation thm.origin)) }
207222
trace_goal[grind.ematch] "activated `{thm.origin.key}`, {thm.patterns.map ppPattern}"
208223
modify fun s => { s with ematch.newThms := s.ematch.newThms.push thm }
209224

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
/-! Test for E-matching patterns containing nested universe polymorphic ground patterns. -/
2+
example : Id.run (pure true) = true := by
3+
grind only [Id.run_pure]

0 commit comments

Comments
 (0)