This PR introduces stricter inference for the `@[defeq]` attribute and a companion `@[backward_defeq]` attribute that preserves the pre-PR behavior as an opt-in. ### What changed * `@[defeq]` is now inferred only when the equation holds at `.instances` transparency (the transparency `dsimp` operates at). * `@[backward_defeq]` is the old set: every theorem whose `rfl` proof the legacy inference would have accepted is tagged `@[backward_defeq]`, so `defeq ⊆ backward_defeq` holds by construction. * The option `backward.defeqAttrib.useBackward` (default `false`) makes `dsimp` also use `@[backward_defeq]` theorems, restoring the pre-PR behavior for a specific proof or file. * The option is eqn-affecting: its value at the point of a function's definition is recorded so that the equation lemmas later generated for that function use the same value, regardless of the ambient option at the use site. ### Mathlib adaption A companion adaption branch (`lean-pr-testing-backward-defeq-attrib` on mathlib4) builds cleanly against this PR and passes `lake test` without warnings. Most adaption changes are scoped `set_option backward.defeqAttrib.useBackward true in` additions on the failing declarations; a small number of files needed proof-level edits where the stored form of a `dsimp%`/`@[reassoc]`/`@[elementwise]` /`@[simps]`/`@[to_app]`-generated lemma had drifted under the stricter regime. --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
147 lines
2.9 KiB
Text
147 lines
2.9 KiB
Text
set_option linter.deprecated.options false
|
||
|
||
def foo : Nat → Nat → Nat
|
||
| 0, m => match m with | 0 => 0 | m => m
|
||
| n+1, m => foo n m
|
||
termination_by n => n
|
||
|
||
/--
|
||
info: equations:
|
||
theorem foo.eq_1 : foo 0 0 = 0
|
||
theorem foo.eq_2 : ∀ (x : Nat), (x = 0 → False) → foo 0 x = x
|
||
theorem foo.eq_3 : ∀ (x n : Nat), foo n.succ x = foo n x
|
||
-/
|
||
#guard_msgs in
|
||
#print equations foo
|
||
|
||
/--
|
||
info: foo.eq_def (x✝ x✝¹ : Nat) :
|
||
foo x✝ x✝¹ =
|
||
match x✝, x✝¹ with
|
||
| 0, m =>
|
||
match m with
|
||
| 0 => 0
|
||
| m => m
|
||
| n.succ, m => foo n m
|
||
-/
|
||
#guard_msgs in
|
||
#check foo.eq_def
|
||
|
||
/-- error: Unknown identifier `foo.eq_4` -/
|
||
#guard_msgs in
|
||
#check foo.eq_4
|
||
|
||
/--
|
||
info: foo._unary.eq_def (_x : (_ : Nat) ×' Nat) :
|
||
foo._unary _x =
|
||
PSigma.casesOn _x fun a a_1 =>
|
||
match a, a_1 with
|
||
| 0, m =>
|
||
match m with
|
||
| 0 => 0
|
||
| m => m
|
||
| n.succ, m => foo._unary ⟨n, m⟩
|
||
-/
|
||
#guard_msgs in
|
||
#check foo._unary.eq_def
|
||
|
||
|
||
set_option backward.eqns.deepRecursiveSplit false in
|
||
def bar : Nat → Nat → Nat
|
||
| 0, m => match m with | 0 => 0 | m => m
|
||
| n+1, m => bar n m
|
||
termination_by n => n
|
||
|
||
/--
|
||
info: equations:
|
||
theorem bar.eq_1 : ∀ (x : Nat),
|
||
bar 0 x =
|
||
match x with
|
||
| 0 => 0
|
||
| m => m
|
||
theorem bar.eq_2 : ∀ (x n : Nat), bar n.succ x = bar n x
|
||
-/
|
||
#guard_msgs in
|
||
#print equations bar
|
||
|
||
/--
|
||
info: bar.eq_def (x✝ x✝¹ : Nat) :
|
||
bar x✝ x✝¹ =
|
||
match x✝, x✝¹ with
|
||
| 0, m =>
|
||
match m with
|
||
| 0 => 0
|
||
| m => m
|
||
| n.succ, m => bar n m
|
||
-/
|
||
#guard_msgs in
|
||
#check bar.eq_def
|
||
|
||
-- Now the same for structural recursion
|
||
|
||
namespace Structural
|
||
|
||
def foo : Nat → Nat → Nat
|
||
| 0, m => match m with | 0 => 0 | m => m
|
||
| n+1, m => foo n m
|
||
termination_by structural n => n
|
||
|
||
/--
|
||
info: equations:
|
||
@[backward_defeq] theorem Structural.foo.eq_1 : foo 0 0 = 0
|
||
theorem Structural.foo.eq_2 : ∀ (x : Nat), (x = 0 → False) → foo 0 x = x
|
||
@[backward_defeq] theorem Structural.foo.eq_3 : ∀ (x n : Nat), foo n.succ x = foo n x
|
||
-/
|
||
#guard_msgs in
|
||
#print equations foo
|
||
|
||
/--
|
||
info: Structural.foo.eq_def (x✝ x✝¹ : Nat) :
|
||
foo x✝ x✝¹ =
|
||
match x✝, x✝¹ with
|
||
| 0, m =>
|
||
match m with
|
||
| 0 => 0
|
||
| m => m
|
||
| n.succ, m => foo n m
|
||
-/
|
||
#guard_msgs in
|
||
#check foo.eq_def
|
||
|
||
/-- error: Unknown identifier `Structural.foo.eq_4` -/
|
||
#guard_msgs in
|
||
#check Structural.foo.eq_4
|
||
|
||
|
||
set_option backward.eqns.deepRecursiveSplit false in
|
||
def bar : Nat → Nat → Nat
|
||
| 0, m => match m with | 0 => 0 | m => m
|
||
| n+1, m => bar n m
|
||
termination_by n => n
|
||
|
||
/--
|
||
info: equations:
|
||
theorem Structural.bar.eq_1 : ∀ (x : Nat),
|
||
bar 0 x =
|
||
match x with
|
||
| 0 => 0
|
||
| m => m
|
||
theorem Structural.bar.eq_2 : ∀ (x n : Nat), bar n.succ x = bar n x
|
||
-/
|
||
#guard_msgs in
|
||
#print equations bar
|
||
|
||
/--
|
||
info: Structural.bar.eq_def (x✝ x✝¹ : Nat) :
|
||
bar x✝ x✝¹ =
|
||
match x✝, x✝¹ with
|
||
| 0, m =>
|
||
match m with
|
||
| 0 => 0
|
||
| m => m
|
||
| n.succ, m => bar n m
|
||
-/
|
||
#guard_msgs in
|
||
#check bar.eq_def
|
||
|
||
end Structural
|