fix: auxiliary matcher definitions should be treated as abbreviations

The motivation is to prevent performance problems such as the one
described at issue #854.

Fixes #854 after a update stage0
This commit is contained in:
Leonardo de Moura 2021-12-07 16:06:41 -08:00
parent b0fe1e5d10
commit 41040a81de
3 changed files with 30 additions and 9 deletions

View file

@ -1035,14 +1035,18 @@ private def tryHeuristic (t s : Expr) : MetaM Bool := do
let tFn := t.getAppFn
let sFn := s.getAppFn
let info ← getConstInfo tFn.constName!
/- We only use the heuristic when `f` is a regular definition. That is, it is marked an abbreviation
(e.g., a user-facing projection) or as opaque (e.g., proof).
/- We only use the heuristic when `f` is a regular definition or an auxiliary `match` application.
That is, it is not marked an abbreviation (e.g., a user-facing projection) or as opaque (e.g., proof).
We check whether terms contain metavariables to make sure we can solve constraints such
as `S.proj ?x =?= S.proj t` without performing delta-reduction.
That is, we are assuming the heuristic implemented by this method is seldom effective
when `t` and `s` do not have metavariables, are not structurally equal, and `f` is an abbreviation.
On the other hand, by unfolding `f`, we often produce smaller terms. -/
unless info.hints.isRegular do
On the other hand, by unfolding `f`, we often produce smaller terms.
Recall that auxiliary `match` definitions are marked as abbreviations, but we must use the heuristic on
them since they will not be unfolded when smartUnfolding is turned on. The abbreviation annotation in this
case is used to help the kernel type checker. -/
unless info.hints.isRegular || isMatcherCore (← getEnv) tFn.constName! do
unless t.hasExprMVar || s.hasExprMVar do
return false
traceCtx `Meta.isDefEq.delta do

View file

@ -715,11 +715,11 @@ def mkMatcherAuxDefinition (name : Name) (type : Expr) (value : Expr) : MetaM (E
| some nameNew => (mkMatcherConst nameNew, none)
| none =>
let decl := Declaration.defnDecl {
name := name,
levelParams := result.levelParams.toList,
type := result.type,
value := result.value,
hints := ReducibilityHints.regular (getMaxHeight env result.value + 1),
name
levelParams := result.levelParams.toList
type := result.type
value := result.value
hints := ReducibilityHints.abbrev
safety := if env.hasUnsafe result.type || env.hasUnsafe result.value then DefinitionSafety.unsafe else DefinitionSafety.safe
}
trace[Meta.debug] "{name} : {result.type} := {result.value}"

17
tests/lean/run/854.lean Normal file
View file

@ -0,0 +1,17 @@
example (a : Nat) : (a - 8000) + 1 = Nat.succ (a - 8000) := by
rfl
theorem mwe (a : Nat) : (a - 100000000) + 1 <= 1 := by
apply Nat.succ_le_succ
sorry
@[irreducible]
def someConstant : Nat := 100000000
theorem mwe' (a : Nat) : (a - someConstant) + 1 <= someConstant + 1 := by
apply Nat.succ_le_succ
sorry
theorem mwe'' (a : Nat) : Nat.add (a - 100000000) 1 <= Nat.zero.succ := by
apply Nat.succ_le_succ
sorry