feat: add @[backward_defeq] attribute and local useBackward simp option#13492
Merged
feat: add @[backward_defeq] attribute and local useBackward simp option#13492
@[backward_defeq] attribute and local useBackward simp option#13492Conversation
…option This PR splits the implicit inference of the `@[defeq]` attribute on equation theorems (from `def`, `@[simps]`, structural/WF unfold lemmas, etc.) into two tiers: * A theorem is tagged `@[defeq]` when the equation holds at instance transparency (matching the transparency at which `dsimp` operates). * A theorem is additionally tagged with the new `@[backward_defeq]` attribute whenever the old permissive inference (at `.default`/`.all` transparency) would have accepted it — i.e. the `@[backward_defeq]` set is a superset of `@[defeq]`. The previous permissive behavior for `dsimp` is preserved behind a new option `backward.defeqAttrib.useBackward` (default `false`), which is consumed at `simp`/`dsimp` use sites: when `true`, `dsimp` treats `@[backward_defeq]` theorems like `@[defeq]` ones. Because the option is consumed locally at the use site, adaptation can be per-proof (`set_option backward.defeqAttrib.useBackward true in ...`) rather than requiring the option to be flipped at definition sites. Theorems with `:= rfl` as their proof (via `DefView.markDefEq`) continue to be tagged as `@[defeq]` (and also `@[backward_defeq]`, to maintain the superset invariant) regardless of the stricter inference. Core-library adaptations: * `Init/BinderNameHint`: marked `@[implicit_reducible]`. * `Std/Data/DHashMap/Internal/Model`: two `dsimp only` proofs rewritten to `simp only` with an extra `AssocList.contains_eq` argument. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`@[defeq]` now additionally tags `@[backward_defeq]` (done inside the `defeq` attribute's validator), so that theorems auto-tagged `@[defeq]` via `DefView.markDefEq` (the `:= rfl` path) also end up in the `@[backward_defeq]` set. This keeps the superset invariant uniform: any theorem that would have been treated as rfl under the permissive pre-stricter-inference rules is in `@[backward_defeq]`, whether the tagging came from `inferDefEqAttr`, explicit `@[defeq]`, or the `:= rfl` auto-tagging. `mkSimpTheoremCore` similarly enforces `rfl → backwardRfl` on the `SimpTheorem` fields. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Collaborator
|
Reference manual CI status:
|
|
Mathlib CI status (docs):
|
Without this, even with `backward.defeqAttrib.useBackward true` globally, `dsimp` could pick up a `@[backward_defeq]` theorem (via the gate in `rewriteUsingIndex?`/`rewriteNoIndex?`), but the `tryTheoremCore` path would still fall through to emitting an explicit rewrite proof rather than using the implicit-defeq-proof shortcut. The resulting generated proof term differed from the one produced when the theorem was `@[defeq]`, which broke downstream metaprograms that translate proof terms (e.g. `@[to_dual]`): the reordered translation ended up ill-typed at the kernel level. Making `useImplicitDefEqProof` treat a `@[backward_defeq]` theorem as rfl-like when the option is on restores proof-term equivalence with the pre-stricter-inference behavior, and removes the need for mathlib-side metaprogramming patches (e.g. the `@[simps]`/`@[to_dual]` "set `[defeq]` directly for rfl proofs" workaround). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
mathlib-nightly-testing Bot
pushed a commit
to leanprover-community/batteries
that referenced
this pull request
Apr 21, 2026
mathlib-nightly-testing Bot
pushed a commit
to leanprover-community/mathlib4-nightly-testing
that referenced
this pull request
Apr 21, 2026
mathlib-nightly-testing Bot
pushed a commit
to leanprover-community/batteries
that referenced
this pull request
Apr 21, 2026
mathlib-nightly-testing Bot
pushed a commit
to leanprover-community/mathlib4-nightly-testing
that referenced
this pull request
Apr 21, 2026
This extends `backward.defeqAttrib.useBackward` to be stored at definition time and replayed at equation realization time, alongside `backward.eqns.nonrecursive` and `backward.eqns.deepRecursiveSplit`. Previously the option was consumed only at simp use sites. But certain downstream metaprograms (observed in `@[simps!]`-generated projections and their interaction with `Equiv.trans_def`-style rfl lemmas) trigger equation realization whose proof terms depend on the option; if it's only set at the use site, the realized const differs from what the use-site expects. Making the option eqn-affecting means the defining module's value of the option is replayed during realization, restoring the expected behavior. To share the option declaration between `Lean.Meta.Eqns` and `Lean.Meta.Tactic.Simp.SimpTheorems`, it is moved to `Lean.DefEqAttrib` (imported by both). Consequence for downstream adaptation: the option now acts non-locally — to restore the legacy behavior of a proof, the option may need to be set at the file level of both the defining module and the using module, not just at the using proof. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
mathlib-nightly-testing Bot
pushed a commit
to leanprover-community/batteries
that referenced
this pull request
Apr 21, 2026
mathlib-nightly-testing Bot
pushed a commit
to leanprover-community/mathlib4-nightly-testing
that referenced
this pull request
Apr 21, 2026
… generation (#13475) This PR replaces the eager equation realization that was triggered by non-default values of equation-affecting options (like `backward.eqns.nonrecursive`) with a `MapDeclarationExtension` that stores non-default option values at definition time. These values are then restored when equations are lazily realized, so the same equations are produced regardless of when generation occurs. Restoring the options is done via a new `withEqnOptions` helper in `Lean.Meta.Eqns`. Because `realizeConst` overrides the caller's options with the options saved in its `RealizationContext` — which are empty for imported constants — the helper must also be applied inside the `realizeConst` callbacks in `mkSimpleEqThm`, `mkEqns` (in `Elab/PreDefinition/Eqns.lean`), `getConstUnfoldEqnFor?`, and `Structural.mkUnfoldEq`. Without that, equation generation code that reads eqn-affecting options inside the realize callback would see the caller-independent defaults rather than the values stored in `eqnOptionsExt` — so the store-at-definition-time behavior would not carry across module boundaries. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This PR fixes a benchmark regression introduced in #13475: `eqnOptionsExt` was using `.async .asyncEnv` asyncMode, which accumulates state in the `checked` environment and can block. Switching to `.local` — consistent with the neighbouring `eqnsExt` and the other declaration caches in `src/Lean/Meta` — restores performance (the `build/profile/blocked (unaccounted) wall-clock` bench moves from +33% back to baseline). `.local` is safe here because `saveEqnAffectingOptions` is only called during top-level `def` elaboration and downstream readers see the imported state; modifications on non-main branches are merged into the main branch on completion. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
mathlib-nightly-testing Bot
pushed a commit
to leanprover-community/batteries
that referenced
this pull request
Apr 21, 2026
mathlib-nightly-testing Bot
pushed a commit
to leanprover-community/mathlib4-nightly-testing
that referenced
this pull request
Apr 21, 2026
Use \`getAsyncConstInfo\` + \`toConstantInfo\` instead of plain \`getConstInfo\`, otherwise \`info.value?\` may return \`none\` for theorems whose body is still being filled in by async elaboration. The caller (e.g. mathlib's \`@[simps!]\`) then sees an axiom-shaped proxy and silently skips tagging the theorem. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This reverts commit 390403b.
… rfl proofs The legacy \`inferDefEqAttr\` (pre-\`[defeq]\`/\`[backward_defeq]\` split) tagged \`@[defeq]\` unconditionally whenever \`isRflProofCore\` returned true — only logging an error if \`validateDefEqAttr\` also failed. The new split variant instead gated \`@[backward_defeq]\` tagging on the validation succeeding, meaning some theorems that the old inference covered get NEITHER tag now. That breaks the superset guarantee between old \`[defeq]\` and new \`[backward_defeq]\`, so \`set_option backward.defeqAttrib.useBackward true\` does not reliably restore pre-PR behavior. Tag \`@[backward_defeq]\` unconditionally on \`isRfl\` theorems, matching the old inference's unconditional tag. Still log when strict fails and permissive validation also fails, as before.
…achable This PR adds a test case exploring whether the `unless strict do logError` branch in `inferDefEqAttr` (which swallows `validateDefEqAttr` errors when the strict `.instances`-transparency check succeeded) can actually be reached. Attempting to construct such a case with `@[reducible]` and/or smart-unfolding shows that every public theorem where strict could succeed also has permissive succeed — the `withoutExporting` in `inferDefEqAttr` does not give strict a bigger view than the permissive validator has. The defensive guard therefore appears to be unreachable in practice; the test documents the phenomenon for future reference.
mathlib-nightly-testing Bot
pushed a commit
to leanprover-community/batteries
that referenced
this pull request
Apr 22, 2026
mathlib-nightly-testing Bot
pushed a commit
to leanprover-community/mathlib4-nightly-testing
that referenced
this pull request
Apr 22, 2026
mathlib-nightly-testing Bot
pushed a commit
to leanprover-community/batteries
that referenced
this pull request
Apr 22, 2026
mathlib-nightly-testing Bot
pushed a commit
to leanprover-community/mathlib4-nightly-testing
that referenced
this pull request
Apr 22, 2026
…lean4 into joachim/backward-defeq-attrib
mathlib-nightly-testing Bot
pushed a commit
to leanprover-community/batteries
that referenced
this pull request
Apr 24, 2026
mathlib-nightly-testing Bot
pushed a commit
to leanprover-community/mathlib4-nightly-testing
that referenced
this pull request
Apr 24, 2026
…lean4 into joachim/backward-defeq-attrib
mathlib-nightly-testing Bot
pushed a commit
to leanprover-community/batteries
that referenced
this pull request
Apr 27, 2026
mathlib-nightly-testing Bot
pushed a commit
to leanprover-community/mathlib4-nightly-testing
that referenced
this pull request
Apr 27, 2026
mathlib-bors Bot
pushed a commit
to leanprover-community/mathlib4
that referenced
this pull request
Apr 29, 2026
…ng-line fixers (#38602) This PR cherry-picks a set of adaptation-helper scripts that @nomeata developed on the `lean-pr-testing-13492` branch during the adaptation for leanprover/lean4#13492 (now merged). He flagged on Zulip ([thread](https://leanprover.zulipchat.com/#narrow/channel/428973-nightly-testing/topic/lean.2313492.2C.20stricter.20.40.5Bdefeq.5D.20inference/near/591078800)) that he plans to delete them from the adaptation branch and offered them up for adoption. Changes: - `scripts/add_set_option.py`, `scripts/rm_set_option.py`: new `--value` flag, so the scripts work for options whose default is `false` (where you want to insert `set_option X true in`) as well as the existing `false` case. - `scripts/add_module_set_option.py` (new): file-level variant of `add_set_option.py` — inserts a module-scoped `set_option` after the imports (and module docstring, if present) rather than scoped to a declaration. Needed for eqn-affecting options. - `scripts/rm_module_set_option.py` (new): file-level variant of `rm_set_option.py`. Traverses the import DAG backward, with resume support keyed on toolchain. - `scripts/fix_nonlocal_set_option.py` (new): given failing modules, adds the option to all transitive deps then binary-search minimizes back to the smallest set whose modules need it. - `scripts/fix_unused_simp_args.py` (new): parses `This simp argument is unused: X` warnings from `lake build` and removes `X` from the corresponding `simp` / `simp only` call. For `← X` it rewrites to `- X` because the `←` has simp-set side effects. - `scripts/fix_long_lines.py` (new): breaks too-long lines at the last comma before column 100 and indents the continuation. Useful as a follow-up to scripts that rewrite simp lists. - `scripts/README.md`: documents all of the above. The defaults for `--option` in the new module-level scripts point at `backward.defeq.atInstanceTransparency` (the option from leanprover/lean4#13492); for current options pass `--option <name>` explicitly. Original commits from `lean-pr-testing-13492`: - `6f1a3512906` chore(scripts): add `--value` flag to add/rm_set_option.py (had a small conflict with #38532's `lakefile.lean` existence check; resolved by combining) - `36625b2a675` chore(scripts): bring back module-level / nonlocal scripts - `de2a59b5839` chore(scripts): thread `--value` through `fix_nonlocal_set_option.py` - `45f3ee41b94` (extracted) `fix_unused_simp_args.py` - `4278b885305` (extracted) `fix_long_lines.py` - `80852596213` doc(scripts): document new helpers I sanity-checked each script: all parse, all `--help`s render, and `fix_long_lines.py` / `fix_unused_simp_args.py` produce the expected output on synthetic inputs. 🤖 Prepared with Claude Code Co-authored-by: Joachim Breitner <mail@joachim-breitner.de>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR introduces stricter inference for the
@[defeq]attribute and acompanion
@[backward_defeq]attribute that preserves the pre-PR behavioras an opt-in.
What changed
@[defeq]is now inferred only when the equation holds at.instancestransparency (the transparencydsimpoperates at).@[backward_defeq]is the old set: every theorem whoserflproofthe legacy inference would have accepted is tagged
@[backward_defeq],so
defeq ⊆ backward_defeqholds by construction.backward.defeqAttrib.useBackward(defaultfalse) makesdsimpalso use@[backward_defeq]theorems, restoring the pre-PRbehavior for a specific proof or file.
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-attribonmathlib4) builds cleanly against this PR and passes
lake testwithoutwarnings. Most adaption changes are scoped
set_option backward.defeqAttrib.useBackward true inadditions on thefailing 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 stricterregime.