fix: unfoldReducible' optimization regression in grind (#9682)
This PR fixes a regression introduced by an optimization in the `unfoldReducible` step used by the `grind` normalizer. It also ensures that projection functions are not reduced, as they are folded in a later step.
This commit is contained in:
parent
08c3f3c236
commit
3056848819
3 changed files with 81 additions and 5 deletions
|
|
@ -41,15 +41,19 @@ def dsimpCore (e : Expr) : GrindM Expr := do profileitM Exception "grind dsimp"
|
|||
|
||||
/--
|
||||
Unfolds all `reducible` declarations occurring in `e`.
|
||||
Similar to `unfoldReducible`, but uses `inShareCommon` as an extra filter
|
||||
Similar to `unfoldReducible`, but uses `alreadyInternalized` as an extra filter
|
||||
-/
|
||||
def unfoldReducible' (e : Expr) : GrindM Expr := do
|
||||
def unfoldReducible' (e : Expr) : GoalM Expr := do
|
||||
if !(← isUnfoldReducibleTarget e) then return e
|
||||
let pre (e : Expr) : GrindM TransformStep := do
|
||||
if (← inShareCommon e) then return .done e
|
||||
let pre (e : Expr) : GoalM TransformStep := do
|
||||
-- We used to use `inShareCommon` here, but it was to correct.
|
||||
-- `inShareCommon` is used in terms that have not been preprocessed (e.g., proofs).
|
||||
if (← alreadyInternalized e) then return .done e
|
||||
let .const declName _ := e.getAppFn | return .continue
|
||||
unless (← isReducible declName) do return .continue
|
||||
if isGrindGadget declName then return .continue
|
||||
-- See comment at isUnfoldReducibleTarget.
|
||||
if (← getEnv).isProjectionFn declName then return .continue
|
||||
let some v ← unfoldDefinition? e | return .continue
|
||||
return .visit v
|
||||
Core.transform e (pre := pre)
|
||||
|
|
|
|||
|
|
@ -59,7 +59,9 @@ def isUnfoldReducibleTarget (e : Expr) : CoreM Bool := do
|
|||
return Option.isSome <| e.find? fun e => Id.run do
|
||||
let .const declName _ := e | return false
|
||||
if getReducibilityStatusCore env declName matches .reducible then
|
||||
return !isGrindGadget declName
|
||||
-- Remark: it is wasteful to unfold projection functions since
|
||||
-- kernel projections are folded again in the `foldProjs` preprocessing step.
|
||||
return !isGrindGadget declName && !env.isProjectionFn declName
|
||||
else
|
||||
return false
|
||||
|
||||
|
|
@ -72,6 +74,8 @@ def unfoldReducible (e : Expr) : MetaM Expr := do
|
|||
let .const declName _ := e.getAppFn | return .continue
|
||||
unless (← isReducible declName) do return .continue
|
||||
if isGrindGadget declName then return .continue
|
||||
-- See comment at isUnfoldReducibleTarget.
|
||||
if (← getEnv).isProjectionFn declName then return .continue
|
||||
let some v ← unfoldDefinition? e | return .continue
|
||||
return .visit v
|
||||
Core.transform e (pre := pre)
|
||||
|
|
|
|||
68
tests/lean/run/grind_unfold_reducible_regression.lean
Normal file
68
tests/lean/run/grind_unfold_reducible_regression.lean
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
import Std
|
||||
|
||||
open Std
|
||||
|
||||
/--
|
||||
An extensional tree map with a default value.
|
||||
|
||||
To preserve extensionality, we require that the default value is not present in the tree.
|
||||
|
||||
**Implementation note**: we use `Ord α` rather than a `cmp : α → α → Ordering` argument,
|
||||
because `grind` can not instantiate `ReflCmp` and `TransCmp` theorems because there is no constant to key on.
|
||||
-/
|
||||
structure TreeMapD (α : Type u) [Ord α] [TransOrd α] (β : Type v) (d : β) where
|
||||
tree : ExtTreeMap α β compare
|
||||
no_default : ∀ a : α, tree[a]? ≠ some d := by grind
|
||||
|
||||
namespace TreeMapD
|
||||
|
||||
variable {α : Type u} [Ord α] [TransOrd α] {β : Type v} {d : β}
|
||||
|
||||
instance : GetElem (TreeMapD α β d) α β (fun _ _ => True) where
|
||||
getElem := fun m a _ => m.tree[a]?.getD d
|
||||
|
||||
@[local grind] private theorem getElem_mk
|
||||
(tree : ExtTreeMap α β compare) (no_default : ∀ a : α, tree[a]? ≠ some d) (a : α) :
|
||||
(TreeMapD.mk tree no_default)[a] = tree[a]?.getD d := rfl
|
||||
|
||||
@[local grind] private theorem getElem?_tree [DecidableEq β] (m : TreeMapD α β d) (a : α) :
|
||||
m.tree[a]? = if m[a] = d then none else some m[a] := by
|
||||
grind [cases TreeMapD]
|
||||
|
||||
@[local grind] private theorem mem_tree (m : TreeMapD α β d) (a : α) :
|
||||
a ∈ m.tree ↔ m[a] ≠ d := by
|
||||
grind [cases TreeMapD]
|
||||
|
||||
@[ext, grind ext]
|
||||
theorem ext [LawfulEqOrd α] {m₁ m₂ : TreeMapD α β d} (h : ∀ a : α, m₁[a] = m₂[a]) : m₁ = m₂ := by
|
||||
rcases m₁ with ⟨tree₁, no_default₁⟩
|
||||
rcases m₂ with ⟨tree₂, no_default₂⟩
|
||||
congr
|
||||
ext a b
|
||||
specialize h a
|
||||
grind
|
||||
|
||||
def empty : TreeMapD α β d where
|
||||
tree := ∅
|
||||
|
||||
instance : EmptyCollection (TreeMapD α β d) :=
|
||||
⟨empty⟩
|
||||
|
||||
@[grind =] theorem empty_eq_emptyc : (empty : TreeMapD α β d) = ∅ := rfl
|
||||
|
||||
instance : Inhabited (TreeMapD α β d) :=
|
||||
⟨empty⟩
|
||||
|
||||
@[grind =] theorem getElem_empty (a : α) : (∅ : TreeMapD α β d)[a] = d := rfl
|
||||
|
||||
variable [DecidableEq β]
|
||||
|
||||
def insert (m : TreeMapD α β d) (a : α) (b : β) : TreeMapD α β d where
|
||||
tree := if b = d then m.tree.erase a else m.tree.insert a b
|
||||
no_default := by
|
||||
-- `grind` can't do this split because of the dependent typing in the `xs[i]?` notation.
|
||||
split <;> grind
|
||||
|
||||
@[grind =] theorem getElem_insert [DecidableEq α] [LawfulEqOrd α] (m : TreeMapD α β d) (a : α) (b : β) :
|
||||
(m.insert a b)[k] = if k = a then b else m[k] := by
|
||||
grind [insert]
|
||||
Loading…
Add table
Reference in a new issue