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>
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.
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>
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>
This PR fixes an issue where commands that do not support incrementality
did not have their elaboration interrupted when a relevant edit is made
by the user. As all built-in variants of def/theorem share a common
incremental elaborator, this likely had negligible impact on standard
Lean files but could affect other use cases heavily relying on custom
commands such as Verso.
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>
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>
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>
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'`.
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>
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
```
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>
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>
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>
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>
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>
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>
This PR fixes a panic in `grind` where `sreifyCore?` could encounter
power subterms not yet internalized in the E-graph during nested
propagation. The ring reifier (`reifyCore?`) already had a defensive
`alreadyInternalized` check before creating variables, but the semiring
reifier (`sreifyCore?`) was missing this guard. When `propagatePower`
decomposed `a ^ (b₁ + b₂)` into `a^b₁ * a^b₂` and the resulting terms
triggered further propagation, the semiring reifier could be called on
subterms not yet in the E-graph, causing `markTerm` to fail.
Closes#12428🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This PR fixes an assertion violation in `grind` reported at #12246 This
assertion fails when in examples containing heterogenous equalities with
elements of different types (e.g., `Fin n` and `Fin m`) attached to the
same theory solver.
Closes#12246
This PR fixes a bug where `lia` was incorrectly solving goals involving
ordered types like `Rat` that it shouldn't handle. The `lia` tactic is
intended for linear integer arithmetic only.
The fix adds `order := false` and `funCC := false` to `NoopConfig`,
which is the base configuration for `CutsatConfig` (used by `lia`).
Closes
https://leanprover.zulipchat.com/#narrow/channel/113488-general/topic/releases.20of.20new.20Lean.20versions/near/564688881🤖 Prepared with Claude Code
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This PR fixes an `AppBuilder` exception in the `cbv` tactic when
simplifying projections whose projection function is dependent (closes
#12457).
Previously, `handleProj` unconditionally used `mkCongrArg` to prove `e.i
= e'.i` from `e = e'`, but `mkCongrArg` requires a non-dependent
function. For dependent projections (e.g., `fun x => x.2 : (x :
String.Slice) → x.1.Pos`), this would fail.
Now, `handleProj` first checks whether the projection function type is
non-dependent (a simple arrow). If so, it proceeds with `mkCongrArg` as
before. Otherwise, it falls back to:
1. Attempting to reduce the projection directly.
2. If reduction fails, using a heterogeneous congruence lemma
(`mkHCongr`) converted to an equality via `mkEqOfHEq`, provided the
original and rewritten struct are definitionally equal.
This PR verifies all of the `String` iterators except for the bytes
iterator by relating them to `String.toList`.
Along the way we define `String.posLE` and `String.posLT` analogously to
`String.posGE` and `String.posGT` and redefine `String.prev` to go
through `String.posLT`.
We also define and verify `String.positionsFrom` and
`String.revPositionsFrom`, which are the obvious generaliziations of
`String.positions` and `String.revPositions` starting at a positions
other than the start/end.
Finally, we get various lemmas about strings and positions, including
some nice induction principles `String.Pos.next_induction` and
`String.Pos.prev_induction`.
Of course, we also have all of the analogous results for `String.Slice`.
This PR adds a simplification rule for `Task.get (Task.pure x) = x` into
the LCNF simplifier. This
ensures that we avoid touching the runtime for a `Task` that instantly
gets destructed anyways.
This PR adds the attribute `@[univ_out_params]` for specifying which
universe levels should be treated as output parameters. By default, any
universe level that does not occur in any input parameter is considered
an output parameter.
This PR sets the `irreducible` attribute before generating the equations
for recursive definitions. This prevents these equations to be marked as
`defeq`, which could lead to `simp` generation proofs that do not type
check at default transparency.
This issue is surfacing more easily since well-founded recursion on
`Nat` is implemented with a dedicated fix point operator (#7965). Before
that, `WellFounded.fix` was used, which is inherently not reducing, so
we did get the desired result even without the explicit reducibility
setting.
Fixes#12398.
This PR adds a finishing `decide_cbv` tactic, which applies
`of_decide_eq_true` and then tries to discharge the remaining goal using
`cbv`.
Stacked on top of #12408.
---------
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
This PR adds caching to `replaceRecApps`, the procedure responsible for
replacing recursive applications for wellfounded recursion, improving
performance when many references to the same recursive call exist, e.g.
when recursive calls exist in proof terms.
Closes#12404
---------
Co-authored-by: Joachim Breitner <mail@joachim-breitner.de>
This PR adds a user facing `cbv` tactic that can be used outside of the
`conv` mode.
Example usage:
```lean4
example : "hello" ++ " " ++ "world" = "hello world" := by cbv
```
---------
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
This PR gives a proof of `LawfulToForwardSearcherModel` for `Slice`
patterns, which amounts to proving that our implementation of KMP is
correct.
Note that this PR also changes the KMP implementation to make it
slightly more efficient and easier to verify. I also have a correctness
proof for the old implementation, so there were no bugs in the old
implementation.
This PR fixes a bug in `mvcgen` caused by incomplete `match` splitting.
In particular, if a program `match s with ...` matches on a state
variable `s` (presumably the result of a call to `get`), then `s` will
also occur in the stateful goal `H ⊢ₛ wp⟦match s with ...⟧ Q s`
*outside* the program expression; this was not anticipated before.
This PR adds `mvcgen` support for specifications in the local context.
Example:
```lean
import Std.Tactic.Do
open Std.Do
set_option mvcgen.warning false
def foo (x : Id Nat → Id Nat) : Id Nat := do
let r₁ ← x (pure 42)
let r₂ ← x (pure 26)
pure (r₁ + r₂)
theorem foo_spec
(x : Id Nat → Id Nat)
(x_spec : ∀ (k : Id Nat) (_ : ⦃⌜True⌝⦄ k ⦃⇓r => ⌜r % 2 = 0⌝⦄), ⦃⌜True⌝⦄ x k ⦃⇓r => ⌜r % 2 = 0⌝⦄) :
⦃⌜True⌝⦄ foo x ⦃⇓r => ⌜r % 2 = 0⌝⦄ := by
mvcgen [foo, x_spec] <;> grind
def bar (k : Id Nat) : Id Nat := do
let r ← k
if r > 30 then return 12 else return r
example : ⦃⌜True⌝⦄ foo bar ⦃⇓r => ⌜r % 2 = 0⌝⦄ := by
mvcgen [foo_spec, bar] -- unfold `bar` and automatically apply the spec for the higher-order argument `k`
```
This PR improves the slice API with lemmas for `drop`/`take` operations
on `Subarray` and more lemmas about `Std.Slice.fold`, `Std.Slice.foldM`
and `Std.Slice.forIn`. It also changes the `simp` and `grind`
annotations for `Slice`-related lemmas. Lemmas converting between slices
of different shapes are no longer `simp`/`grind`-annotated because they
often complicated lemmas and hindered automation.
This PR adds a custom simproc to handle `Decidable.rec`, where we force
the rewrite in the argument of the `Decidable` type, that normally is
not rewritten due to being a subsingleton.
Closes#12386