lean4-htt/tests/elab/wfEqns5.lean
Joachim Breitner ac9a1cb415
feat: add @[backward_defeq] attribute and local useBackward simp option (#13492)
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>
2026-04-27 10:07:59 +00:00

147 lines
2.9 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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