Commit graph

6844 commits

Author SHA1 Message Date
Sebastian Ullrich
c595413fcc
test: robustify but also CI-disable idbg test for now (#12667) 2026-02-24 09:19:53 +00:00
Kyle Miller
cd7f55b6c9
feat: pp.mdata (#12606)
This PR adds the pretty printer option `pp.mdata`, which causes the
pretty printer to annotate terms with any metadata that is present. For
example,
```lean
set_option pp.mdata true
/-- info: [mdata noindex:true] 2 : Nat -/
#guard_msgs in #check no_index 2
```
The `[mdata ...] e` syntax is only for pretty printing.

Thanks to @Rob23oba for an initial version.

Closes #10929
2026-02-24 04:30:26 +00:00
Kyle Miller
673d1a038c
feat: clean up binder annotations inside of let rec definitions (#12608)
This PR continues #9674, cleaning up binder annotations inside the
bodies of `let rec` and `where` definitions.

Closes #11025
2026-02-24 04:24:47 +00:00
Sebastian Graf
cdbed919ec
fix: preserve TermInfo for do-match discriminant variables (#12666)
This PR fixes spurious unused variable warnings for variables used in
non-atomic match discriminants in `do` notation. For example, in `match
Json.parse s >>= fromJson? with`, the variable `s` would be reported as
unused.

The root cause is that `expandNonAtomicDiscrs?` eagerly elaborates the
discriminant via `Term.elabTerm`, which creates TermInfo for variable
references. The result is then passed to `elabDoElem` for further
elaboration. When the match elaboration is postponed (e.g. because the
discriminant type contains an mvar from `fromJson?`), the result is a
postponed synthetic mvar. The `withTermInfoContext'` wrapper in
`elabDoElemFns` checks `isTacticOrPostponedHole?` on this result,
detects a postponed mvar, and replaces the info subtree with a `hole`
node — discarding all the TermInfo that was accumulated during
discriminant elaboration.

The fix applies `mkSaveInfoAnnotation` to the result, which prevents
`isTacticOrPostponedHole?` from recognizing it as a hole. This is the
same mechanism that `elabLetMVar` uses to preserve info trees when the
body is a metavariable.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 23:54:17 +00:00
Sebastian Graf
65e5053008
fix: add TermInfo for mut vars in ControlStack.stateT.runInBase (#12661)
This PR fixes false-positive "unused variable" warnings for mutable
variables reassigned inside `try`/`catch` blocks with the new do
elaborator.

The root cause was that `ControlStack.stateT.runInBase` packed mutable
variables into a state tuple without calling `Term.addTermInfo'`, so the
unused variable linter could not see that the variables were used. The
fix mirrors how the `for` loop elaborator handles the same pattern in
`useLoopMutVars`.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 19:21:40 +00:00
Sebastian Ullrich
8f80881c2f
feat: idbg interactive debug expression evaluator (#12648)
This PR adds the experimental `idbg e`, a new do-element (and term)
syntax for live debugging between the language server and a running
compiled Lean program.

When placed in a `do` block, `idbg` captures all local variables in
scope and expression `e`, then:

- **In the language server**: starts a TCP server on localhost waiting
for the running program to
connect; the editor will mark this part of the program as "in progress"
during this wait but that
  will not block `lake build` of the project.
- **In the compiled program**: on first execution of the `idbg` call
site, connects to the server,
receives the expression, compiles and evaluates it using the program's
actual runtime values, and
  sends the `repr` result back.

The result is displayed as an info diagnostic on the `idbg` keyword. The
expression `e` can be
edited while the program is running - each edit triggers re-elaboration
of `e`, a new TCP exchange,
and an updated result. This makes `idbg` a live REPL for inspecting and
experimenting with
program state at a specific point in execution. Only when `idbg` is
inserted, moved, or removed does
the program need to be recompiled and restarted.

# Known Limitations

* The program will poll for the server for up to 10 minutes and needs to
be killed manually
  otherwise.
* Use of multiple `idbg` at once untested, likely too much overhead from
overlapping imports without
  further changes.
* `LEAN_PATH` must be properly set up so compiled program can import its
origin module.
* Untested on Windows and macOS.
2026-02-23 17:22:44 +00:00
Kim Morrison
ed0fd1e933
perf: restrict nontrivial class projection classification to reducible transparency (#12650)
This PR fixes a performance regression introduced by enabling
`backward.whnf.reducibleClassField`
(https://github.com/leanprover/lean4/pull/12538). The
`isNonTrivialRegular` function in `ExprDefEq` was classifying class
projections as nontrivial at all transparency levels, but the extra
`.instances` reduction in `unfoldDefault` that motivates this
classification only applies at `.reducible` transparency. At higher
transparency levels, the nontrivial classification caused unnecessary
heuristic comparison attempts in `isDefEqDelta` that cascaded through
BitVec reductions, causing elaboration of `Lean.Data.Json.Parser` to
double from ~3.6G to ~7.2G instructions.

The fix restricts the nontrivial classification to `.reducible`
transparency only, matching the scope of `unfoldDefault`'s extra
reduction behavior.

🤖 Prepared with Claude Code

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 15:10:45 +00:00
Wojciech Różowski
24380fc900
feat: unfold nullary constants in cbv (#12646)
This PR enables the `cbv` tactic to unfold nullary (non-function)
constant
definitions such as `def myNat : Nat := 42`, allowing ground term
evaluation
(e.g. `evalEq`, `evalLT`) to recognize their values as literals.

Previously, `handleConst` skipped all nullary constants. Now it performs
direct
delta reduction using `instantiateValueLevelParams` instead of going
through
the equation theorem machinery (`getUnfoldTheorem`), which would trigger
`realizeConst` and fail for constants (such as derived typeclass
instances)
where `enableRealizationsForConst` has not been called.

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 11:46:57 +00:00
Sebastian Ullrich
ebd22c96ee
fix: mark failed compilations as noncomputable (#12625)
This PR ensures that failure in initial compilation marks the relevant
definitions as `noncomputable`, inside and outside `noncomputable
section`, so that follow-up errors/noncomputable markings are detected
in initial compilation as well instead of somewhere down the pipeline.

This may require additional `noncomputable` markers on definitions that
depend on definitions inside `noncomputable section` but accidentally
passed the new computability check.

Reported at
https://leanprover.zulipchat.com/#narrow/channel/270676-lean4/topic/Cryptic.20error.20message.20in.20new.20lean.20toolchain.3F.
2026-02-23 09:18:21 +00:00
Markus Himmel
7b3d778ab0
feat: simprocs for String.toList and String.push (#12642)
This PR adds dsimprocs for reducing `String.toList` and `String.push`.
2026-02-23 07:39:27 +00:00
Leonardo de Moura
70aa6bc81d
fix: detect stuck mvars through auxiliary parent projections (#12564)
This PR fixes `getStuckMVar?` to detect stuck metavariables through
auxiliary parent projections created for diamond inheritance. These
coercions (e.g., `AddMonoid'.toAddZero'`) are not registered as regular
projections because they construct the parent value from individual
fields rather than extracting a single field. Previously,
`getStuckMVar?` would give up when encountering them, preventing TC
synthesis from being triggered.

- Add `AuxParentProjectionInfo` environment extension to `ProjFns.lean`
recording `numParams` and `fromClass` for these coercions
- Register the info during structure elaboration in
`mkCoercionToCopiedParent`
- Consult the new extension in `getStuckMVar?` as a fallback when
`getProjectionFnInfo?` returns `none`

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Kim Morrison <kim@tqft.net>
2026-02-23 03:46:06 +00:00
Leonardo de Moura
93683eb455
feat: enable backward.whnf.reducibleClassField (#12538)
This PR enables `backward.whnf.reducibleClassField` for v4.29.

The support is particularly important when the user marks a class field
as `[reducible]` and
the transparency mode is `.reducible`. For example, suppose `e` is `a ≤
b` where `a b : Nat`,
and `LE.le` is marked as `[reducible]`. Simply unfolding `LE.le` would
give `instLENat.1 a b`,
which would be stuck because `instLENat` has transparency
`[instance_reducible]`. To avoid this, when we unfold
a `[reducible]` class field, we also unfold the associated projection
`instLENat.1` using
`.instances` reducibility, ultimately returning `Nat.le a b`.

---------

Co-authored-by: Paul Reichert <6992158+datokrat@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Kim Morrison <kim@tqft.net>
2026-02-22 23:22:14 +00:00
Leonardo de Moura
c2ec2ecab1
fix: handle class projections in isNonTrivialRegular for backward.whnf.reducibleClassField (#12639)
This PR fixes the interaction between
`backward.whnf.reducibleClassField` and `isDefEqDelta`'s
argument-comparison heuristic.

When `backward.whnf.reducibleClassField` is enabled, `unfoldDefault`
reduces class field projections past the `.proj` form at `.instances`
transparency. This causes `isDefEqDelta` to lose the instance structure
that `isDefEqProj` needs to bump transparency for instance-implicit
parameters. The fix adds an `.abbrev` branch in `isNonTrivialRegular`
that classifies class field projections as nontrivial when the option is
enabled, so `tryHeuristic` applies the argument-comparison heuristic
(with the correct transparency bump) instead of unfolding.

Key insight: all projection functions receive `.abbrev` kernel hints
(not `.regular`), regardless of their reducibility status. Structure
projections default to `.reducible` status, while class projections
default to `.semireducible` status. The old code only handled the
`.regular` case and treated everything else (including `.abbrev`) as
trivial.

Also fixes two minor comment issues in `tryHeuristic`: "non-trivial
regular definition" → "non-trivial definition" (since `.abbrev`
definitions can now be nontrivial too), and "when `f` is not simple" →
"when `f` is simple" (logic inversion in the original comment).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 21:20:33 +00:00
Sebastian Graf
4278038940
feat: new, extensible do elaborator (#12459)
This PR adds a new, extensible `do` elaborator. Users can opt into the
new elaborator by unsetting the option `backward.do.legacy`.

New elaborators for the builtin `doElem` syntax category can be
registered with attribute `doElem_elab`. For new syntax, additionally a
control info handler must be registered with attribute
`doElem_control_info` that specifies whether the new syntax `return`s
early, `break`s, `continue`s and which `mut` vars it reassigns.

Do elaborators have type ``TSyntax `doElem → DoElemCont → DoElabM
Expr``, where `DoElabM` is essentially `TermElabM` and the `DoElemCont`
represents how the rest of the `do` block is to be elaborated. Consult
the docstrings for more details.

Breaking Changes:
* The syntax for `let pat := rhs | otherwise` and similar now scope over
the `doSeq` that follows. Furthermore, `otherwise` and the sequence that
follows are now `doSeqIndented` in order not to steal syntax from record
syntax.
 
Breaking Changes when opting into the new `do` elaborator by unsetting
`backward.do.legacy`:
* `do` notation now always requires `Pure`.
* `do match` is now always non-dependent. There is `do match (dependent
:= true)` that expands to a
  term match as a workaround for some dependent uses.
2026-02-21 17:17:29 +00:00
Leonardo de Moura
e34c424459
fix: bump transparency in isDefEqProj for class projections (#12633)
This PR makes `isDefEqProj` bump transparency to `.instances` (via
`withInstanceConfig`) when comparing the struct arguments of class
projections. This makes the behavior consistent with `isDefEqArgs`,
which already applies the same bump for instance-implicit parameters
when comparing function applications.

When a class field like `X.x` is marked `@[reducible]`, `isDefEqDelta`
unfolds it to `.proj` form. Previously, `isDefEqProj` compared the
struct arguments at the ambient transparency (`.reducible` in simp),
which meant instance definitions (which are `[implicit_reducible]`)
could not be unfolded, causing `eq_self` to fail. In the function
application form (`X.x inst` vs `X.x inst'`), `isDefEqArgs` correctly
bumps to `.instances` for the instance-implicit parameter. The `.proj`
path should behave the same way.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 16:27:06 +00:00
Wojciech Różowski
5fb480e9f3
fix: revert #12615 which broke Leroy's compiler verification course benchmark (#12627)
This PR reverts #12615, which accidentally broke Leroy's compiler
verification course benchmark.
2026-02-20 17:54:52 +00:00
Leonardo de Moura
73751bbb27
fix: interaction between simp and backward.whnf.reducibleClassField (#12622)
This PR fixes a bug where `simp` made no progress on class projection
reductions when `backward.whnf.reducibleClassField` is `true`.

- In `reduceProjFn?`, for class projections applied to constructor
instances (`Class.projFn (Class.mk ...)`), the code called
`reduceProjCont? (← unfoldDefinitionAny? e)`. The helper
`reduceProjCont?` expects the unfolded result to have a `.proj` head so
it can apply `reduceProj?`. However, when `reducibleClassField` is
enabled, `unfoldDefault` in WHNF.lean already reduces the `.proj` node
during unfolding, so `reduceProjCont?` discards the fully-reduced
result.
- The fix uses `unfoldDefinitionAny?` directly, bypassing
`reduceProjCont?`. The dsimp traversal revisits the result (via
`.visit`) and handles any remaining `.proj` nodes naturally.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 16:37:11 +00:00
Wojciech Różowski
0520de7374
fix: respect @[cbv_opaque] in reduceRecMatcher and reduceProj (#12621)
This PR fixes a bug where `reduceRecMatcher?` and `reduceProj?` bypassed
the `@[cbv_opaque]` attribute. These kernel-level reduction functions
use `whnf` internally, which does not know about `@[cbv_opaque]`. This
meant `@[cbv_opaque]` values were unfolded when they appeared as match
discriminants, recursor major premises, or projection targets. The fix
introduces `withCbvOpaqueGuard`, which wraps these calls with
`withCanUnfoldPred` to prevent `whnf` from unfolding `@[cbv_opaque]`
definitions.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 16:13:26 +00:00
Wojciech Różowski
a3e1f82808
fix: cbv now unfolds nullary constant definitions (#12615)
This PR fixes a flipped condition in `handleConst` that prevented `cbv`
from unfolding nullary (non-function) constant definitions like
`def myVal : Nat := 42`. The check `unless eType matches .forallE` was
intended to skip bare function constants (whose unfold theorems expect
arguments) but instead skipped value constants. The fix changes the
guard to `if eType matches .forallE`, matching the logic used in the
standard `simp` ground evaluator.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 15:16:29 +00:00
Wojciech Różowski
14e1d4328f
fix: prevent cbv crash on dependent projections with @[cbv_eval] rewrites (#12612)
This PR fixes a crash in the `cbv` tactic's `handleProj` simproc when
processing a dependent projection (e.g. `Sigma.snd`) whose struct is
rewritten via `@[cbv_eval]` to a non-definitionally-equal term that
cannot be further reduced.

- Previously, `handleProj` returned `.rfl (done := false)`, causing the
`.proj` expression to flow into `simpStep` which throws "unexpected
kernel projection term"
- The fix marks the result as `done := true` so that `cbv` gracefully
gets stuck instead of crashing
- Adds regression tests for dependent projections on `Sigma`, custom
structures, and `Subtype`

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 14:38:15 +00:00
Henrik Böving
8cd4c44055
feat: derived value analysis for Array.uget (#12604)
This PR makes the derived value analysis in RC insertion recognize
`Array.uget` as another kind of
"projection-like" operation. This allows it to reduce reference count
pressure on elements accessed
through uget.
2026-02-20 08:51:07 +00:00
Leonardo de Moura
ab26eaf647
feat: enable implicit argument transparency bump (part 2) (#12572)
This PR is part 2 of the `implicit_reducible` refactoring (part 1:
#12567).

**Background.** When Lean checks definitional equality of function
applications
`f a₁ ... aₙ =?= f b₁ ... bₙ`, it compares arguments `aᵢ =?= bᵢ` at a
transparency level determined by the binder type. Previously, only
instance-implicit (`[C]`) arguments received a transparency bump to
`.instances`. With `backward.isDefEq.implicitBump` enabled, ALL implicit
arguments (`{x}`, `⦃x⦄`, and `[x]`) are bumped to `.instances`, so that
definitions marked `[implicit_reducible]` unfold when comparing implicit
arguments. This is important because implicit arguments often carry type
information (e.g., `P (i + 0)` vs `P i`) where the mismatch is in
non-proof positions (Sort arguments to `cast`) — proof irrelevance does
not
help here, so the relevant definitions must actually unfold.

**`[implicit_reducible]`** (renamed from `[instance_reducible]` in part
1) marks
definitions that should unfold at `TransparencyMode.instances` — between
`[reducible]` (unfolds at `.reducible` and above) and the default
`[semireducible]` (unfolds only at `.default` and above). This is the
right
level for core arithmetic operations that appear in type indices.

## Changes

- **Enable `backward.isDefEq.implicitBump` by default** and set it in
  `stage0/src/stdlib_flags.h` so stage0 also compiles with it
- **Mark `Nat.add`, `Nat.mul`, `Nat.sub`, `Array.size` as
`[implicit_reducible]`**
so they unfold when comparing implicit arguments at `.instances`
transparency
- **Remove redundant unification hints** (`n + 0 =?= n`, `n - 0 =?= n`,
  `n * 0 =?= 0`) that are now handled by `[implicit_reducible]`
- **Rename all remaining `[instance_reducible]` attribute usages** to
`[implicit_reducible]` across the codebase (the old name remains as an
alias)
- **Remove 28 `set_option backward.isDefEq.respectTransparency false
in`**
  workarounds that are no longer needed

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 03:28:48 +00:00
Wojciech Różowski
833434cd56
feat: add warning for cbv and decide_cbv tactics (#12601)
This PR adds a warning when using `cbv` or `decide_cbv` in tactic mode,
matching the existing warning in conv mode
(`src/Lean/Elab/Tactic/Conv/Cbv.lean`). The warning informs users that
these tactics are experimental and still under development. It can be
disabled with `set_option cbv.warning false`.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 22:47:01 +00:00
Henrik Böving
9f0b44b260
refactor: port RC insertion from IR to LCNF (#12548)
This PR ports the RC insertion from IR to LCNF.

In doing so it makes the entire code monadic as opposed to simulating a
ReaderT StateRefT stack manually.
2026-02-19 16:55:09 +00:00
Wojciech Różowski
d035efbb87
refactor: remove unnecessary simp call in simpAppFn and update cbv_eval attribute usage in tests (#12589)
This PR removes unnecessary `simp` call in `simpAppFn` in `cbv` tactic
and updates the usage of `cbv_eval` attribute in
`tests/lean.run/cbv1.lean` to follow the new syntax that does not
require an explicit name of the function for which we are registering
the unfold lemma.
2026-02-19 15:17:55 +00:00
David Thrane Christiansen
953b60c894
fix: rendering of hygiene info nodes in Verso docstring code blocks (#12594)
This PR fixes a bug with rendering of hygiene info nodes in embedded
Verso code examples. The embedded anonymous identifier was being
rendered as [anonymous] instead of being omitted.
2026-02-19 15:13:12 +00:00
Wojciech Różowski
2ce55ba460
fix: catch exceptions in cbv rewrite simprocs to handle projections (#12562)
This PR fixes #12554 where the `cbv` tactic throws "unexpected kernel
projection term during structural definitional equality" when a rewrite
theorem's pattern contains a lambda and the expression being matched has
a `.proj` (kernel projection) at the corresponding position.

The `Sym` pattern matching infrastructure (`isDefEqMain` in
`Pattern.lean`) does not handle `.proj` expressions and can throw an
exception. Rather than presenting it as an error in `cbv`, we fail
quietly and let the `cbv` tactic try other fallback paths.

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 11:10:30 +00:00
Kyle Miller
309f44d007
feat: more reliable universe level inference in inductive/structure commands (#12514)
This PR improves universe level inference for the `inductive` and
`structure` commands to be more reliable and to produce better error
messages. Recall that the main constraint for inductive types is that if
`u` is the universe level for the type and `u > 0`, then each
constructor field's universe level `v` satisfies `v ≤ u`, where a
*constructor field* is an argument that is not one of the type's
*parameters* (recall: the type's parameters are a prefix of the
parameters shared by the type former and all the constructors). Given
this constraint, the `inductive` elaborator attempts to find reasonable
assignments to metavariables that may be present:
- For the universe level `u`, choosing an assignment that makes this
level least is reasonable, provided it is unique.
- For constructor fields, choosing the unique assignment is usually
reasonable.
- For the type's parameters, promoting level metavariables to new
universe level parameters is reasonable.

The order of these steps led to somewhat convoluted error messages; for
example, metavariable->parameter promotion was done early, leading to
errors mentioning `u_1`, `u_2`, etc. instead of metavariables, as well
as extraneous level constraint errors. Furthermore, early parameter
promotion meant it was too late to perform certain kinds of inferences.

Now there is a straightforward order of inference:
1. If the type's universe level could be zero, it checks that the type
is an "obvious `Prop` candidate", which means it's non-recursive, has
one constructor with at least one field, and all the fields are proofs.
If it's a `Prop` candidate, the level is set to zero and we skip to step
4.
2. If the type's simplified universe level is of the form `?u + k`, it
will accumulate level constraints to find a least upper bound solution
for `?u`. To avoid sort polymorphism, it adds `1 ≤ ?u + k`, ensuring the
result stays in `Type _`, or at least `Sort (max 1 _)`. It allows other
metavariables to appear in the assignment for `?u`, provided they appear
in the type former, or for `structure` in the `extends` clause.
3. If the type's simplified universe level is then of the form `r + k`,
where `r` is a parameter, metavariable, or zero, then for every
constructor field it will take the `v ≤ r + k` constraint and extract
`?v ≤ r + k'` constraints. It will also *weakly* extract `1 ≤ ?v`
constraints, using the observation that it's surprising if fields are
automatically inferred to be proofs. Once the constraints are collected,
each metavariable is solved for independently. Heuristically, if there
is a unique non-constant solution we take that, or else a unique
constant solution.
4. Any remaining level metavariables in the type former (or `extends`
clause) become level parameters.
5. Remaining level metavariables in the constructor fields are reported
as errors.
6. Then, the elaborator checks that the level constraints actually hold
and reports an error if they don't.

In 2 and 3, there are procedures to simplify universe levels. You can
write `Sort (max 1 _)` for the resulting type now and it will solve for
`_`.

The "accidentally higher universe" error is now a warning. The
constraint solving is also done in a more careful way, which keeps it
from being reported erroneously. There are still some erroneous reports,
but these ones are hard for the checker to reject. As before, the
warning can be turned off by giving an explicit universe.

Note about `extends` clauses: in testing, there were examples where it
was surprising if the universe polymorphism of parent structures didn't
carry over to the type being defined, even though parent structures are
actually constructor fields.

**Breaking change.** Universe level metavariables present only in
constructor fields are no longer promoted to be universe level
parameters: use explicit universe level parameters. This promotion was
inconsistently done depending on whether the inductive type's universe
level had a metavariable, and also it caused confusion for users, since
these universe levels are not constrained by the type former's
parameters.

**Breaking change.** Now recursive types do not count as "obvious `Prop`
candidates". Use an explicit `Prop` type former annotation on recursive
inductive predicates.

Additional changes:
- level metavariable errors are now localized to constructors, and
`structure` fields have such errors localized to fields
- adds module docs for the index promotion algorithm and the universe
level inference algorithm for inductives
- factors out `Lean.Elab.Term.forEachExprWithExposedLevelMVars` for
printing out the context of an expression with universe level
metavariables
- makes universe level metavariable exposure more effective at exposing
level metavariables (with an exception of `sorry` terms, which are too
noisy to expose)

Supersedes #11513 and #11524.
2026-02-18 23:46:12 +00:00
Leonardo de Moura
b668a18a9d
refactor: rename instance_reducible to implicit_reducible (#12567)
This PR renames `instance_reducible` to `implicit_reducible` and adds a
new
`backward.isDefEq.implicitBump` option to prepare for treating all
implicit
arguments uniformly during definitional equality checking.

## Changes

**Rename `instance_reducible` → `implicit_reducible`:**
- Rename `ReducibilityStatus.instanceReducible` constructor to
`implicitReducible`
- Register new `[implicit_reducible]` attribute, keep
`[instance_reducible]` as alias
- Rename `isInstanceReducible` → `isImplicitReducible` (with deprecated
aliases)
- Update all references across src/ and tests/

The rename reflects that this reducibility level is used not just for
instances
but for any definition that needs unfolding during implicit argument
resolution
(e.g., `Nat.add`, `Array.size`).

**Add `backward.isDefEq.implicitBump` option:**
- When `true` (+ `respectTransparency`), bumps transparency to
`.instances` for
ALL implicit arguments in `isDefEqArgs`, not just instance-implicit ones
- Defaults to `false` for staging compatibility — will be flipped to
`true` after
  stage0 update
- Adds `// update me!` to `stage0/src/stdlib_flags.h` to trigger CI
stage0 update

## Follow-up (after stage0 update)
- Flip `backward.isDefEq.implicitBump` default to `true`
- Fix resulting test/module failures

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 22:19:16 +00:00
Joachim Breitner
7fa7ec1887
fix: instantiate mvars after synthesizing inductive type header (#12558)
This PR fixes a `(kernel) declaration has metavariables` error that
occurred when a `by` tactic was used in a dependent inductive type index
that refers to a previous index:

```lean
axiom P : Prop
axiom Q : P → Prop
-- Previously gave: (kernel) declaration has metavariables 'Foo'
inductive Foo : (h : P) → (Q (by exact h)) → Prop
```

The root cause: `elabDepArrow` calls `mkForallFVars [h_fvar] body`
before the `by` tactic's metavariable `?m` is resolved. Since `h_fvar`
is in `?m`'s local context, `elimMVarDeps` creates a delayed assignment
`?newMVar #[h_fvar] := ?m`. After `synthesizeSyntheticMVarsNoPostponing`
assigns `?m := h_fvar`, `instantiateMVars` can resolve the delayed
assignment (substituting `h_fvar` with the actual argument, `bvar 0`, in
the pending value), yielding the correct type `∀ (h : P), Q (bvar 0) →
Prop`. The fix is to call `instantiateMVars` on the header type right
after `synthesizeSyntheticMVarsNoPostponing` in `elabHeadersAux`.

Fixes #12543.

🤖 This PR was created with [Claude Code](https://claude.ai/claude-code).

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-18 15:29:33 +00:00
Paul Reichert
ac7c8e157e
feat: upstream List.scanl and List.scanr (#12452)
This PR upstreams `List.scanl`, `List.scanr` and their lemmas from
batteries into the standard library.
2026-02-18 12:15:18 +00:00
Sebastian Graf
6c671ffe6f
chore: add mvcgen regression test case (#12546) 2026-02-18 09:04:26 +00:00
Wojciech Różowski
4dd6a99fec
feat: improve decide_cbv error reporting (#12500)
This PR improves the error messages produced by the `decide_cbv` tactic
by only reducing the left-hand side of the equality introduced by
`of_decide_eq_true`, rather than attempting to reduce both sides via
`cbvGoal`.

Previously, `evalDecideCbv` called `cbvGoalCore` which would try to
reduce both sides of `decide P = true` and leave a remaining goal on
failure, resulting in a generic error showing the mvar ID. Now, a
dedicated `cbvDecideGoal` function in `Cbv/Main.lean`:

- closes the goal immediately when the LHS reduces to `Bool.true`
- reports a clear error when the LHS reduces to `Bool.false`, telling
the user the proposition is false
- reports a clear error with the stuck expression when reduction cannot
complete

Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-17 16:45:38 +00:00
Sebastian Graf
b491d868ed
chore: respect transparency in restoreM and Std.Do related proofs (#12529) 2026-02-17 16:04:15 +00:00
David Thrane Christiansen
c5b58092e3
fix: allow checked self-references in Verso docstring module role (#12523)
This PR allows the `module` role in Verso docstrings to refer to the
current module without requiring the `-checked` flag.
2026-02-17 14:02:23 +00:00
Wojciech Różowski
424fbbdf26
feat: add support to cbv_eval attribute (#12506)
This PR adds the ability to register theorems with the `cbv_eval`
attribute in the reverse direction using the `←` modifier, mirroring the
existing `simp` attribute behavior. When `@[cbv_eval ←]` is used, the
equation `lhs = rhs` is inverted to `rhs = lhs`, allowing `cbv` to
rewrite occurrences of `rhs` to `lhs`.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 12:23:17 +00:00
Kim Morrison
642bcdf55a
fix: handle universe polymorphism in ground grind theorems (#12226)
This PR fixes a bug where `grind [foo]` fails when the theorem `foo` has
a different universe variable name than the goal, even though universe
polymorphism should allow the universes to unify.

The issue was in `instantiateGroundTheorem` (used for theorems with no
quantified parameters), which was passing `thm.proof` directly instead
of calling `getProofWithFreshMVarLevels`. This meant ground theorems
retained their original universe level params instead of getting fresh
level metavariables that could unify with the goal's universe levels.

Fixes
https://leanprover.zulipchat.com/#narrow/channel/270676-lean4/topic/grind.20fails.20because.20of.20universe.20variable.20name

🤖 Prepared with Claude Code

Co-authored-by: Claude <noreply@anthropic.com>
2026-02-17 01:51:37 +00:00
Paul Reichert
af7b3866b2
feat: prove xs.extract start stop = (xs.take stop).drop start for lists (#12359)
This PR deprecates `extract_eq_drop_take` in favor of the more correct
name `extract_eq_take_drop`, so that we'll be able to use the old name
for a lemma `xs.extract start stop = (xs.take stop).drop start`. Until
the deprecation deadline has passed, this new lemma will be called
`extract_eq_drop_take'`.
2026-02-16 22:43:44 +00:00
Joachim Breitner
2f8c85af89
fix: ensure etaStruct is enabled during type inference (#12507)
This PR fixes #12495 where equational theorem generation fails for
structurally recursive definitions using a Box-like wrapper around
nested inductives.

## Root Cause

`withInferTypeConfig` (in `InferType.lean`) ensures various MetaM config
settings (`beta`, `iota`, `zeta`, `zetaHave`, `zetaDelta`, `proj`) are
enabled during type inference, but was missing `etaStruct`. When
`inferType` is called from a context where `etaStruct` is disabled —
such as inside `simpMatch` (which sets `etaStruct := .none` via
`SimpM.run` → `withSimpContext`) — `whnf` cannot eta-expand structure
values needed for recursor iota reduction.

Concretely, projecting from a type like `Rec.rec_2 ... base` (where
`base : Box Rec`) requires eta-expanding `base` to `Box.mk base.data` so
the `Box` recursor can reduce. With `etaStruct := .none`,
`toCtorWhenStructure` skips the eta-expansion, leaving `whnf` stuck and
`inferProjType` unable to recognize the resulting type as a structure.

## Fix

Add `etaStruct := .all` to the config settings ensured by
`withInferTypeConfig`, alongside the existing `beta`, `iota`, `zeta`,
`zetaHave`, `zetaDelta`, and `proj` settings. This also allows reverting
the workaround (`try/catch` around `simpMatch?`) that was added in the
first commit.

## Test plan

- [x] Existing test `tests/lean/run/issue12495.lean` passes
- [x] Full test suite (3561 tests) passes with 0 failures

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 20:27:57 +00:00
Leonardo de Moura
9a032cd261
feat: backward.isDefEq.respectTransparency (#12179)
This PR ensures `isDefEq` does not increase the transparency mode to
`.default` when checking whether implicit arguments are definitionally
equal. The previous behavior was creating scalability problems in
Mathlib. That said, this is a very disruptive change. The previous
behavior can be restored using the command
```
set_option backward.isDefEq.respectTransparency false
```
2026-02-16 15:57:21 +00:00
Henrik Böving
620ef3bb86
fix: bring lengthTR lemma back into scope at toArray (#12502)
This PR brings the `length = lengthTR` lemma back into scope after shake
mistakenly removed it.
2026-02-16 13:33:10 +00:00
Kim Morrison
5c7a508e21
fix: use target type's instances in delta deriving (#12339)
This PR fixes a diamond problem in delta deriving where
instance-implicit class parameters in the derived instance type were
using instances synthesized for the underlying type, not the alias type.

When deriving an instance for a type alias (e.g., `def ENat := WithTop
ℕ`), this caused a diamond when the alias has its own instance for a
dependency class (e.g., `AddMonoidWithOne` from `CommSemiring`) that
differs from the underlying type's instance (e.g.,
`WithTop.addMonoidWithOne`). Instance search would fail because it
expected the alias's instance but the derived instance used the
underlying's.

The fix: after synthesis succeeds, for each instance-implicit class
parameter, re-synthesize for the target type and use that instance if
it's defeq to what we synthesized for the underlying type.

### Example

```lean
class MyBase (α : Type) where value : Nat := 42
class MyHigher (α : Type) [MyBase α] : Prop where prop : True

instance instBaseNat : MyBase Nat := {}
def MyAlias := Nat
instance instBaseMyAlias : MyBase MyAlias := {}  -- Different expression, but defeq

instance instHigherNat : MyHigher Nat where prop := trivial
deriving instance MyHigher for MyAlias
```

**Before**: `instMyHigherMyAlias : @MyHigher MyAlias instBaseNat` →
instance search fails
**After**: `instMyHigherMyAlias : @MyHigher MyAlias instBaseMyAlias` →
instance search succeeds

### Motivation

This fixes the `CharZero ℕ∞` diamond in Mathlib under #12179 where the
derived instance was using `WithTop.addMonoidWithOne` instead of the
`AddMonoidWithOne` from `CommSemiring ℕ∞`.

🤖 Prepared with Claude Code

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-02-16 00:35:59 +00:00
Kim Morrison
bda15f6c25
chore: revert "feat: add higher-order Miller pattern support in e-matching (#12483)" (#12492)
This PR temporarily reverts #12483, which broke many proofs in Batteries
and Mathlib. We'll restore this once we have fixes in place.
2026-02-15 09:53:55 +00:00
Leonardo de Moura
0d2a511bde
fix: type class resolution cache (#12286)
This PR ensures the type resolution cache properly caches results for
type classe containing output parameters.

It ensures the cache key for a query like
```
HAppend.{0, 0, ?u} (BitVec 8) (BitVec 8) ?m
```
should be independent of the specific metavariable IDs in output
parameter positions. To achieve this, output parameter arguments are
erased from the cache key. Universe levels that only appear in output
parameter types (e.g., ?u corresponding to the result type's universe)
must also be erased to avoid cache misses when the same query is issued
with different universe metavariable IDs.

---------

Co-authored-by: Kim Morrison <kim@tqft.net>
2026-02-15 03:07:15 +00:00
Leonardo de Moura
97a7fed6c5
chore: add test for issue #11738 (#12485)
Closes #11738
2026-02-15 00:48:34 +00:00
Leonardo de Moura
0721313dcc
chore: add test for issue #11930 (#12484)
Issue has already been fixed

Closes #11930
2026-02-15 00:41:28 +00:00
Leonardo de Moura
022bf2f822
feat: add higher-order Miller pattern support in e-matching (#12483)
This PR adds support for higher-order Miller patterns in `grind`'s
e-matching engine.

Previously, lambda arguments in e-matching patterns were always treated
as `dontCare`, meaning
they could not contribute to matching or bind pattern variables. This
was a significant limitation
for theorems where lambda arguments carry essential structure, such as
`List.foldl`, `List.foldrM`,
or any combinator that takes a function argument.

With this change, when a pattern argument is a lambda whose body
satisfies the **Miller pattern
condition** — i.e., pattern variables are applied only to distinct
lambda-bound variables — the
lambda is preserved as an `ho[...]` pattern. At instantiation time,
these higher-order patterns
are matched via `isDefEq` after all first-order pattern variables have
been assigned by the E-graph.

### Example

```lean
@[grind =] theorem applyFlip_spec (f : Nat → Nat → Nat) (a b : Nat)
    : applyFlip (fun x y => f y x) a b = f b a := sorry
```

The pattern `applyFlip ho[fun x => fun y => #2 y x] #1 #0` captures the
lambda argument
structurally: `#2` (the pattern variable for `f`) is applied to distinct
lambda-bound
variables `y` and `x`. When `grind` encounters `applyFlip (fun x y =>
Nat.add y x) 3 4`,
it binds `f := Nat.add` via `isDefEq` and fires the rewrite.

### Key design decisions

- **Miller condition check**: Only lambdas where at least one pattern
variable appears
in applied Miller position (applied to distinct lambda-bound vars) are
promoted to
  `ho[...]`. Other lambdas remain `dontCare`.
- **Redundancy elimination**: A post-processing pass demotes `ho[...]`
patterns to `dontCare`
if all their pattern variables already appear in non-HO positions of the
same pattern. This
avoids unnecessary `isDefEq` calls when the lambda doesn't contribute
new variable bindings.
- **E-graph bypass**: HO patterns are not internalized into the E-graph.
They are accumulated
during matching and checked via `isDefEq` after the first-order
assignment is complete.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 00:28:03 +00:00
Leonardo de Moura
57a2dc0146
fix: handle heterogeneous equality in closeGoalWithValuesEq (#12477)
This PR fixes an internal `grind` error where `mkEqProof` is invoked
with terms of different types. When equivalence classes contain
heterogeneous equalities (e.g., `0 : Fin 3` and `0 : Fin 2` merged via
`HEq`), `closeGoalWithValuesEq` would call `mkEqProof` on terms with
incompatible types, triggering an internal error.

Closes #12140

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 07:12:01 +00:00
Leonardo de Moura
cce6ce9577
fix: treat outParam arguments as support in e-matching patterns (#12476)
This PR fixes #12245 where `grind` works on `Fin n` but fails on `Fin (n
+ 1)`.

The `outParam` argument (e.g., the `range` parameter of `ToInt`) was
included as a relevant position in the e-matching pattern. The `grind`
normalizer rewrites `↑(n + 1)` to `↑n + 1` inside the range expression,
causing the pattern to no longer match. Since `outParam` arguments are
uniquely determined by type class resolution, they can be safely
wildcarded in patterns — the same reasoning that already applies to
instance-implicit arguments.

Reproducer from the issue:
```lean
example {n : Nat} {a : Fin (n + 1)} {b : Nat} (hb : b < n + 1)
    (h : (a : Nat) < b) : a < ⟨b, hb⟩ := by grind -- fails without fix
```

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 05:05:56 +00:00
Leonardo de Moura
103ed5b54b
fix: handle delayed mvar assignments in grind when hypotheses contain mvars (#12475)
This PR fixes `grind` failing when hypotheses contain metavariables
(e.g., after `refine`). The root cause was that `abstractMVars` in
`withProtectedMCtx` only abstracted metavariables in the target, not in
hypotheses, creating a disconnect in grind's e-graph.

The fix removes `abstractMVars` and instead resolves delayed
metavariable assignments before exiting `withNewMCtxDepth`.
`instantiateMVars` refuses to resolve a delayed assignment when the
pending assignment is non-ground (contains unassigned expression
metavariables). This function converts such delayed assignments to
regular ones using `LocalContext.mkLambda`, allowing `instantiateMVars`
to resolve them via beta reduction. The mvar internalization warning is
also removed since grind now handles mvars.

Closes #12242

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 04:28:06 +00:00