This PR updates the `inferInstanceAs` docstring to reflect current behavior: it now
requires an expected type from context and should not be used as a simple `inferInstance`
synonym. The broken `#check inferInstanceAs (Inhabited Nat)` example is replaced with a
working one that demonstrates the intended transport use case.
Additionally, rename `InstanceNormalForm.lean` to `WrapInstance.lean`, `normalizeInstance`
to `wrapInstance`, and the trace class `Meta.instanceNormalForm` to `Meta.wrapInstance`,
removing the "instance normal form" terminology from both documentation and code.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This PR fixes the sym mvcgen benchmark script and tunes input sizes.
**run_bench.sh**: Replace `| tee` with the `capture` helper from
`util.sh`.
Without `pipefail`, piping through `tee` masks non-zero exit codes from
`lake build`, so build failures (OOM, stack overflow) go unnoticed.
**Benchmark sizes**: Scale down inputs for benchmarks that exceeded the
2s
budget so each benchmark completes in 1-2s across its 3 linearly
increasing
inputs.
**Metric collision**: Copy `GetThrowSet.Goal` into a `GetThrowSetGrind`
namespace so the grind variant reports as `GetThrowSetGrind(n)` instead
of
colliding with `GetThrowSet(n)` in `measurements.jsonl`.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This PR fixes a race condition in `Cache.saveArtifact` that caused
intermittent "permission denied" errors when two library facets (e.g.,
`static` and `static.export`) produce artifacts with the same content
hash and attempt to cache them concurrently.
The race occurs because `saveArtifact` checks `cacheFile.pathExists`,
then writes the file and makes it read-only. When two tasks race past
the existence check, the second task's write fails because the first
task already created the file and set it to read-only. On Linux, this is
common for `static` vs `static.export` since both resolve to the same
`coExport` object files, producing byte-identical archives.
The fix introduces `writeFileIfNew` and `writeBinFileIfNew` helpers that
use `O_CREAT | O_EXCL` (via `IO.FS.Mode.writeNew`) to atomically
create-or-skip, eliminating the race window. For the binary path, hard
link `alreadyExists` errors are also handled explicitly to avoid an
unnecessary copy fallback.
Additionally, `IO.setAccessRights` for the cache file is moved outside
the `unless pathExists` block so that permissions are always enforced,
and the `getMTime` call no longer silently swallows errors.
🤖 Prepared with Claude Code
This PR verifies `String.Pos.nextn` by providing the low-level API
`nextn_zero`/`nextn_add_one` as well as a `Splits` lemma.
The `Splits` lemma trivially implies, for a string `s`, the statement
`(s.drop n).copy.toList = s.toList.drop n`, to be included in a later
PR.
This PR marks the `Inhabited` arguments of all functions in core marked
as `extern` as borrowed
(panicking array accessors and `panic!` itself). This in turn causes a
transitive effect throughout
the codebase and promotes most, if not all, `Inhabited` arguments to
functions to borrowed.
This PR adds iota reduction to the sym-based `mvcgen'` tactic by calling
`reduceRecMatcher?` before falling back to the match split backward
rule.
When a matcher/recursor has a concrete discriminant, it is reduced
directly
instead of constructing and applying a splitting backward rule, which is
significantly faster for benchmarks like `MatchIota` (previously
`MatchSplit`)
where `loop n` unrolls into `n` nested matches with known `Nat`
discriminants.
The old `MatchSplit` test case (concrete discriminants) is renamed to
`MatchIota`
and a new `MatchSplit` test case with symbolic discriminants (matching
on state)
is added to keep exercising the split backward rule code path.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This PR extends the sym-based `mvcgen'` tactic with two new modes:
1. `mvcgen' with <tac>`: run VCGen, then apply `<tac>` to each remaining
VC.
2. `mvcgen' with grind`: integrate grind into the VCGen loop for
incremental context internalization. Each VC inherits the parent's
E-graph state, so hypothesis processing is shared across sibling VCs,
avoiding O(n) re-internalization per VC.
The grind mode accepts the full grind configuration syntax (`mvcgen'
with grind (config := { ... }) [params]`).
A persistent `Sym.Simp` cache with a `reassocNatAdd` simproc normalizes
hypothesis types (e.g., `s + 1 + 1 + 1` → `s + 3`) before grind
internalization, achieving O(1) amortized simplification per VC.
Benchmark results for GetThrowSet (`mvcgen' with grind`):
- n=100: 400ms total, 180ms kernel
- n=250: 855ms total, 1.8s kernel
- n=500: 1.9s total, 11.8s kernel
Kernel checking time grows superlinearly and is the dominant cost at
larger sizes. This is a separate issue from VCGen performance.
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This PR fixes an issue where `Std.Iter.joinString` had an extra universe
parameter because of an `IteratorLoop` instance which was actually
unnecessary.
This PR adds `SymExtension`, a typed extensible state mechanism for
`SymM`,
following the same pattern as `Grind.SolverExtension`. Extensions are
registered at initialization time via `registerSymExtension` and provide
typed `getState`/`modifyState` accessors. Extension state persists
across
`simp` invocations within a `sym =>` block and is re-initialized on each
`SymM.run`.
This enables modules (e.g., the upcoming arithmetic normalizer) to
register persistent state without modifying `Sym.State` directly.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This PR alters the `Linux Lake` CI job to enable the Lake cache and
upload the builds results to the remote cache storage. It also adds a
`Linux Lake (Cached)` secondary build job which fetches a build from the
Lake remote cache (if possible) and tests it.
---------
Co-authored-by: Sebastian Ullrich <sebasti@nullri.ch>
This PR switches `normalizeInstance` from using `isMetaSection` to the
existing `declName?` pattern (already used by `unsafe` in
`BuiltinNotation.lean` and `private_decl%` in `BuiltinTerm.lean`) for
determining whether aux defs should be marked `meta`.
#13043 used `isMetaSection` to determine whether `normalizeInstance` aux
defs should be marked `meta`. This caused `deriving` in meta sections to
fail: the deriving handler doesn't mark the instance itself as meta, so
the non-meta instance couldn't access its meta-marked aux defs:
```
Invalid definition `instInhabitedLibraryNote`, may not access declaration
`instInhabitedLibraryNote._aux_1` marked as `meta`
```
The `declName?` pattern inherits meta status from the parent declaration
rather than the scope. This correctly handles both cases:
- **`inferInstanceAs`**: parent declaration is marked meta by
`processHeaders`, so `declName?.any (isMarkedMeta env)` is true and aux
defs are correctly marked meta
- **`deriving`**: `declName?` is `none` (the deriving handler runs
outside `withDeclName`), so `isMeta` is `false` and aux defs are not
marked meta — matching the instance itself, which the deriving handler
also does not mark meta
Found while adapting Batteries to nightly-2026-03-23.
🤖 Prepared with Claude Code
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This PR changes the behavior of forward and backward projection
propagation in the context of user defined borrows. The reason to have
them be "forced" override (i.e. override user annotations as well) was
that a user annotated borrowed value can potentially flow into a
reset-reuse transitively through a projection and must thus have
accurate reference count. The reasons that this is no longer necessary
are:
1. Forward never had to be forced anyways, it can only affect the `z` in
`let z := oproj x i` which can't be annotated by a user
2. Backward is no longer necessary as the forward propagator for user
annotations prevents the reset-reuse insertion from working with values
that have user defined borrow annotations entirely.
This PR informs the borrow inference that if an `Array` is borrowed and
we index into it, the value we obtain is effectively a borrowed value as
well. This helps improve the ABI of operations that recurse on linked
structures containing arrays such as tries or persistent hash maps.
This PR adds a check that rejects Lake configurations where multiple
executables share the same root module name. Previously, Lake would
silently compile the root module once and link it into all executables,
producing identical binaries regardless of differing `srcDir` settings.
Lake (and Lean) rely on module names being unique within a package.
Rather than attempting to support duplicate module names, Lake now
produces a clear error at configuration load time, for both TOML and
Lean configuration files.
Closes#13013🤖 Prepared with Claude Code
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This PR rewrites the docstring on `Lean.ReducibilityHints` to accurately
describe the
kernel's lazy delta reduction strategy: which side gets unfolded when
comparing two
definitions, how definitional height is computed, and how hints relate
to the
`@[reducible]`/`@[irreducible]` elaborator attributes.
The old docstring referenced a `selfOpt` flag that no longer exists and
contained a few
inaccuracies (e.g. `irrelevance` instead of `irreducible`).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This PR marks the context argument of `ReaderT` as borrowed, causing a
wide spread of useful borrow annotations throughout the entire meta
stack which reduces RC pressure. This introduces a crucial new behavior:
When modifying `ReaderT` context, e.g. through `withReader` this will
almost always cause an allocation. Given that the `ReaderT` context is
frequently used in a non-linear fashion anyways we think this is an
acceptable behavior.
This PR demotes the cmake error to a warning because it tends to get
triggered by a combination of add_dir_of_test_dirs and git checkout not
removing untracked files.
This PR makes theorems opaque in almost all ways, including in the
kernel.
Already now, because of proof irrelevance, theorems are almost never
unfolded. Furthermore, the import handling allows conflicting theorem
declaration with same type and different values. This is sound, but
would be confusing if the value, and thus the import order, matters for
completeness.
So with this change, a `theorem` becomes more like an `opaque`: It has a
value (for soundness), but it is never unfolded during reduction or type
checking. There are still some places in meta code that have to peek
into theorems (e.g. `FunInd`, wfrec processing), but these are code
transformations, not reduction.
One place where reducing proofs is necessary is reducing `Acc.rec`
eliminating into Type. With this change, all proofs that need to be
reducable that way have to be `def`, not `theorem`. This is already the
case due to the module system. This does not affect uses of `Acc` via
well-founded recursion, because that has already been made opaque in
#5182. This moves the reduction behavior of `Acc.rec` further into the
“supported by the theory but not relied upon by regular Lean“ corner.
Fixes#12804
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This PR extracts the functional (lambda) passed to `brecOn` in
structural
recursion into a named `_f` helper definition (e.g. `foo._f`), similar
to
how well-founded recursion uses `._unary`. This way the functional shows
up
with a helpful name in kernel diagnostics rather than as an anonymous
lambda.
The `_f` definition is added with `.abbrev` kernel reducibility hints
and
the `@[reducible]` elaborator attribute, so the kernel unfolds it
eagerly
after `brecOn` iota-reduces. For inductive predicates, the previous
inline
lambda behavior is kept.
To ensure that parent definitions still get the correct reducibility
height
(since `getMaxHeight` ignores `.abbrev` definitions), each `_f`'s body
height is registered via a new `defHeightOverrideExt` environment
extension.
`getMaxHeight` checks this extension for all definitions, making the
height
computation transparent to the extraction.
This change improves code size (a bit). It may regress kernel reduction
times,
especially if a function defined by structural recursion is used in
kernel reduction
proofs on the hot path. Functions defined by structural recursion are
not particularly
fast to reduce anyways (due to the `.brecOn` construction), so already
now it may be
worth writing a kernel-reduction-friendly function manually (using the
recursor directly,
avoiding overloaded operations). This change will guide you in knowing
which function to
optimize.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This PR adds lemmas about `BEq` on `List String.Slice`.
We show `(l == l') = false ↔ l.map copy ≠ l'.map copy` and deduce a
`BEq` version of the theorem about "intercalate-then-split":
```lean
theorem toList_split_intercalate_beq {c : Char} {l : List String} (hl : ∀ s ∈ l, c ∉ s.toList) :
((String.intercalate (String.singleton c) l).split c).toList ==
if l = [] then ["".toSlice] else l.map String.toSlice
```
This PR adds `EquivBEq` and `LawfulHashable` instances to
`String.Slice`.
To this end, we redefine `String.Slice.hash`, which used to be
completely opaque, to be defined as `String.hash s.copy` (and then
`String.hash` remains opaque). We add tests that the `lean_slice_hash`
and `lean_string_hash` functions do indeed satisfy this relationship.
Of course, it would be even better to have a streaming MurmurHash64A
implementation in core that could be used to implement both of these so
that we can avoid the `opaque`, but that is a project for another day.
This PR fixes a bug in the borrow inference in connection with `export`
annotations.
Previously parameters of `export` functions were presumed as owned from
the beginning of the
analysis. However, they were not added into the set of owned parameters
and thus sometimes failed to
force necessary changes to borrowedness of other values that the
parameters flowed into.
This PR adds the functions `Std.Iter.joinString` and
`Std.Iter.intercalateString`.
`it.intercalateString s` is a more efficient version of
`s.copy.intercalate (it.toList.map toString)`, and we have a lemmas
proving exactly that.
This PR adds the simproc String.reduceToSingleton`, which is disabled by
default and turns `"c"` into `String.singleton 'c'`.
Recall that the simproc `reduceSingleton`, which does the reverse, is
part of the default `simp` set.
This PR adds a CI check that fails when a PR introduces no changes
compared to its base branch. This catches cases where a duplicate PR is
queued for merge after an identical PR has already landed (as happened
with https://github.com/leanprover/lean4/pull/12876 and
https://github.com/leanprover/lean4/pull/12877).
The check is added as a second job in the existing `check-stage0.yml`
workflow, which already has the same trigger conditions and git setup
pattern. On `pull_request` events it diffs against the merge base; on
`merge_group` events it diffs `HEAD^1..HEAD` (the PR's contribution to
the synthetic merge commit). Note that batched merge groups are treated
as a unit — if the entire group is non-empty the check passes, which is
the right behaviour for lean4's typical single-PR queuing.
🤖 Prepared with Claude Code
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This PR fixes a crash in release_checklist.py when a repository uses the
`leanprover/lean4-nightly:` toolchain prefix (e.g. leansqlite). The
`is_version_gte` function only checked for `leanprover/lean4:nightly-`
but
not `leanprover/lean4-nightly:`, causing a `ValueError: invalid literal
for
int() with base 10: 'nightly'` when trying to parse the version.
🤖 Prepared with Claude Code
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This PR prevents `Sym.simp` from looping on permutation theorems like
`∀ x y, x + y = y + x`.
- Add `perm : Bool` field to `Theorem`
- Add `isPerm` that checks if LHS and RHS have the same structure with
pattern variables (de Bruijn indices) rearranged via a consistent
bijection. Uses `ReaderT` (offset for binder entry), `StateT`
(forward/backward maps), `ExceptT` (failure).
- Compute `perm` in `mkTheoremFromDecl` / `mkTheoremFromExpr`
- In `Theorem.rewrite`, when `perm` is true, only apply the rewrite if
the result is strictly less than the input (using `acLt`)
- Tests include the classic AC normalization stress test with
`add_comm`, `add_assoc`, `add_left_comm`
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This PR fixes a bug where `inferInstanceAs` and the default `deriving`
handler, when used inside a `meta section`, would create auxiliary
definitions (via `normalizeInstance`) that were not marked as `meta`.
This caused the compiler to reject the parent `meta` definition with:
```
Invalid `meta` definition `instEmptyCollectionNamePrefixRel`, `instEmptyCollectionNamePrefixRel._aux_1` not marked `meta`
```
The fix adds an `isMeta` parameter to `normalizeInstance` that is
propagated from the elaboration context (`isMarkedMeta` for
`inferInstanceAs`, `Scope.isMeta` for the deriving handler), and marks
each auxiliary definition created by `mkAuxDefinition` as `meta` when
appropriate.
Found while adapting Mathlib to
https://github.com/leanprover/lean4/pull/12897.
🤖 Prepared with Claude Code
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This PR extends the `simp` tactic in `sym =>` mode to support local
hypotheses in the extra theorem list.
`simp myVariant [h]` now resolves `h` against the local context first,
falling back to global constants. Local hypotheses are converted to
rewrite rules via `mkTheoremFromExpr`, which applies the `eq_true`/
`eq_false`/`propext` adapter from #13041.
- Add `ExtraTheorem` inductive (`.const` / `.fvar`) for cache keying
- Add `resolveExtraTheorems` that checks the local context before
globals
- Update `addExtraTheorems`, `mkDefaultMethods`, `elabVariant`
signatures
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This PR extends `mkTheoremFromDecl` and `mkTheoremFromExpr` to handle
theorems whose conclusion is not an equality, enabling `Sym.simp` to use
a broader class of lemmas as rewrite rules.
Adaptations:
- `¬ p` → `p = False` via `eq_false`
- `p ↔ q` → `p = q` via `propext`
- `p` (proposition) → `p = True` via `eq_true`
Conjunctions (`p ∧ q`) are not handled here since the `SymM` E-graph
aggressively splits them via case analysis.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This PR adds validation to the `register_sym_simp` command:
- Reject duplicate variant names
- Validate `pre`/`post` syntax by elaborating them via `elabSymSimproc`
in a minimal `GrindTacticM` context, catching unknown theorem names
and unknown theorem set references at registration time
Adds `withGrindTacticM` helper for running `GrindTacticM` from
`CommandElabM`,
and `validateOptionSimprocSyntax` for optional syntax validation.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This PR adds the `simp` tactic to the `sym =>` interactive mode,
completing
the `Sym.simp` interactive infrastructure.
The `simp` tactic supports:
- `simp` — default variant with `@[sym_simp]` theorem set, `simpControl
>> simpArrowTelescope` pre, `evalGround >> thms.rewrite` post
- `simp myVariant` — named variant registered via `register_sym_simp`
- `simp [thm₁, thm₂]` — default variant with extra rewrite theorems
appended to `post`
- `simp myVariant [thm₁, thm₂]` — named variant with extra theorems
Per-variant persistent caching: each unique (variant name, extra theorem
list)
combination gets its own `Sym.Simp.State` cache, shared across
invocations
within a `sym =>` block. Test 10 verifies cache hits using
`trace.sym.simp.debug.cache`.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This PR removes the unused `change ... with` tactic syntax.
It's been asked about a number of times recently, but it was never
implemented. In PR #6018 we decided it was a Lean-3-ism.
This PR adjusts the results of `inferInstanceAs` and the `def` `deriving` handler to conform to recently strengthened restrictions on reducibility. This change ensures that when deriving or inferring an instance for a semireducible type definition, the definition's RHS is not leaked when the instance is reduced at lower than semireducible transparency.
More specifically, given the "source type" and "target type" (the given and expected type for `inferInstanceAs`, the right-hand side and applied left-hand side of the `def` for `deriving`), we synthesize an instance for the source type and then unfold and rewrap its components (fields, nested instances) as necessary to make them compatible with the target type. The individual steps are represented by the following options, which all default to enabled and can be disabled to help with porting:
- `backward.inferInstanceAs.wrap`: master switch for instance adjustment in both `inferInstanceAs` and the default `deriving` handler
- `backward.inferInstanceAs.wrap.reuseSubInstances`: reuse existing instances for the target type for sub-instance fields to avoid non-defeq instance diamonds
- `backward.inferInstanceAs.wrap.instances`: wrap non-reducible instances in auxiliary definitions
- `backward.inferInstanceAs.wrap.data`: wrap data fields in auxiliary definitions (proof fields are always wrapped)
This PR is an extension and rewrite of prior work in Mathlib: https://github.com/leanprover-community/mathlib4/pull/36420
Last(?) part of fix for #9077🤖 Prepared with Claude Code
# Breaking changes
Proofs that relied on the prior "defeq abuse" of these instance or that depended on their specific structure may need adjustments. As `inferInstanceAs A` now needs to know the source and target types exactly before it can continue, it cannot be used anymore as a synonym for `(inferInstance : A)`, use the latter instead when source and target type are identical.
This PR fixes the `workflow_dispatch` path for nightly releases.
Previously,
when a scheduled nightly failed (so no tag was created) and someone
manually
re-triggered the workflow, it would find the most recent existing
nightly tag
(from a previous day) and create a `-revK` revision of that old tag. Now
it
checks if today's nightly tag exists: if not, it creates it directly; if
it
already exists, it creates a `-revK` revision as before.
🤖 Prepared with Claude Code
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This PR adds the `register_sym_simp` command for declaring named
`Sym.simp`
variants with `pre`/`post` simproc chains and optional config overrides.
```
register_sym_simp myVariant where
pre := telescope
post := ground >> rewrite [thm1, thm2] with self
maxSteps := 50000
```
- `SymSimpVariant` structure storing `pre?`/`post?` syntax (elaborated
at use
time in `GrindTacticM`) and `Config` overrides
- `SimpleScopedEnvExtension` for persistent cross-module variant storage
- `register_sym_simp` command syntax with `sym_simp_field` category
- Command elaborator with syntax quotation matching and duplicate field
detection
- `getSymSimpVariant?` lookup function
- `deriving Inhabited` on `Simp.Config` for extension support
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This PR adds `r == e` guards to the `norm_eq_var` and
`norm_eq_var_const` branches of `Int.Linear.simpEq?`. Without these
guards, `simpEq?` returns a non-trivial proof for already-normalized
equations like `x = -1`, causing `exists_prop_congr` to fire repeatedly
and build an infinitely growing term.
The existing `Nat.simpCnstrPos?` already had the equivalent guard (`if r
!= lhs then ... else return none`).
Closes#12812
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This PR fixes#12842 where `grind` exhausts memory on goals involving
high-degree polynomials such as `(x + y)^2 = x^128 + y^2` over `Fin 2`.
The root cause is that `incSteps` in the ring module's Groebner basis
engine increments the step counter by 1 per simplification, regardless
of polynomial size. For high-degree polynomials (e.g., degree 128),
intermediate results can have hundreds of terms, making each operation
extremely expensive — but the flat counter cannot catch this before
memory is exhausted.
The fix weights each step by `Poly.numTerms` of the result polynomial
and increases the default `ringSteps` from 10 000 to 100 000 to
accommodate the new cost model.
Note: the example from #12842 will not be *proved* by `grind` even after
this fix, because Frobenius / Fermat's little theorem (`x^p = x` in `Fin
p`) is not available in core Lean. The long-term plan is to introduce a
type class in core stating this property, with an instance provided by
Mathlib, so that `grind` can exploit it when Mathlib is imported.
Closes #12842🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This PR fixes a nondeterministic crash in `grind` caused by a
`BEq`/`Hashable` invariant
violation in the congruence table. `congrHash` uses each expression's
own `funCC` flag to
compute its hash (one-level decomposition for `funCC = true`, full
recursive decomposition
for `funCC = false`), but `isCongruent` only checked the stored
expression's flag. When two
expressions with mismatched `funCC` flags accidentally hash-collided
(via pointer-based
`ptrAddrUnsafe` hashing), `isCongruent` could declare them congruent
despite different
argument counts, leading to an assertion failure in `mkCongrProof`.
The fix requires matching `funCC` flags in `isCongruent`. The PR also
fixes the debug
invariant checker (`checkParents`) to skip `funCC` parents and adds a
regression test for
funCC congruence.
Observed as a nondeterministic crash in Mathlib at
`Analysis/ODE/PicardLindelof.lean`.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This PR adds the infrastructure for simproc and discharger DSLs used to
specify `pre`/`post` simproc chains and conditional rewrite dischargers
in `Sym.simp` variants.
**Syntax categories** (`src/Init/Sym/Simp/SimprocDSL.lean`):
- `sym_simproc` with primitives (`ground`, `telescope`, `rewrite`,
`self`, `none`) and combinators (`>>`, `<|>`)
- `sym_discharger` with primitives (`self`, `none`) for the `with`
clause of `rewrite`
**Elaboration attributes**
(`src/Lean/Elab/Tactic/Grind/SimprocDSL.lean`):
- `builtin_sym_simproc` / `sym_simproc` mapping syntax to `Syntax →
GrindTacticM Simproc`
- `builtin_sym_discharger` / `sym_discharger` mapping syntax to `Syntax
→ GrindTacticM Discharger`
- `elabSymSimproc`, `elabSymDischarger`, and `elabWithClause`
dispatchers
Built-in elaborators for each primitive/combinator will follow in a
subsequent PR after the stage0 update.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This PR fixes an issue where `grind` could prove each conjunct
individually but failed on the conjunction. The root cause:
`solverAction`'s `.propagated` path calls `processNewFacts` which drains
the `newFacts` queue, but the resulting propagation cascade (congruence
closure, or-propagation, `propagateForallPropDown`) can call
`addNewRawFact`, enqueuing to the separate `newRawFacts` queue. These
raw facts were never drained.
The fix moves `Solvers.mkAction` from `Types.lean` to `Intro.lean` where
it can compose the core solver action with `assertAll`, unconditionally
draining `newRawFacts` after every solver step.
Closes#12581
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This PR adds named theorem sets for `Sym.simp` with associated
attributes, following the same pattern as `Meta.simp`'s
`register_simp_attr`.
- `register_sym_simp_attr my_set` creates a named set with its own
`PersistentEnvExtension` and attribute
- `@[my_set] theorem ...` adds a rewrite theorem
- `@[my_set] def ...` adds equation theorems from the definition
- `builtin_initialize symSimpExtension` registers a default
`@[sym_simp]` set
- `getSymSimpTheorems` / `getSymSimpExtension?` retrieve theorem sets at
tactic time
New files:
- `Sym/Simp/Attr.lean`: attribute logic (`mkSymSimpAttr`,
`registerSymSimpAttr`)
- `Sym/Simp/RegisterCommand.lean`: `register_sym_simp_attr` macro
Tests:
- `tests/pkg/sym_simp_attr/`: package test with user-defined set
(`my_sym_simp`)
- `tests/elab/sym_simp_set.lean`: tests for the builtin `@[sym_simp]`
set
This PR makes errors in `lake cache get` / `lake cache put` artifact
transfers more verbose, which helps with debugging. It also fixes an
issue with error reporting when downloading artifacts on demand.
This PR removes the custom `M`/`State` monad from structural recursion
elaboration, replacing it with plain `MetaM`. This simplifies the code
and makes the control flow more explicit, in preparation for #12987
which
introduces named `_f` auxiliary definitions for structural recursion.
Key changes:
- Remove `State`/`M` types from `Structural.Basic`, use `MetaM`
throughout
- Extract `withRecFunsAsAxioms` helper for adding recursive functions as
temporary axioms
- Split `tryAllArgs` into `findRecArgCandidates` (analysis) and
`tryCandidates` (backtracking execution)
- Move `withoutModifyingEnv` into each phase that needs it
- For inductive predicates, return matchers from `mkIndPredBRecOnF`
instead of accumulating in state
- Pass `fnTypes` explicitly to `mkBRecOnMotive` instead of re-inferring
This is a pure refactoring with no behavior changes (except matcher
numbering in `inductive_pred` test due to changed
`saveState`/`restoreState` boundaries).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This PR further enforces that all modules used in compile-time execution
must be meta imported in preparation for enabling
https://github.com/leanprover/lean4/pull/10291
# Breaking changes
Metaprograms that call `compileDecl` directly may now need to call
`markMeta` first where appropriate, possibly based on the value of
`isMarkedMeta` of existing decls. `addAndCompile` should be split into
`addDecl` and `compileDecl` for this in order to insert the call in
between.
This PR increases Lean's default stack size, including for the main
thread of Lean executables, to 1GB.
As stack pages are allocated dynamically, this should not change the
memory usage of programs but can prevent them from stack overflowing.
The stack size (of any Lean thread) can now be customized via the
`LEAN_STACK_SIZE_KB` environment variable. `main` can be prevented from
running on a new thread by setting `LEAN_MAIN_USE_THREAD=0`, in which
case the default OS stack size management applies to the main thread
again.
This PR enables support for respecting user provided borrow annotations.
This allows user to mark arguments of their definitions or local
functions with `(x : @&Ty)` and have the borrow inference try its best
to preserve this annotation, thus potentially reducing RC pressure. Note
that in some cases this might not be possible. For example, the compiler
prioritizes preserving tail calls over preserving borrow annotations. A
precise reasoning of why the compiler chose to make its inference
decisions can be obtained with `trace.Compiler.inferBorrow`.
The implementation consists of two parts:
1. A propagator in ToLCNF. This is required because the elaborator does
not place the borrow annotations in the function binders themselves but
just in type annotations of let binders/global declarations while LCNF
expects them in the lambda variable binders themselves. Thus ToLCNF now
implements a (very weak but strong enough for this purpose) propagator
of the borrow annotations of a type annotation into the variable binders
of the term affected by the annotations
2. A weakening of the InferBorrow heuristic. It now has a set of
"forced" and "non-forced" reasons to mark a variable as owned instead of
borrowed. If a variable is user annotated as borrowed, it will only be
marked as owned if the reason is a forced one, e.g. preservation of tail
calls.
This PR speeds up some benchmarks when run as tests by lowering their
workload. It also stops testing some of the more expensive benchmarks
that can't be easily made smaller.
The ultimate goal of this work is to turn production of `.ir` files into
separate build step so that it does not block non-`meta` imports and can
be skipped entirely when not needed. This PR implements the main logic
of this new `leanir` compiler executable and runs it after `lean` inside
the same Lake build step but leaves its use disabled behind a
`compiler.postponeCompile` flag until further Lake adjustments move it
to a separate facet so that its use can be actually beneficial.
---------
Co-authored-by: Joscha <joscha@plugh.de>
This PR reorganizes the instances `ToString Int` and `Repr Int` so that
they both point at a common definition `Int.repr` (the same setup is
used for `Nat`). It then verifies the functions `Int.repr`,
`String.isInt` and `String.toInt`.
In particular, for `a : Int` we get `a.repr.toInt? = some a`, which
implies that `Int.repr` is injective.
This PR adds per-result `contextDependent` tracking to `Sym.Simp.Result`
and splits the simplifier cache into persistent (context-independent)
and transient (context-dependent, cleared on binder entry). This
replaces the coarse `wellBehavedMethods` flag.
Key changes:
- Add `contextDependent : Bool := false` to `Result.rfl` and
`Result.step`
- Split `State.cache` into `persistentCache` and `transientCache`
- Remove `wellBehavedMethods` from `Methods`
- Replace `withoutModifyingCacheIfNotWellBehaved` with
`withFreshTransientCache`
- Change `DischargeResult` to an inductive (`.failed`/`.solved`)
- Add `dischargeAssumption` (context-dependent discharger for testing)
- Add `sym.simp.debug.cache` trace class
- Propagate `contextDependent` through all combinators (congruence,
transitivity, control flow, arrows, rewriting)
- Add `mkRflResult`/`mkRflResultCD` to avoid dynamic allocation of rfl
results
- Fix `isRfl` to ignore `contextDependent` (was silently broken by the
extra field)
Propagation invariant: when combining sub-results, `cd` is the
disjunction of ALL sub-results' flags — including `.rfl` results. If
`simp` returned `.rfl (contextDependent := true)`, it means `simp` might
take a completely different code path in another local context, so all
downstream results must be marked context-dependent.
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This PR introduces the `URI` data type.
This contains the same code as #10478, divided into separate pieces to
facilitate easier review.
The pieces of this feature are:
- Core data structures: #12126
- Headers: #12127
- URI: #12128
- Body: #12144
- H1: #12146
- Server: #12151
- Client:
---------
Co-authored-by: Rob23oba <152706811+Rob23oba@users.noreply.github.com>
This PR introduces the functions `String.Slice.skipPrefix?`,
`String.Slice.Pos.skip?`, `String.Slice.skipPrefixWhile`,
`String.Slice.Pos.skipWhile` and redefines `String.Slice.takeWhile` and
`String.Slice.dropWhile` to use these new functions.
This PR fixes an issue where the `induction` and `cases` tactics would
swallow diagnostics (such as unsolved goals errors) when the `using`
clause contains a nested tactic.
Closes#12815
This PR renames the function `ForwardPattern.dropPrefix?` to
`ForwardPattern.skipPrefix`?
This function `(s : String.Slice) -> Option s.Pos` is not to be confused
with `String.Slice.dropPrefix? : (s : String.Slice) -> Option
String.Slice`.
This PR fixes the interaction between `cbv_opaque` and
`inline`/`always_inline` annotations, to make sure that inlined
definitions marked as `cbv_opaque` are not unfolded during the
preprocessing stage of `cbv` tactic.
This PR redefines the `String.isNat` function to use less state and
perform short-circuiting. It then verifies the `String.isNat` and
`String.toNat?` functions.
Recall that `isNat` and `toNat?` allow `_` as a digit separator. This is
why we get the complicated statement
```lean
public theorem isNat_iff {s : String} :
s.isNat = true ↔
s ≠ "" ∧
(∀ c ∈ s.toList, c.isDigit ∨ c = '_') ∧
¬ ['_', '_'] <:+: s.toList ∧
s.toList.head? ≠ some '_' ∧
s.toList.getLast? ≠ some '_'
```
For `toNat?`, we prove the fully general
```lean
public theorem toNat?_eq_some_ofDigitChars {s : String} (h : s.isNat = true) :
s.toNat? = some (Nat.ofDigitChars 10 (s.toList.filter (· != '_')) 0)
```
as well as the useful `(Nat.repr n).toNat? = some n` (and the corollary
that `Nat.repr` is injective).
For people implementing formatting routines that involve digit
separators, we have
```lean
public theorem isNat_of_isDigit {s : String} (hne : s ≠ "")
(hdigit : ∀ c ∈ s.toList, c.isDigit) : s.isNat = true
public theorem isNat_append_underscore_append {s t : String}
(hs : s.isNat = true) (ht : t.isNat = true) :
(s ++ "_" ++ t).isNat = true
public theorem toNat?_append_underscore_append_eq_some {s t : String} {n m : Nat}
(hs : s.toNat? = some n) (ht : t.toNat? = some m) :
(s ++ "_" ++ t).toNat? =
some (10 ^ (t.toList.filter (· != '_')).length * n + m)
```
The missing bit here is `(s.leftpad k '0').toNat? = s.toNat?`, which is
missing because we don't have `String.leftpad` (yet). For any reasonable
definition of `leftpad`, this will follow from
`toNat?_eq_some_ofDigitChars` since we prove the necessary ingredients
about `ofDigitChars`.
There are some rough edges around `ofDigitChars`, and in the future it
will be nice to connect this all to mathlib's `Nat.digits` and
`Nat.ofDigits`, which are similar but different.
This PR makes `#print` show the full internal private name (including
module prefix) in the declaration signature when `pp.privateNames` is
set to true. Previously, `pp.privateNames` only affected names in the
body but the signature always stripped the private prefix.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This PR fixes a series of errors in docstrings.
This includes:
- incorrect gramar
- errant reference to "dependent" in the non-dependent `HashMap` files
- reference to expression metavariables as universe level metavariables
- outdated reference to `usizeSz` instead of `USize.size`
- syntax errors in code examples
- a broken link to a paper
---------
Co-authored-by: Derrik Petrin <derrik.petrin@pm.me>
This PR changes `lake cache get` and `lake cache put` to transfer
artifacts in parallel (using `curl --parallel`) when uploading or
eagerly downloading artifacts. Transfers are still recorded one-by-one
in the output -- no progress meter yet.
This PR removes the obsolete `Lean.Environment.replay` from
`src/Lean/Replay.lean` and replaces it with the improved version from
`src/LeanChecker/Replay.lean`, which includes fixes for duplicate
theorem handling and Quot/Eq dependency ordering. The primed names
(`Replay'`, `replay'`) are renamed back to `Replay` and `replay`.
A test for the original issue (nested inductives failing with `replay`)
is added as `tests/elab/issue12819.lean`.
Closes#12819🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This PR adds a `sym =>` tactic that enters an interactive symbolic
simulation
mode built on `grind`. Unlike `grind =>`, it does not eagerly introduce
hypotheses or apply by-contradiction, giving users explicit control over
`intro`, `apply`, and `internalize` steps.
New tactics available in `sym =>` mode:
- `intro` / `intros`: introduce binders and internalize into the E-graph
by
default. Use `intro~` or `intro (internalize := false)` to skip
internalization.
- `apply t`: apply backward rules with caching for `repeat`.
- `internalize` / `internalize_all`: internalize hypotheses into the
E-graph.
- `by_contra`: apply proof by contradiction, negating the target.
Satellite solvers (`lia`, `ring`, `linarith`) automatically introduce
remaining
binders and apply by-contradiction in `sym =>` mode, matching their
behavior in
default tactic mode. All existing `grind =>` tactics (`finish`,
`instantiate`,
`cases`, etc.) also work in `sym =>` mode. The sym-specific tactics are
guarded
and rejected in regular `grind =>` mode.
```lean
example (x : Nat) : myP x → myQ x := by
sym [myP_myQ] =>
intro h
finish
example (x y z : Nat) : x > 1 → x + y + z > 0 := by
sym =>
lia
```
This PR fixes an issue where `realizeConst` would generate auxiliary
declarations
(like `_sparseCasesOn`) using the original defining module's private
name prefix
rather than the realizing module's prefix. When two modules
independently realized
the same imported constant, they produced identically-named auxiliary
declarations,
causing "environment already contains" errors on diamond import.
The fix re-privatizes the constant name under the current module before
passing it
to `withDeclNameForAuxNaming`, ensuring each realizing module generates
distinctly
named auxiliary declarations.
Fixes#12825
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This PR fixes a build failure on macOS introduced by #12540. macOS BSD
`ar` does not support the `@file` response file syntax that #12540
enabled unconditionally. On macOS, when building core (i.e., `bootsrap
:= true`), `recBuildStatic` now uses `libtool -static -filelist`, which
handles long argument lists natively.
Includes a `stage0/src/stdlib_flags.h` trigger so CI will automatically
run `update-stage0` after merge.
🤖 Prepared with Claude Code
Implementation adjusted by @tydeu
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Mac Malone <mac@lean-fro.org>
Co-authored-by: Sebastian Ullrich <sebasti@nullri.ch>
This PR fixes a regression introduced in Lean 4.29.0-rc2 where `simp` no
longer simplifies inside type class instance arguments due to the
`backward.isDefEq.respectTransparency` change. This breaks proofs where
a term like `(a :: l).length` appears both in the main expression and
inside implicit instance arguments (e.g., determining a `BitVec` width).
**The problem:** After `simp only [List.length_cons]`, the main
expression has `l.length + 1` but instances still have `(a ::
l).length`. Since `simp` no longer simplifies inside instances, and
`isDefEq` won't unfold `List.length` at the default transparency,
subsequent lemma applications fail.
**Reproducer** (from Son Ho, reported by Sebastian Ullrich):
```lean
theorem BitVec.getElem!_eq_testBit_toNat {w : Nat} (x : BitVec w) (i : Nat) :
x[i]! = x.toNat.testBit i := by sorry
example (l : List Nat) (a : Nat) (j : Nat) :
(0#((a :: l).length))[j]! = (0#((a :: l).length)).toNat.testBit j := by
simp only [List.length_cons]
simp only [BitVec.getElem!_eq_testBit_toNat] -- works in 4.28.0-rc1, fails in 4.29.0-rc6
```
**The fix:** Mark `List.length` as `@[implicit_reducible]`, allowing
`isDefEq` to unfold it when checking implicit arguments. Several proofs
that previously needed a trailing `rfl` after `simp` now close directly,
since `simp` can see through `List.length` in more positions.
**Longer term:** The root cause is that `GetElem` carries complex proof
obligations in its type class instances, making implicit arguments
sensitive to definitional equality of collection sizes. We are
considering a redesign with a noncomputable `GetElemV` variant based on
`Nonempty` that avoids these casts entirely, but that is a larger change
planned for a future release.
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This PR adds simp lemmas equating kernel-friendly function names with
their operator notation equivalents: `Nat.land_eq`, `Nat.lor_eq`,
`Nat.xor_eq`, `Nat.shiftLeft_eq'`, `Nat.shiftRight_eq'`, and
`Bool.rec_eq`. These are useful when proofs involve reflection and need
to simplify kernel-reduced terms back to operator notation.
Closes#12716
Co-authored-by: Claude <noreply@anthropic.com>
This PR changes the Lake `CacheMap` data structure to track the
platform-dependence of outputs. Platform-independent packages will no
longer include platform-dependent mappings in the output files produced
by `lake build -o`.
This PR ensures that when a function is marked `export` its borrow
annotations (if present) are always ignored.
This was the previous behavior in the C++ version of this file but
slightly modified when porting to the old IR and thus subsequently
ported to LCNF wrongly as well.
This PR moves `RequestCancellationToken` from `IO.Ref` to
`IO.CancelToken`.
They consist of the same data, but the constructor of `CancelToken` is
private. Hence there is no way to take the `Ref` in a
`RequestCancellationToken` and turn it into a `CancelToken`. This in
turn means that we can't set `Core.Context.cancelTk?` to be the one in
`RequestContext` when launching `CoreM` tasks in request handlers.
This PR fixes some process signals that were incorrectly numbered.
From what I can tell, the code used signals and signal numbers for
Alpha/SPARC, not x86/ARM. The test was also broken and always green,
hiding the mistake.
This PR changes the interaction between `@[cbv_opaque]` and
`@[cbv_eval]`
attributes in the `cbv` tactic. Previously, `@[cbv_opaque]` completely
blocked
all reduction including `@[cbv_eval]` rewrite rules. Now, `@[cbv_eval]`
rules
can fire on `@[cbv_opaque]` constants, allowing users to provide custom
rewrite
rules without exposing the full definition. Equation theorems, unfold
theorems,
and kernel reduction remain suppressed for opaque constants.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This PR introduces the `Headers` data type, that provides a good and
convenient abstraction for parsing, querying, and encoding HTTP/1.1
headers.
This contains the same code as #10478, divided into separate pieces to
facilitate easier review.
The pieces of this feature are:
- Core data structures: #12126
- Headers: #12127
- URI: #12128
- Body: #12144
- H1: #12146
- Server: #12151
- Client:
---------
Co-authored-by: Rob23oba <152706811+Rob23oba@users.noreply.github.com>
This PR fixes a segfault when running `idbgClientLoop`. `@[extern]`
expects that the function doesn't include erased arguments in the
signature; however, `@[export]` exports the function with all arguments,
including erased ones. This causes a function signature mismatch between
`idbgClientLoopImpl` and `idbgClientLoop`, causing segfaults. However,
instead of solving the deeper problem that `@[extern]` - `@[export]`
pairs can cause such problems, this PR removes the erased arguments from
`idbgClientLoopImpl` and replaces occurrences of `α` with `NonScalar`.
This PR extends Lake's use of response files (`@file`) from Windows-only
to all platforms, avoiding `ARG_MAX` limits when invoking `clang`/`ar`
with many object files.
Lake already uses response files on Windows to avoid exceeding CLI
length limits. On macOS and Linux, linking Mathlib's ~15,000 object
files into a shared library can exceed macOS's `ARG_MAX` (262,144
bytes). Both `clang` and `gcc` support `@file` response files on all
platforms, so this is safe to enable unconditionally.
Reported as a macOS issue at
https://leanprover.zulipchat.com/#narrow/channel/270676-lean4/topic/The.20clang.20command.20line.20with.20all.20~15.2C000.20Mathlib.20.2Ec.2Eo.2Eexport/near/574369912:
the Mathlib cache ships Linux `.so` shared libs but not macOS `.dylib`
files, so `precompileModules` on macOS triggers a full re-link that
exceeds `ARG_MAX`.
🤖 Prepared with Claude Code
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This PR adds the `fixedToolchain` Lake package configuration option.
Setting this to `true` informs Lake that the package is only expected to
function on a single toolchain (like Mathlib). This causes Lake's
toolchain update procedure to prioritize its toolchain and avoids the
need to separate input-to-output mappings for the package by toolchain
version in the Lake cache.
The tests need to run with certain environment variables set that only
cmake really knows and that differ between stages. Cmake could just set
the variables directly when running the tests and benchmarks, but that
would leave no good way to manually run a single benchmark. So cmake
generates some stage-specific scripts instead that set the required
environment variables.
Previously, those scripts were sourced directly by the individual
`run_*` scripts, so the env scripts of different stages would overwrite
each other. This PR changes the setup so they can instead be generated
next to each other. This also simplifies the `run_*` scripts themselves
a bit, and makes `tests/bench/build` less of a hack.
This PR fixes the `HSub PlainTime Duration` instance, which had its
operands reversed: it computed `duration - time` instead of `time -
duration`. For example, subtracting 2 minutes from `time("13:02:01")`
would give `time("10:57:59")` rather than the expected
`time("13:00:01")`. We also noticed that `HSub PlainDateTime
Millisecond.Offset` is similarly affected.
Closes#12918
This PR places `set_option compiler.ignoreBorrowAnnotation true in` on
to all `export`/`extern`
pairs. This is necessary because `export` forces all arguments to be
passed as owned while `extern`
respects borrow annotations. The current approach to the
`export`/`extern` trick was always broken
but never surfaced. However, with upcoming changes many
`export`/`extern` pairs are going to be
affected by borrow annotations and would've broken without this.
This PR replaces `find -print0 | xargs -0 -I{} sh -c '...'` with
`find -print0 | while IFS= read -r -d '' f; do ... done` for the
subverso sub-manifest sync in release_steps.py. The original xargs
invocation had fragile nested shell quoting; the while-read loop is
both null-delimiter safe and more readable.
🤖 Prepared with Claude Code
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This PR changes `lake cache get` to download artifacts by default.
Artifacts can be downloaded on demand with the new `--mappings-only`
option (`--download-arts` is now obsolete).
In the future, the plan is to have Lake download mappings when cloning
dependencies. Then, `lake cache get` will primarily be used to download
artifacts eagerly. Thus, it makes sense to have that as the default.
This PR removes unused functions (`mkPatternCoreFromLambda`,
`mkPatternFromLambda`, `mkSimprocPatternFromExpr`) and the `import
Lean.Meta.AbstractMVars` that were added to `Lean.Meta.Sym.Pattern`
after merging #12597.
This PR fixes a bug where `max u v` and `max v u` fail to match in
SymM's pattern matching. Both `processLevel` (Phase 1) and
`isLevelDefEqS` (Phase 2) treated `max` positionally, so `max u v ≠ max
v u` structurally even though they are semantically equal.
The fix has three parts:
- Eagerly normalize universe levels in patterns at creation time
(`preprocessDeclPattern`, `preprocessExprPattern`,
`mkSimprocPatternFromExpr`)
- Normalize the target level in `processLevel` before matching, using a
`where go` refactor
- Add `tryApproxMaxMax` to `processLevel` and `isLevelDefEqS`: when
positional `max/max` matching would fail, check if one argument from
each side matches structurally and match the remaining pair
Also moves `normalizeLevels` from `Grind.Util` to `Sym.Util` to avoid
code duplication, since both Sym and Grind need it.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This PR adds eta reduction to the sym discrimination tree lookup
functions (`getMatch`, `getMatchWithExtra`, `getMatchLoop`). Without
this, expressions like `StateM Nat` that unfold to eta-expanded forms
`(fun α => StateT Nat Id α)` fail to match discrimination tree entries
for the eta-reduced form `(StateT Nat Id)`.
Also optimizes `etaReduce` with an early exit for non-lambda expressions
and removes a redundant `n == 0` check.
Includes a test verifying that `P (StateM Nat)` matches a disc tree
entry for `P (StateT Nat Id)`.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This PR optimizes the `String.reduceEq`, `String.reduceNe`, and
`Sym.Simp` string equality simprocs to produce kernel-efficient proofs.
Previously, these used `String.decEq` which forced the kernel to run
UTF-8 encoding/decoding and byte array comparison, causing 86+ kernel
unfoldings on short strings.
The new approach reduces string inequality to `List Char` via
`String.ofList_injective`, then uses two strategies depending on the
difference:
- **Different characters at position `i`**: Projects to `Nat` via
`congrArg (fun l => (List.get!Internal l i).toNat)`, then uses
`Nat.ne_of_beq_eq_false rfl`. This avoids `Decidable` instances entirely
— the kernel only evaluates `Nat.beq` on two concrete natural numbers.
- **One string is a prefix of the other**: Uses `congrArg (List.drop n
·)` with `List.cons_ne_nil`, which is a definitional proof requiring no
`decide` step at all.
For equal strings, `eq_true rfl` avoids kernel evaluation entirely.
The shared proof construction is in `Lean.Meta.mkStringLitNeProof`
(`Lean/Meta/StringLitProof.lean`), used by both the standard simprocs
and the `Sym.Simp` ground evaluator.
Kernel max unfolds for `"hello" ≠ "foo"`: 86+ → 6.
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This PR adjusts the JSON encoding of RPC references from `{"p": "n"}` to
`{"__rpcref": "n"}`. Existing clients will continue to work unchanged,
but should eventually move to the new format by advertising the
`rpcWireFormat` client capability.
- This came up in leanprover/vscode-lean4#712.
- The new encoding is far less likely to clash with real-world names,
and is now documented as a "reserved internal name".
- At 8 bytes vs. 1 byte, it incurs a ~5% size increase on the JSON size
of interactive terms, e.g. from 868KiB to 903KiB on the
leanprover/vscode-lean4#500 test.
- Make `deriving RpcEncodable` throw an error when it encounters the
reserved name. We cannot easily guard against clashes in user-provided
JSON, however, so we just assume it does not clash.
- Add a notion of *RPC wire format* with corresponding `rpcWireFormat`
client and server capabilities. The format before this PR is now called
`v0`, whereas here we implement `v1`. Existing clients should eventually
implement compatibility with `v1` (because doing so fixes the above
bug), but will continue to work in the meantime. The format may be
revised again in the future (but we don't expect to revise it so often
that semver would be useful).
- Document everything.
## Alternative designs (abandoned for now)
- Option 1. Add a method `$/lean/rpc/metadata` which, given the name of
an RPC method `foo`, returns metadata containing a description of where
the RPC refs in any return value of `foo` would be (essentially a
description of the structure of the return type).
- Option 2. Wrap every response to `$/lean/rpc/call` in such metadata.
This would be a different change to the wire format.
- To implement this in an extensible way, we extend `RpcEncodable` by a
`refPaths` field. But how does `refPaths` describe where the refs are?
- Option A. Emit the code of a JS method that extracts the refs. This is
maybe simplest, but it would leave non-JS clients (e.g. `lean.nvim`)
behind.
- Option B. Give the description in some query language. The query
language must be able to describe paths into arbitrary inductive types.
- The most popular option,
[JSONPath](https://www.rfc-editor.org/rfc/rfc9535), seemingly cannot
describe non-uniform paths (e.g. both the `a`s in `{a: 1, {b: {a:
2}}}`).
- [JMESPath](https://jmespath.org/) can describe non-uniform paths, and
has 'fully compliant' implementations in many languages, but doesn't
seem to handle recursive paths.
- The most expressive option is [jq](https://github.com/jqlang/jq), but
the most popular way to run it is via an Emscripten WASM blob in
[jq-web](https://github.com/fiatjaf/jq-web) which seems heavy. There is
[jqjs](https://github.com/mwh/jqjs) as well; I'm not sure how
production-ready that is.
This PR makes `@[cbv_opaque]` unconditionally block all evaluation of a
constant
by `cbv`, including `@[cbv_eval]` rewrite rules. Previously,
`@[cbv_eval]` could
bypass `@[cbv_opaque]`, and for bare constants (not applications),
`isOpaqueConst`
could fall through to `handleConst` which would unfold the definition
body.
The intended usage pattern is now: mark subterm-producing functions
(like
`DHashMap.insert`) as `@[cbv_opaque]` to prevent unfolding, and provide
`@[cbv_eval]` theorems on the *consuming* function (like
`DHashMap.contains`)
which pattern-matches against the opaque subterms.
This PR generalizes the sym MVCGen's match splitting from `ite`-only to
`ite`, `dite`, and arbitrary matchers. Previously, only `ite` was
supported; `dite` and match expressions were rejected with an error.
`mkBackwardRuleForSplit` uses `SplitInfo.splitWith` to build the
splitting proof. Hypothesis types are discovered via `rwIfOrMatcher`
inside the splitter telescope, and `TransformAltFVars.all` provides the
proper fvars for `mkForallFVars`. Subgoal type metavariables use
`mkFreshExprSyntheticOpaqueMVar` so that `rwIfOrMatcher`'s internal
`assumption` tactic cannot assign them.
Adds `DiteSplit`, `MatchSplit`, and `MatchSplitState` test cases and a
`vcgen_match_split` benchmark.
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This PR introduces a `TransformAltFVars` structure to replace the flat
`Array Expr`
parameter in the `onAlt` callback of `MatcherApp.transform`. The new
structure gives
callers structured access to the different kinds of fvars introduced in
matcher
alternative telescopes: constructor fields, overlap parameters,
discriminant equations,
and extra equations from `addEqualities`.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This PR adds support for ignoring user defined borrow annotations. This
can be useful when defining
`extern`/`export` pairs as the `extern` might be infected by borrow
annotations while in `export`
they are already ignored.
This PR adds a benchmark that measures `simp` performance on string
literal equality and inequality for various string lengths and
difference positions.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This PR ports the C emission pass from IR to LCNF, marking the last step
of the IR/LCNF conversion and thus enabling end-to-end code generation
through the new compilation infrastructure.
This PR adds an `@[mvcgen_witness_type]` tag attribute, analogous to
`@[mvcgen_invariant_type]`, that allows users to mark types as witness
types. Goals whose type is an application of a tagged type are
classified as witnesses rather than verification conditions, and appear
in a new `witnesses` section in the `mvcgen` tactic syntax (before
`invariants`).
Witnesses are concrete values the prover supplies (inspired by
zero-knowledge proofs), as opposed to invariants (predicates maintained
across iterations) or verification conditions (propositions to prove).
The test uses a ZK-inspired example where a `SquareRootWitness` value
must be provided by the prover, with the resulting constraint
auto-discharged.
Changes:
- `src/Lean/Elab/Tactic/Do/Attr.lean`: register `@[mvcgen_witness_type]`
tag attribute and `isMVCGenWitnessType` helper
- `src/Lean/Elab/Tactic/Do/VCGen/Basic.lean`: add `witnesses` field to
`State`, three-way classification in `addSubGoalAsVC`
- `src/Std/Tactic/Do/Syntax.lean`: add `witnesses` section syntax
(before `invariants`), extract shared `goalDotAlt`/`goalCaseAlt` syntax
kinds
- `src/Lean/Elab/Tactic/Do/VCGen.lean`: extract shared
`elabGoalSection`, add `elabWitnesses`, wire up witness labeling and
elaboration
- `tests/elab/mvcgenWitnessType.lean`: end-to-end tests for
witness-only, witness with `-leave`, and combined witness+invariant
scenarios
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This PR adds `Invariant.withEarlyReturnNewDo`,
`StringInvariant.withEarlyReturnNewDo`, and
`StringSliceInvariant.withEarlyReturnNewDo` which use `Prod` instead of
`MProd` for the state tuple, matching the new do elaborator's output.
The existing `withEarlyReturn` definitions are reverted to `MProd` for
backwards compatibility with the legacy do elaborator. Tests and
invariant suggestions are updated to use the `NewDo` variants.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This PR applies `@[mvcgen_invariant_type]` to `Std.Do.Invariant` and
removes the hard-coded fallback in `isMVCGenInvariantType` that was
needed for bootstrapping (cf. #12874). It also extracts
`StringInvariant` and `StringSliceInvariant` as named abbreviations
tagged with `@[mvcgen_invariant_type]`, so that `mvcgen` classifies
string and string slice loop invariants correctly.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This PR adds an `@[mvcgen_invariant_type]` tag attribute so that users
can mark
custom types as invariant types for the `mvcgen` tactic. Goals whose
type is an
application of a tagged type are classified as invariants rather than
verification
conditions. The hard-coded check for `Std.Do.Invariant` is kept as a
fallback
until a stage0 update allows applying the attribute directly.
A follow-up PR (after a stage0 update) will apply
`@[mvcgen_invariant_type]` to
`Std.Do.Invariant` and remove the hard-coded fallback.
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This PR fixes a CMake error when the `lake-ci` label is used. The
previous
implementation appended the full `tests/lake/tests/` glob to a base list
that
already included `tests/lake/tests/shake/test.sh`, causing a duplicate
`add_test` name. This uses an if/else to select the appropriate glob
instead.
Discovered via https://github.com/leanprover/lean4/pull/12540 which has
the
`lake-ci` label.
🤖 Prepared with Claude Code
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This PR makes sure that identifiers with `Meta` or `Simproc` in their
name do not show up in library search results.
For example, `Nat.Simproc.eq_add_gt` can currently be suggested by
library search, even though it is an implementation detail.
Additionally, there are various declarations in mathlib in the
`Mathlib.Meta` namespace that we do not want to suggest.
This PR adds support for simp/equational spec theorems in the SymM-based
`mvcgen'` tactic,
catching up with a feature that the original `mvcgen` has supported for
a long time.
Users can write `@[spec] theorem : get (m := StateT σ m) = fun s => pure
(s, s) := rfl`
instead of manually specifying equivalent Hoare triples. The equational
form is more
concise and natural for specs that simply unfold definitions.
The universe level normalization (`normalizeLevelsExpr`) applied in
`work` and the backward
rule constructors is a workaround; ideally this should be integrated
into
`preprocessMVar`/`preprocessExpr` in the SymM framework so all users
benefit.
Changes:
- Add `SpecTheoremKind` to distinguish triple vs simp specs in
`SpecTheoremNew`
- Add `mkSpecTheoremNewFromSimpDecl?` to create spec entries from
equational lemmas, filtering no-op equations
- Add `mkBackwardRuleFromSimpSpec` to build backward rules via
`Eq.mpr`/`congrArg`, with instance synthesis, projection reduction, and
`unfoldReducible` on the RHS
- Migrate simp theorems from `SimpTheorems` database during
`migrateSpecTheoremsDatabase`
- Normalize universe levels so structural matching in
`BackwardRule.apply` succeeds when `max u v` vs `max v u` arise from
different code paths
- Simplify `mkSpecContext` by removing the mock `simp` context
construction
- Use `mkBackwardRuleFromExpr` instead of `mkAuxLemma` for triple specs,
since the proof may contain free variables from the goal context
- Add `AddSubCancelSimp` benchmark case and test exercising the simp
spec code path
- Change `AddSubCancel` spec proofs from `mvcgen` to `mvcgen'`
(dogfooding)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This PR extracts the example programs from the sym mvcgen benchmarks
into
shared `Cases.*` modules so that both benchmarks and a new fast test
suite
can reuse them. It also renames `vcgen_deep_add_sub_cancel` to
`vcgen_add_sub_cancel_deep` for consistency.
The test suite (`test_vcgen.lean`) runs all cases at n=10, completing in
~2s vs minutes for the full benchmarks. It is wired up as a `lake test`
driver and integrated with the lean4 test/bench infrastructure via
`run_test`/`run_bench` scripts registered in `CMakeLists.txt`.
Benchmark output now uses aligned `CaseName(n):` labels. The `run_bench`
script extracts per-case vcgen and kernel timings into
`measurements.jsonl`.
Benchmarks run single-threaded (`LEAN_NUM_THREADS=1`) for
reproducibility.
`vcgen_get_throw_set` is excluded from benchmarks due to pathological
`instantiateMVars` behavior.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This PR adds `optType` support to the `doPatDecl` parser, allowing
`let ⟨width, height⟩ : Nat × Nat ← action` in do-notation. Previously,
only
the less ergonomic `let ⟨width, height⟩ : Nat × Nat := ← action`
workaround
was available. The type annotation is propagated to the monadic action
as an
expected type, matching `doIdDecl`'s existing behavior.
Both the legacy and new (BuiltinDo) elaborators are updated.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This PR adds a `cbv_simproc` system for the `cbv` tactic, mirroring
simp's `simproc` infrastructure but tailored to cbv's three-phase
pipeline (`↓` pre, `cbv_eval` eval, `↑` post). User-defined
simplification procedures are indexed by discrimination tree patterns
and dispatched during cbv normalization.
New syntax:
- `cbv_simproc [↓|↑|cbv_eval] name (pattern) := body` — define and
register a cbv simproc
- `cbv_simproc_decl name (pattern) := body` — define without registering
- `attribute [cbv_simproc [↓|↑|cbv_eval]] name` — register an existing
declaration
- `builtin_cbv_simproc` variants for the internal use
New files:
- `src/Init/CbvSimproc.lean` — syntax and macros
- `src/Lean/Meta/Tactic/Cbv/CbvSimproc.lean` — types, env extensions,
registration, dispatch
- `src/Lean/Elab/Tactic/CbvSimproc.lean` — pattern elaboration and
command elaborators
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This PR add support for erasing `@[cbv_eval]` annotations using
`attribute [-cbv_eval]`, mirroring the existing `@[-simp]` mechanism for
simp lemmas.
The `CbvEvalEntry` now tracks the original declaration name (`origin`)
so that inverted theorems (`@[cbv_eval ←]`) can be erased by their
original name. The `CbvEvalState` stores individual entries alongside
the composed `Theorems` discrimination tree, allowing the tree to be
rebuilt from remaining entries after erasure. Erasure is properly scoped
via `modifyState`, so `attribute [-cbv_eval]` inside a `section` is
reverted when the section ends.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
This PR adds a `lake-ci` label that enables the full Lake test suite in
CI,
avoiding the need to temporarily commit and revert changes to
`tests/CMakeLists.txt`. The `lake-ci` label implies `release-ci` (check
level
3), so all release platforms are also tested.
Motivated by
https://github.com/leanprover/lean4/pull/12540#issuecomment-4000081071
where @tydeu requested running `release-ci` with Lake tests enabled,
which
previously required temporarily uncommenting a line in
`tests/CMakeLists.txt`.
Users can add it via a PR comment containing `lake-ci` on its own line,
or by
adding the label manually. CI automatically restarts when the label is
added.
Implementation:
- `ci.yml`: detect `lake-ci` label, set check level 3, pass
`-DLAKE_CI=ON` to cmake
- `tests/CMakeLists.txt`: `option(LAKE_CI ...)` conditionally enables
full `tests/lake/tests/` glob
- `restart-on-label.yml`: restart CI on `lake-ci` label
- `labels-from-comments.yml`: support `lake-ci` comment
🤖 Prepared with Claude Code
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This PR adds a `set_option grind.unusedLemmaThreshold` that, when set to
N > 0
and `grind` succeeds, reports E-matching lemmas that were activated at
least N
times but do not appear in the final proof term. This helps identify
`@[grind]`
annotations that fire frequently without contributing to proofs.
🤖 Prepared with Claude Code
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This PR adds a `result? : Option TraceResult` field to `TraceData` and
populates it in `withTraceNode` and `withTraceNodeBefore`, so that
metaprograms walking trace trees can determine success/failure
structurally instead of string-matching on emoji.
`TraceResult` has three cases: `.success` (checkEmoji), `.failure`
(crossEmoji), and `.error` (bombEmoji, exception thrown). An
`ExceptToTraceResult` typeclass converts `Except` results to
`TraceResult` directly, with instances for `Bool` and `Option`.
`TraceResult.toEmoji` converts back to emoji for display. This replaces
the previous `ExceptToEmoji` typeclass — `TraceResult` is now the
primary representation rather than being derived from emoji strings.
`withTraceNodeBefore` (used by `isDefEq`) uses
`ExceptToTraceResult.toTraceResult` directly, correctly handling `Bool`
(`.ok false` = failure) and `Option` (`.ok none` = failure), with
`Except.error` mapping to `.error`.
For `withTraceNode`, `result?` defaults to `none`. Callers can pass
`mkResult?` to provide structured results; when set, the corresponding
emoji is auto-prepended to the message.
Motivated by mathlib's `#defeq_abuse` diagnostic tactic
(https://github.com/leanprover-community/mathlib4/pull/35750) which
currently string-matches on emoji to determine trace node outcomes. See
https://leanprover.zulipchat.com/#narrow/channel/113488-general/topic/backward.2EisDefEq.2ErespectTransparency🤖 Prepared with Claude Code
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This PR makes notations such as `∨`, `∧`, `≤`, and `≥` pretty print
using ASCII versions when `pp.unicode` is false.
Continuation of #10373. Closes#1056.
This will require followup with a stage0 update and removal of the
ASCII-only `<=` and `>=` syntaxes from `Init.Notation`, for cleanup.
This PR optimizes the handling of `match_same_ctor.het` to make it emit
nice match trees as opposed to unoptimized CPS style code.
`match_same_ctor.het` is essentially a specialized kind of matcher where
we know that two objects are built from the same constructor and we wish
to call a continuation on their data. This means for every constructor
that contains data `het` takes one closure as an argument. Then after
matching on one of the objects every closure but the one relevant for
the match is released in every match arm, causing quadratic code
generation. This PR ensures that the `het` declarations get inlined and
then further processed by ordinary matcher and casesOn compilation,
thereby removing all of the continuations from the compiled code.
This PR replaces the default `instantiateMVars` implementation with a
two-pass variant that fuses fvar substitution into the traversal,
avoiding separate `replace_fvars` calls for delayed-assigned MVars and
preserving sharing. The old single-pass implementation is removed
entirely.
The previous implementation had quadratic complexity when instantiating
expressions with long chains of nested delayed-assigned MVars. Such
chains arise naturally from repeated `intro`/`apply` tactic sequences,
where each step creates a new delayed assignment wrapping the previous
one. The new two-pass approach resolves the entire chain in a single
traversal with a fused fvar substitution, reducing this to linear
complexity.
### Terminology (used in this PR and in the source)
* **Direct MVar**: an MVar that is not delayed-assigned.
* **Pending MVar**: the direct MVar stored in a
`DelayedMetavarAssignment`.
* **Assigned MVar**: a direct MVar with an assignment, or a
delayed-assigned MVar with an assigned pending MVar.
* **MVar DAG**: the directed acyclic graph of MVars reachable from the
expression.
* **Resolvable MVar**: an MVar where all MVars reachable from it
(including itself) are assigned.
* **Updateable MVar**: an assigned direct MVar, or a delayed-assigned
MVar that is resolvable but not reachable from any other resolvable
delayed-assigned MVar.
In the MVar DAG, the updateable delayed-assigned MVars form a cut (the
**updateable-MVar cut**) with only assigned MVars behind it and no
resolvable delayed-assigned MVars before it.
### Two-pass architecture
**Pass 1** (`instantiate_direct_fn`): Traverses all MVars and
expressions reachable from the initial expression and instantiates all
updateable direct MVars (updating their assignment with the result),
instantiates all level MVars, and determines if there are any updateable
delayed-assigned MVars.
**Pass 2** (`instantiate_delayed_fn`): Only run if pass 1 found
updateable delayed-assigned MVars. Has an **outer** and an **inner**
mode, depending on whether it has crossed the updateable-MVar cut.
In outer mode (empty fvar substitution), all MVars are either unassigned
direct MVars (left alone), non-updateable delayed-assigned MVars
(pending MVar traversed in outer mode and updated with the result), or
updateable delayed-assigned MVars. When a delayed-assigned MVar is
encountered, its MVar DAG is explored (via `is_resolvable_pending`) to
determine if it is resolvable (and thus updateable). Results are cached
across invocations.
If it is updateable, the substitution is initialized from its arguments
and traversal continues with the value of its pending MVar in inner
mode. In inner mode (non-empty substitution), all encountered
delayed-assigned MVars are, by construction, resolvable but not
updateable. The substitution is carried along and extended as we cross
such MVars. Pending MVars of these delayed-assigned MVars are NOT
updated with the result (as the result is valid only for this
substitution, not in general).
Applying the substitution in one go, rather than instantiating each
delayed-assigned MVar on its own from inside out, avoids the quadratic
overhead of that approach when there are long chains of delayed-assigned
MVars.
**Write-back behavior**: Pass 2 writes back the normalized pending MVar
values of delayed-assigned MVars above the updateable-MVar cut (the
non-resolvable ones whose children may have been resolved). This is
exactly the right set: these MVars are visited in outer mode, so their
normalized values are suitable for storing in the mctx. MVars below the
cut are visited in inner mode, so their intermediate values cannot be
written back.
### Pass 2 scope-tracked caching
A `scope_cache` data structure ensures that sharing is preserved even
across different delayed-assigned MVars (and hence with different
substitutions), when possible. Each `visit_delayed` call pushes a new
scope with fresh fvar bindings. The cache correctly handles cross-scope
reuse, fvar shadowing, and late-binding via generation counters and
scope-level tracking.
The `scope_cache` has been formally verified:
`tests/elab/scopeCacheProofs.lean` contains a complete Lean proof that
the lazy generation-based implementation refines the eager
specification, covering all operations (push, pop, lookup, insert)
including the rewind lazy cleanup with scope re-entry and degradation.
The key correctness invariant is inter-entry gen list consistency
(GensConsistent), which, unlike per-entry alignment with `currentGens`,
survives pop+push cycles.
### Behavioral differences from original `instantiateMVars`
The implementation matches the original single-pass `instantiateMVars`
behavior with one cosmetic difference: the new implementation
substitutes fvars inline during traversal rather than constructing
intermediate beta-redexes, producing more beta-reduced terms in some
edge cases. This changes the pretty-printed output for two elab tests
(`1179b`, `depElim1`) but all terms remain definitionally equal.
### Tests
Correctness and performance tests for the new implementation were added
in #12808.
### Files
- `src/library/instantiate_mvars.cpp` — C++ implementation of both
passes (replaces `src/kernel/instantiate_mvars.cpp`)
- `src/library/scope_cache.h` — scope-aware cache data structure
- `src/Lean/MetavarContext.lean` — exported accessors for
`DelayedMetavarAssignment` fields
- `tests/elab/scopeCacheProofs.lean` — formal verification of
`scope_cache` correctness
- `tests/elab/1179b.lean.out.expected`,
`tests/elab/depElim1.lean.out.expected` — updated expected output
Co-authored-by: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This PR changes the way the linting for `linter.unusedSimpArgs` gets the
value from the environment. This is achieved by using the appropriate
helper functions defined in `Lean.Linter.Basic`.
The following now compiles without warning
```lean4
set_option linter.all false in
example : True := by simp [False]
```
Fixes#12559
This PR constructs SizeOf instances directly in SizeOf spec theorem
generation.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This PR makes the `omit`, `unusedSectionVars` and `loopingSimpArgs`
linters respect the `linter.all` option:
when `linter.all` is set to false (and the respective linter option is
unset), the linter should not report errors.
Similarly to #12559, these linters should honour the linter.all flag
being set to false. These are all remaining occurrences of this pattern.
This fixes an issue analogous to #12559.
This PR and #12560 fix all occurrences of this pattern. (The only
question is around `RCases.linter.unusedRCasesPattern`: should this also
respect this? I have left this alone for now.)
Co-authored-by: fiforeach <249703130+fiforeach@users.noreply.github.com>
This PR modifies `#eval e` to elaborate `e` with section variables in
scope. While evaluating expressions with free variables is not possible,
this lets `#eval` give a better error message than "unknown identifier."
Example:
```lean
section
variable (n : Nat)
/-- error: Cannot evaluate, contains free variable `n` -/
#guard_msgs in #eval n
end
```
The error is localized to `#eval`. It would be more friendly if the
error were to be placed on uses of free variables.
[Zulip
discussion](https://leanprover.zulipchat.com/#narrow/channel/270676-lean4/topic/Unknown.20identifier.20error.20messages.20for.20.60.23eval.60/near/560864544)
This PR changes the elaboration of the `structure`/`class` commands so
that default values have later fields in context as well. This allows
field defaults to depend on fields that come both before and after them.
While this was already the case for inherited fields to some degree, it
now applies uniformly to all fields. Additionally, when elaborating the
default value for a field, all fields that depend on it are cleared from
the context to avoid situations where the default value depends on
itself.
This addresses an issue reported by Aaron Liu [on
Zulip](https://leanprover.zulipchat.com/#narrow/channel/270676-lean4/topic/default.20structure.20values.20can.20depend.20on.20themselves/near/578014370).
This PR changes "structure-like" terminology to "non-recursive
structure" across internal documentation, error messages, the
metaprogramming API, and the kernel, to clarify Lean's type theory. A
*structure* is a one-constructor inductive type with no indices — these
can be created by either the `structure` or `inductive` commands — and
are supported by the primitive `Expr.proj` projections. Only
*non-recursive* structures have an eta conversion rule. The PR
description contains the APIs that were renamed.
Addresses RFC #5891, which proposed this rename. The change is motivated
by the need to distinguish between `structure`-defined structures,
structures, and non-recursive structures. Especially since #5783, which
enabled the `structure` command to define recursive structures,
"structure-like" has been easy to misunderstand.
Changes:
- Kernel: `is_structure_like()` -> `is_non_rec_structure()`
- `Lean.isStructureLike` -> `Lean.isNonRecStructure`
- `Lean.matchConstStructLike` -> `Lean.matchConstNonRecStructure`
- `Lean.getStructureLikeCtor?` -> `Lean.getNonRecStructureCtor?`
- `Lean.getStructureLikeNumFields` -> `Lean.getNonRecStructureNumFields`
- `Lean.Expr.proj`: extended and corrected documentation (note: despite
the fact that not every projection can be written as a recursor
application, I left in this claim since it seems good to document a
more-restrictive specification, and some users have requested the kernel
be more restrictive in this way)
Closes#5891
This PR changes the default behavior of the `restoreAllArtifacts`
package configuration to mirror that of the workspace. If the workspace
also has it unset, the default remains the same (`false`).
This PR changes Lake to only emit `.nobuild` traces (introduced in
#12076) if the normal trace file already exists. This fixes an issue
where a `lake build --no-build` would create the build directory and
thereby prevent a cloud release fetch in a future build.
This PR migrates most remaining tests to the new test suite. It also
completes the migration of directories like `tests/lean/run`, meaning
that PRs trying to add tests to those old directories will now fail.
This PR solves three distinct issues with the handling of
`ite`/`dite`,`decide`.
1) We prevent the simprocs from picking up `noncomputable`, `Classical`
instances, such as `Classical.propDecidable`, when simplifying the
proposition in `ite`/`dite`/`decide`.
2) We fix a type mismatch occurring when the condition/proposition is
unchanged but the `Decidable` instance is simplified.
3) If we rewrite the proposition from `c` to `c'` and the evaluation of
the original instance `Decidable c` gets stuck we try fallback path of
of obtaining `Decidable c'` instance and evaluating it. This matters
when the instance is evaluated via `cbv_eval` lemmas.
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This PR implements a merge sort algorithm on arrays. It has been
measured to be about twice as fast as `List.mergeSort` for large arrays
with random elements, but for small or almost sorted ones, the list
implementation is faster. Compared to `Array.qsort`, it is stable and
has O(n log n) worst-case cost. Note: There is still a lot of potential
for optimization. The current implementation allocates O(n log n)
arrays, one per recursive call.
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This PR adjusts the module parser to set the leading whitespace of the
first token to the whitespace up to that token. If there are no actual
tokens in the file, the leading whitespace is set on the final (empty)
EOI token. This ensures that we do not lose the initial whitespace (e.g.
comments) of a file in `Syntax`.
(Tests generated/adjusted by Claude)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This PR replaces three independent name demangling implementations
(Lean, C++, Python) with a single source of truth in
`Lean.Compiler.NameDemangling`. The new module handles the full
pipeline: prefix parsing (`l_`, `lp_`, `_init_`, `initialize_`,
`lean_apply_N`, `_lean_main`), postprocessing (suffix flags, private
name stripping, hygienic suffix stripping, specialization contexts),
backtrace line parsing, and C exports via `@[export]`.
The C++ runtime backtrace handler now calls the Lean-exported functions
instead of its own 792-line reimplementation. This is safe because
`print_backtrace` is only called from `lean_panic_impl` (soft panics),
not `lean_internal_panic`.
The Python profiler demangler (`script/profiler/lean_demangle.py`) is
replaced with a thin subprocess wrapper around a Lean CLI tool,
preserving the `demangle_lean_name` API so downstream scripts work
unchanged.
**New files:**
- `src/Lean/Compiler/NameDemangling.lean` — single source of truth (483
lines)
- `tests/lean/run/demangling.lean` — comprehensive tests (281 lines)
- `script/profiler/lean_demangle_cli.lean` — `c++filt`-style CLI tool
**Deleted files:**
- `src/runtime/demangle.cpp` (792 lines)
- `src/runtime/demangle.h` (26 lines)
- `script/profiler/test_demangle.py` (670 lines)
Net: −1,381 lines of duplicated C++/Python code.
🤖 Prepared with Claude Code
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This PR adds tests and a benchmark exercising `instantiateMVars` on
metavariable assignment graphs with nested delayed assignments, in
preparation for optimizing the delayed mvar resolution path.
- `tests/elab/instantiateMVarsShadow.lean`: Two test cases for
correctness when the same fvar is bound to different values at different
scope levels (fvar shadowing and late-bind patterns). A buggy cache
could return a stale result from one scope level in another.
- `tests/elab/instantiateMVarsSharing.lean`: Verifies correct resolution
and object sharing on a graph with nested delayed mvars producing `∀ s,
(s = s → (s = s) ∧ (s = s)) ∧ (s = s)`.
- `tests/elab_bench/delayed_assign.lean`: Constructs an O(n²) delayed
mvar graph (n=700) and measures `instantiateMVars` resolution time,
calibrated to ~1s total elaboration.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This PR adds a warning to any `def` of class type that does not also
declare an appropriate reducibility.
The warning check runs after elaboration (checking the actual
reducibility status via `getReducibilityStatus`) rather than
syntactically checking modifiers before elaboration. This is necessary
to accommodate patterns like `@[to_additive (attr :=
implicit_reducible)]` in Mathlib, where the reducibility attribute is
applied during `.afterCompilation` by another attribute, and would be
missed by a purely syntactic check.
---------
Co-authored-by: Paul Reichert <6992158+datokrat@users.noreply.github.com>
Co-authored-by: Kim Morrison <kim@tqft.net>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This PR moves the universe-level-count check from
`unfold_definition_core` into `is_delta`, establishing the invariant
that if `is_delta` succeeds then `unfold_definition` also succeeds. This
prevents a crash (SIGSEGV or garbled error) that occurred when call
sites in `lazy_delta_reduction_step` unconditionally dereferenced the
result of `unfold_definition` even on a level-parameter-count mismatch.
Additionally, moves the `is_prop` check for theorem types in
`add_theorem` to occur after `check_constant_val`, so the type is
verified to be well-formed before `is_prop` evaluates it. This prevents
`is_prop` from being called on an ill-typed term when a malformed
theorem declaration is supplied.
Fixes#10577.
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: nomeata <148037+nomeata@users.noreply.github.com>
This PR reverts https://github.com/leanprover/lean4/pull/12757.
We suspect this caused the v4.29.0-rc5 tag CI to fail. All 6 test jobs
on the tag CI (run
https://github.com/leanprover/lean4/actions/runs/22699133179) are
failing with:
```
PANIC at _private.Lean.Environment.0.Lean.EnvExtension.getStateUnsafe Lean.Environment:1425:6:
called on `async` extension, must set `asyncDecl` or pass `(asyncMode := .local)` to explicitly access local state
```
29 tests fail, affecting deriving, grind, linter, interactive, and pkg
tests. The v4.29.0-rc4 tag CI passed, and the only code changes between
rc4 and rc5 are this PR and
https://github.com/leanprover/lean4/pull/12782. The failure only
manifests in release builds (with `LEAN_VERSION_IS_RELEASE=1` and
`CHECK_OLEAN_VERSION=ON`).
🤖 Prepared with Claude Code
This PR changes Lake to use the modification times of traces (where
available) for artifact modification times.
When artifacts are hard-linked from the cache, they retain the
modification time of the artifact in the cache. Thus, the artifact
modification time is an unreliable metric for determining whether an
artifact is up-to-date relative to other artifacts in the presence of
the cache. The trace file, however, is modified consistently when the
artifacts are updated, making it the most reliable indicator of
modification time.
This PR fixes a false positive in `release_checklist.py` where the check
for the dev cycle being started would fail even when it was correctly
set up.
The script was looking for `set(LEAN_VERSION_IS_RELEASE 0)` as an exact
prefix match, but CMakeLists.txt uses the CMake cache variable form:
`set(LEAN_VERSION_IS_RELEASE 0 CACHE STRING "")`. The fix uses a regex
that handles both syntaxes.
This was discovered during the v4.29.0-rc4 release when the checklist
incorrectly reported that a "begin dev cycle" PR was needed, even though
PR #12526 had already set `LEAN_VERSION_IS_RELEASE 0` and
`LEAN_VERSION_MINOR 30` on master.
🤖 Prepared with Claude Code
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This PR marks `Id.run` as `[implicit_reducible]` to ensure that
`Id.instMonadLiftTOfPure` and `instMonadLiftT Id` are definitionally
equal when using `.implicitReducible` transparency setting.
This PR takes a more principled approach in deriving `String` pattern
lemmas by reducing to simpler cases similar to how the instances are
defined.
This reduces duplication of complex arguments (at the expense of having
to state more simple lemmas; however these lemmas are useful to users as
well).
This PR adds a `set_option cbv.maxSteps N` option that controls the
maximum
number of simplification steps the `cbv` tactic performs. Previously the
limit
was hardcoded to the `Sym.Simp.Config` default of 100,000 with no way
for
users to override it. The option is threaded through `cbvCore`,
`cbvEntry`,
`cbvGoal`, and `cbvDecideGoal`.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This PR moves cbv tests to the correct test directories. `cbv4.lean` is
a
straightforward elaboration test and is moved to `tests/elab/`. The AES
and ARM
load/store tests are performance-oriented stress tests and are moved to
`tests/elab_bench/`.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This PR makes the compiler removes arguments to join points that are
void, avoiding a bunch of dead
stores in the bytecode and the initial C (though LLVM was surely able to
optimize these away further
down the line already).
This PR introduces the core HTTP data types: `Request`, `Response`,
`Status`, `Version`, and `Method`. Currently, URIs are represented as
`String` and headers as `HashMap String (Array String)`. These are
placeholders, future PRs will replace them with strict implementations.
This contains the same code as #10478, divided into separate pieces to
facilitate easier review.
The pieces of this feature are:
- Core data structures: #12126
- Headers: #12127
- URI: #12128
- Body: #12144
- H1: #12146
- Server: #12151
- Client:
---------
Co-authored-by: Rob23oba <152706811+Rob23oba@users.noreply.github.com>
This PR skips the noncomputable pre-check in `processDefDeriving` when
the instance type is `Prop`. Since proofs are erased by the compiler,
computability is irrelevant for `Prop`-valued instances.
Previously (since https://github.com/leanprover/lean4/pull/12756),
`deriving instance` would reject instances that transitively depend on
noncomputable definitions, even when the class extends `Prop`. This came
up in mathlib where `Precoverage.IsStableUnderBaseChange` (a `Prop`
class) needs `deriving noncomputable instance` unnecessarily.
🤖 Prepared with Claude Code
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This PR fixes `release_steps.py` for `verso`. After running `lake
update` in the root, the `test-projects/*/lake-manifest.json` files
retain stale subverso pins, causing verso's "SubVerso version
consistency" CI check to fail. The fix syncs the root manifest's
subverso rev into all test-project sub-manifests.
Root cause: verso has nested Lake projects in `test-projects/` each with
their own `lake-manifest.json`. Running `lake update` in the root
updates the root manifest but doesn't touch the nested ones.
🤖 Prepared with Claude Code
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This PR adds high priority to instances for `OfSemiring.Q` in the grind
ring envelope. When Mathlib is imported, instance synthesis for types
like `OfSemiring.Q Nat` becomes very expensive because the solver
explores many irrelevant paths before finding the correct instances. By
marking these instances as high priority and adding shortcut instances
for basic operations (`Add`, `Sub`, `Mul`, `Neg`, `OfNat`, `NatCast`,
`IntCast`, `HPow`), instance synthesis resolves quickly.
🤖 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>
This PR fixes `release_checklist.py` to report failing CI checks
immediately, even when other checks are still in progress. Previously,
having any in-progress checks would return `"pending"` status, masking
failures that had already occurred. Now it returns `"failure"` with a
message like `"1 check(s) failing, 2 still in progress"`.
Also adds a section to `.claude/commands/release.md` instructing the AI
assistant to investigate any CI failure immediately rather than
reporting it as "in progress" and moving on.
🤖 Prepared with Claude Code
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This PR fixes a parsing bug in \`release_checklist.py\` introduced by
https://github.com/leanprover/lean4/pull/12700, which reformatted
\`src/CMakeLists.txt\` to use \`CACHE STRING \"\"\`:
\`\`\`cmake
set(LEAN_VERSION_MINOR 30 CACHE STRING "")
\`\`\`
The old code used \`split()[-1].rstrip(")")\` to extract the version
number, which now yields \`""\` (the empty string argument) instead of
the minor version. Use a regex to extract the digit directly.
🤖 Prepared with Claude Code
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This PR adds user-facing API lemmas for `s.contains t`, where `s` and
`t` are both a string or a slice.
Under the hood these lemmas are backed by the correctness proof for KMP
that was added a few weeks ago.
This PR fixes a CMake scoping bug that made `-DLEAN_VERSION_*` overrides
ineffective.
The version variables (`LEAN_VERSION_MAJOR`, `MINOR`, `PATCH`,
`IS_RELEASE`) were declared with plain `set()`, which creates normal
variables that shadow cache variables set by `-D` on the command line.
The fix changes them to `CACHE STRING ""` to match the existing
`LEAN_SPECIAL_VERSION_DESC` pattern.
However, `CACHE STRING ""` alone isn't sufficient because `project(LEAN
CXX C)` implicitly creates empty `LEAN_VERSION_{MAJOR,MINOR,PATCH}`
normal variables (CMake sets `<PROJECT>_VERSION_*` for the project
name). These shadow the cache values, so we `unset()` them after the
cache declarations to let `${VAR}` fall through to the cache.
Closes https://github.com/leanprover/lean4/issues/12681🤖 Prepared with Claude Code
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This PR replaces `lean.code-workspace` with standard `.vscode/`
configuration
files (`settings.json`, `tasks.json`, `extensions.json`). The workspace
file
required users to explicitly "Open Workspace from File" (and moreover
gives a
noisy prompt whether or not they want to open it), while `.vscode/`
settings
are picked up automatically when opening the folder. This became
possible after
#12652 reduced the workspace to a single folder.
Also drops the `rewrap.wrappingColumn` markdown setting, as the Rewrap
extension
is no longer signed on the VS Code marketplace.
🤖 Prepared with Claude Code
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This PR deprecates `levelZero` in favor of `Level.zero` and `levelOne`
in favor of the new `Level.one`, and updates all usages throughout the
codebase. The `levelZero` alias was previously required for computed
field `data` to work, but this is no longer needed.
🤖 Prepared with Claude Code
This PR adds general projection lemmas for `ExceptConds` conjunction:
- `ExceptConds.and_elim_left`: `(x ∧ₑ y) ⊢ₑ x`
- `ExceptConds.and_elim_right`: `(x ∧ₑ y) ⊢ₑ y`
The existing `and_true`, `true_and`, `and_false`, `false_and` are
refactored as one-line corollaries.
Suggested by @sgraf812 in
https://github.com/leanprover-community/cslib/pull/376#discussion_r2066993469.
🤖 Prepared with Claude Code
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
MacOS uses a very old version of bash where `"${FOO[@]}"` fails if `set
-u` is enabled and `FOO` is undefined. Newer versions of bash expand
this to zero arguments instead.
Also, `lint.py` used the shebang `#!/usr/bin/env python` instead of
`python3`, which fails on some systems.
In CI, all macos tests run on nscloud runners. Presumably, they have
installed newer versions of various software, hence this didn't break in
CI.
This PR fixes `@[implicit_reducible]` on well-founded recursive
definitions.
`addPreDefAttributes` sets WF-recursive definitions as `@[irreducible]`
by default, skipping this only when the user explicitly wrote
`@[reducible]` or `@[semireducible]`. It was missing
`@[instance_reducible]` and `@[implicit_reducible]`, causing those
attributes to be silently overridden.
Add `instance_reducible` and `implicit_reducible` to the check in
`src/Lean/Elab/PreDefinition/Mutual.lean` that guards against overriding
user-specified reducibility attributes, and add regression tests in
`tests/elab/wfirred.lean`.
## Example
```lean
-- Before fix: printed @[irreducible] def f : List Nat → Nat
-- After fix: printed @[implicit_reducible] def f : List Nat → Nat
@[instance_reducible] def f : ∀ _l : List Nat, Nat
| [] => 0
| [_x] => 1
| x :: y :: l => if h : x = y then f (x :: l) else f l + 2
termination_by l => sizeOf l
#print sig f
```
Fixes#12775
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: nomeata <148037+nomeata@users.noreply.github.com>
This PR provides a `ForwardPatternModel` for string patterns and deduces
theorems and lawfulness instances from the corresponding results for
slice patterns.
This PR fixes an inconsistency in `getStuckMVar?` where the instance
argument to class projection functions and auxiliary parent projections
was not whnf-normalized before checking for stuck metavariables. Every
other case in `getStuckMVar?` (recursors, quotient recursors, `.proj`
nodes) normalizes the major argument via `whnf` before recursing — class
projection functions and aux parent projections were the exception.
This bug was identified by Matthew Jasper. When the instance parameter
to a class projection is not normalized, `getStuckMVar?` may fail to
detect stuck metavariables that would be revealed by whnf, or conversely
may report stuckness for expressions that would reduce to constructors.
This caused issues with `OfNat` and `Zero` at
`with_reducible_and_instances` transparency.
Note: PR #12701 (already merged) is also required to fix the original
Mathlib examples.
This PR adds `at` location syntax to the `cbv` tactic, matching the
interface of `simp at`. Previously `cbv` could only reduce the goal
target; now it supports `cbv at h`, `cbv at h |-`, and `cbv at *`.
`cbvGoal` is rewritten to use `Sym.preprocessMVar` followed by `cbvCore`
within a single `SymM` context, sharing the term table across all
hypotheses and the target. The old `cbvGoalCore` (which reduced one side
of an equation goal at a time) is replaced by a general approach that
reduces arbitrary goal types and hypothesis types, with special handling
for `True` targets and `False` hypotheses. `cbvDecideGoal` is updated to
use the extracted `cbvCore` as well.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This PR adds a dedicated cbv simproc for `Decidable.decide` that
directly matches on `isTrue`/`isFalse` instances, producing simpler
proof terms and avoiding unnecessary unfolding through `Decidable.rec`.
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This PR generalizes `String.Slice.Pos.cast`, which turns an `s.Pos` into
a `t.Pos`, to no longer require `s = t`, but merely `s.copy = t.copy`.
This is a breaking change, but one that is easy to adapt to, by
replacing `proof` with `congrArg Slice.copy proof` where required.
This PR adds `deriving noncomputable instance Foo for Bar` syntax so
that delta-derived instances can be marked noncomputable. Previously,
when the underlying instance was noncomputable, `deriving instance`
would fail with an opaque async compilation error.
Now:
- `deriving noncomputable instance Foo for Bar` marks the generated
instance as noncomputable (using `addDecl` + `addNoncomputable` instead
of `addAndCompile`)
- `deriving instance Foo for Bar` pre-checks for noncomputable
dependencies and gives an actionable error with a "Try this:" suggestion
pointing to the noncomputable variant
- For handler-based deriving (inductives/structures), `noncomputable`
sets `isNoncomputable` on the scope
The `optDefDeriving` and `optDeriving` trailing parsers are updated with
`notSymbol "noncomputable"` to prevent them from stealing the parse of
`deriving noncomputable instance ...`.
🤖 Prepared with Claude Code
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This PR enables Lake to download artifacts from a remote cache service
on demand as part of a `lake build`. It also refactors much of the cache
API to be more type safe.
The newly documented `lake cache add` command loads input-to-output
mappings from a file and stores them in the cache with optional
information about which cache service and what scope they come from.
With this information, Lake can now download artifacts on demand during
a `lake build`.
The `lake cache get` command has also changed its default behavior to
download just the input-to-outputs mapping and then lazily fetch
artifacts from Reservoir as part of a `lake build`. The original eager
behavior can be forced via the new `--download-arts` option.
This PR using `StateT.run` rather than the "defeq abuse" of function
application. There remain many places where we still use function
application for `ReaderT`, but I've updated this in the touched files.
(To really solve this, we would make `StateT` irreducible, but that is
not happening here.)
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This PR ensures `linter.all` disables `constructorNameAsVariable`.
The issue was discovered by @eric-wieser while investigating a quote4
issue.
This seems like an easy mistake to make when setting up a new linter,
and perhaps we need a better structure to make it easy to do the right
thing.
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This PR replaces the `isImplicitReducible` check with `Meta.isInstance`
in the `shouldInline` function within `inlineCandidate?`.
At the base phase, we skip inlining instances tagged with
`[inline]`/`[always_inline]`/`[inline_if_reduce]` because their local
functions will be lambda lifted during the base phase. The goal is to
keep instance code compact so the lambda lifter can extract
cheap-to-inline declarations. Inlining instances prematurely expands the
code and creates extra work for the lambda lifter — producing many
additional lambda-lifted closures.
The previous check used `isImplicitReducible`, which does not capture
the original intent: some `instanceReducible` declarations are not
instances. `Meta.isInstance` correctly targets only actual type class
instances. Although `Meta.isInstance` depends on the scoped extension
state, this is safe because `shouldInline` runs during LCNF compilation
at `addDecl` time — any instance referenced in the code was resolved
during elaboration when the scope was active, and LCNF compilation
occurs before the scope changes.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This PR changes the approach in `simpIteCbv` and `simpDIteCbv`, by
replacing call to `Decidable.decide`
with reducing and direct pattern matching on the `Decidable` instance
for `isTrue`/`isFalse`. This produces simpler proof terms.
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This PR adds pre-pass simprocs `simpOr` and `simpAnd` to the `cbv`
tactic that evaluate only the left argument of `Or`/`And` first,
short-circuiting when the result is determined without evaluating the
right side. Previously, `cbv` processed `Or`/`And` via congruence, which
always evaluated both arguments. For expressions like `decide (m < n ∨
expensive)`, when `m < n` is true, the expensive right side is now
skipped entirely.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This PR provides injectivity lemmas for `List.getElem`, `List.getElem?`,
`List.getElem!` and `List.getD` as well as for `Option`. Note: This
introduces a breaking change, changing the signature of
`Option.getElem?_inj`.
This PR adds a section to the /release command explaining how to use `gh
pr checks --watch` to wait for CI or merges without polling.
🤖 Prepared with Claude Code
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This PR fixes `pp.fvars.anonymous` to display loose free variables as
`_fvar._` instead of `_` when the option is set to `false`. This was the
intended behavior in https://github.com/leanprover/lean4/pull/12688 but
the fix was committed locally and not pushed before that PR was merged.
🤖 Prepared with Claude Code
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This PR switches four lightweight workflows from `pull_request` to
`pull_request_target` to stop GitHub from requiring manual approval when
the
`mathlib-lean-pr-testing[bot]` app triggers label events (e.g. adding
`builds-mathlib`). Since the bot never lands commits on master, it is
perpetually treated as a "first-time contributor" and every
`pull_request`
event it triggers requires approval. `pull_request_target` events always
run
without approval because they execute trusted code from the base branch.
This is safe for all four workflows because none check out or execute
code
from the PR branch — they only read labels, PR body, and file lists from
the
event payload and API:
- `awaiting-mathlib.yml` — checks label combinations
- `awaiting-manual.yml` — checks label combinations
- `pr-body.yml` — checks PR body formatting
- `check-stdlib-flags.yml` — checks if stdlib_flags.h was modified via
API
Also adds explicit `permissions: pull-requests: read` to each workflow
as a
least-privilege hardening measure, since `pull_request_target` has
access to
secrets.
Addresses the issue reported by Sebastian:
https://lean-fro.zulipchat.com/#narrow/channel/398861-general/topic/mathlib.20pr-testing.20breakage.3F/near/575084348🤖 Prepared with Claude Code
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This PR changes the shebang in `lean_profile.sh` from `#!/bin/bash` to
`#!/usr/bin/env bash` so the script works on NixOS and other systems
where bash is not at `/bin/bash`, and adds a Claude Code skill pointing
to the profiler documentation.
🤖 Prepared with Claude Code
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This PR gives the `generate` function's "apply @Foo to Goal" trace nodes
their own trace sub-class `Meta.synthInstance.apply` instead of sharing
the parent `Meta.synthInstance` class.
This allows metaprograms that walk synthesis traces to distinguish
instance application attempts from other synthesis nodes by checking
`td.cls` rather than string-matching on the header text.
The new class is registered with `inherited := true`, so `set_option
trace.Meta.synthInstance true` continues to show these nodes.
Motivated by mathlib's `#defeq_abuse` diagnostic tactic
(https://github.com/leanprover-community/mathlib4/pull/35750) which
currently checks `headerStr.contains "apply"` to identify these nodes.
See
https://leanprover.zulipchat.com/#narrow/channel/113488-general/topic/backward.2EisDefEq.2ErespectTransparency🤖 Prepared with Claude Code
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This PR adds a `pp.fvars.anonymous` option (default `true`) that
controls the display of loose free variables (fvars not in the local
context).
- When `true` (default), loose fvars display their internal name like
`_fvar.42`
- When `false`, they display as `_fvar._`
This is analogous to `pp.mvars.anonymous` for metavariables. It's useful
for stabilizing output in `#guard_msgs` when messages contain fvar IDs
that vary between runs — for example, in diagnostic tools that report
`isDefEq` failures from trace output where the local context is not
available.
🤖 Prepared with Claude Code
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This PR fixes a gap in how `@[implicit_reducible]` is assigned to parent
projections during structure elaboration.
When `class C extends P₁, P₂` has diamond inheritance, some ancestor
structures become constructor subobject fields even though they aren't
direct parents. For example, in `Monoid extends Semigroup, MulOneClass`,
`One` becomes a constructor subobject of `Monoid` — its field `one`
doesn't overlap with `Semigroup`'s fields, and `inSubobject?` is `none`
during `MulOneClass` flattening.
`mkProjections` creates the projection `Monoid.toOne` but defers
reducibility to `addParentInstances` (guarded by `if !instImplicit`).
However, `addParentInstances` only processes direct parents from the
`extends` clause. Grandparent subobject projections fall through the gap
and stay `semireducible`.
This causes defeq failures when `backward.isDefEq.respectTransparency`
is enabled (#12179): at `.instances` transparency, the semireducible
grandparent projection can't unfold, so two paths to the same ancestor
structure aren't recognized as definitionally equal.
Fix: before `addParentInstances`, iterate over all `.subobject` fields
and set `implicitReducible` on those whose parent is a class.
🤖 Prepared with Claude Code
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
The linter was running in parallel with other tests, which were creating
and deleting files. Since the linter was iterating over some files and
directories at the time, it crashed.
This PR relates `String.split` to `List.splitOn` and `List.splitOnP`,
provided that we are splitting by a character or character predicate.
Also included: some more lemmas about `List.splitOn`, and a refactor of
the generic `split` verification to get rid of the awkward `SlicesFrom`
constuct.
This PR ensures the compiler extracts `Array`/`ByteArray`/`FloatArray`
literals as one big closed term to avoid quadratic overhead at closed
term initialization time.
This PR changes the spec lookup procedure in Sym-based mvcgen so that
1. Spec candidates are sorted first before being filtered
2. Instead of filtering the whole set of candidates using
`spec.pattern.match?`, we take the first match with the highest
priority.
The second point means we will do a lot fewer matches when the highest
priority spec matches immediately. In this case, the one match is still
partially redundant with the final application of the backward rule
application. It would be great if could somehow specialize the backward
rule after it has been created. Still, this yields some welcome
speedups. Before and after for each.
```
vcgen_add_sub_cancel:
goal_1000: 865 ms, 1 VCs by grind: 228 ms, kernel: 435 ms
goal_1000: 540 ms, 1 VCs by grind: 229 ms, kernel: 426 ms
vcgen_ping_pong:
goal_1000: 458 ms, 0 VCs, kernel: 431 ms
goal_1000: 454 ms, 0 VCs, kernel: 443 ms (unchanged, because there is only ever one candidate spec)
vcgen_deep_add_sub_cancel:
goal_1000: 986 ms, 1 VCs by grind: 234 ms, kernel: 735 ms
goal_1000: 728 ms, 1 VCs by grind: 231 ms, kernel: 708 ms
vcgen_reader_state:
goal_1000: 746 ms, 1 VCs by sorry: 1 ms, kernel: 803 ms
goal_1000: 525 ms, 1 VCs by sorry: 1 ms, kernel: 840 ms
```
This PR fixes a bug in `Meta.zetaReduce` where `have` expressions were
not being zeta reduced. It also adds a feature where applications of
local functions are beta reduced, and another where zeta-delta reduction
can be disabled. These are all controllable by flags:
- `zetaDelta` (default: true) enables unfolding local definitions
- `zetaHave` (default: true) enables zeta reducing `have` expressions
- `beta` (default: true) enables beta reducing applications of local
definitions
Closes#10850
This PR adds support for generating and discharging postcondition VCs in
Sym-based `mvcgen`. It also adds a new benchmark case
`vcgen_ping_pong.lean` that tests this functionality. This benchmark
required a more diligent approach to maintain maximal sharing in goal
preprocessing. Goal preprocessing was subsequently merged into the main
VC generation function.
This PR changes the order of implicit parameters `α` and `ps` such that
`α` consistently comes before `ps` in `PostCond.noThrow`,
`PostCond.mayThrow`, `PostCond.entails`, `PostCond.and`, `PostCond.imp`
and theorems.
This PR ports the simple ground expression extraction pass from IR to
LCNF.
I locally confirmed that this produces no diff between stage1/stage2 at
the C level (apart from the
changed compiler files) so this should essentially be binary equivalent.
This PR upstreams `List.splitOn` and `List.splitOnP` from
Batteries/mathlib.
The function `splitOnP.go` is factored out to `splitOnPPrepend`, because
it is useful to state induction hypotheses in terms of
`splitOnPPrepend`.
This PR ports the expand reset/reuse pass from IR to LCNF. In addition
it prevents exponential code generation unlike the old one. This results
in a ~15% decrease in binary size and slight speedups across the board.
The change also removes the "is this reset actually used" syntactic
approximation as the previous passes guarantee (at the moment) that all
uses are in the continuation and will thus be caught by this.
This PR enables the module system, in cooperation with the linker, to
separate meta and non-meta code in native binaries. In particular, this
ensures tactics merely used in proofs do not make it into the final
binary. A simple example using `meta import Lean` has its binary size
reduced from 130MB to 1.7MB.
# Breaking change
`importModules (loadExts := true)` must now be preceded by
`enableInitializersExecution`. This was always the case for correct
importing but is now enforced and checked eagerly.
2026-02-26 08:05:19 +00:00
4359 changed files with 45162 additions and 13641 deletions
Benchmarks are also run as tests. Use the `TEST_BENCH` environment variable (unset in tests, set to `1` in benchmarks) to scale problem sizes:
- In `compile_bench``.init.sh` files: check `$TEST_BENCH` and set `TEST_ARGS` accordingly
- In `elab_bench` Lean files: use `(← IO.getEnv "TEST_BENCH") == some "1"` to switch between small (test) and large (bench) inputs
See `tests/README.md` for the full benchmark writing guide.
## Testing stage 2
When requested to test stage 2, build it as follows:
```
make -C build/release stage2 -j$(nproc)
```
Stage 2 is *not* automatically invalidated by changes to `src/` which allows for faster iteration
when fixing a specific file in the stage 2 build but for invalidating any files that already passed
the stage 2 build as well as for final validation,
```
make -C build/release/stage2 clean-stdlib
```
must be run manually before building.
## New features
When asked to implement new features:
@@ -40,6 +73,10 @@ When asked to implement new features:
- ONLY use the project's documented build command: `make -j$(nproc) -C build/release`
- If a build is broken, ask the user before attempting any manual cleanup
## stage0 Is a Copy of src
**Never manually edit files under `stage0/`.** The `stage0/` directory is a snapshot of `src/` produced by `make update-stage0`. To change anything in stage0 (CMakeLists.txt, C++ source, etc.), edit the corresponding file in `src/` and let `update-stage0` propagate it.
## LSP and IDE Diagnostics
After rebuilding, LSP diagnostics may be stale until the user interacts with files. Trust command-line test results over IDE diagnostics.
@@ -121,6 +121,42 @@ The nightly build system uses branches and tags across two repositories:
When a nightly succeeds with mathlib, all three should point to the same commit. Don't confuse these: branches are in the main lean4 repo, dated tags are in lean4-nightly.
## CI Failures: Investigate Immediately
**CRITICAL: If the checklist reports `❌ CI: X check(s) failing` for any PR, investigate immediately.**
Do NOT:
- Report it as "CI in progress" or "some checks pending"
- Wait for the remaining checks to finish before investigating
- Assume it's a transient failure without checking
DO:
1. Run `gh pr checks <number> --repo <owner>/<repo>` to see which specific check failed
2. Run `gh run view <run-id> --repo <owner>/<repo> --log-failed` to see the failure output
3. Diagnose the failure and report clearly to the user: what failed and why
4. Propose a fix if one is obvious (e.g., subverso version mismatch, transient elan install error)
The checklist now distinguishes `❌ X check(s) failing, Y still in progress` from `🔄 Y check(s) in progress`.
Any `❌` in CI status requires immediate investigation — do not move on.
## Waiting for CI or Merges
Use `gh pr checks --watch` to block until a PR's CI checks complete (no polling needed).
Run these as background bash commands so you get notified when they finish:
description: Profile Lean programs with demangled names using samply and Firefox Profiler. Use when the user asks to profile a Lean binary or investigate performance.
Requires `samply` (`cargo install samply`) and `python3`.
## Agent Notes
- The pipeline is interactive (serves to browser at the end). When running non-interactively, run the steps manually instead of using the wrapper script.
- The three steps are: `samply record --save-only`, `symbolicate_profile.py`, then `serve_profile.py`.
-`lean_demangle.py` works standalone as a stdin filter (like `c++filt`) for quick name lookups.
- The `--raw` flag on `lean_demangle.py` gives exact demangled names without postprocessing (keeps `._redArg`, `._lam_0` suffixes as-is).
- Use `PROFILE_KEEP=1` to keep the temp directory for later inspection.
- The demangled profile is a standard Firefox Profiler JSON. Function names live in `threads[i].stringArray`, indexed by `threads[i].funcTable.name`.
If you want to make changes to Lean itself, start by [building Lean](../make/index.md) from a clean checkout to make sure that everything is set up correctly.
After that, read on below to find out how to set up your editor for changing the Lean source code, followed by further sections of the development manual where applicable such as on the [test suite](testing.md) and [commit convention](commit_convention.md).
After that, read on below to find out how to set up your editor for changing the Lean source code,
followed by further sections of the development manual where applicable
such as on the [test suite](../../tests/README.md) and [commit convention](commit_convention.md).
If you are planning to make any changes that may affect the compilation of Lean itself, e.g. changes to the parser, elaborator, or compiler, you should first read about the [bootstrapping pipeline](bootstrap.md).
You should not edit the `stage0` directory except using the commands described in that section when necessary.
@@ -61,10 +63,10 @@ you can then put `my_name/lean4:my-tag` in your `lean-toolchain` file in a proje
### VS Code
There is a `lean.code-workspace` file that correctly sets up VS Code with workspace roots for the stage0/stage1 setup described above as well as with other settings.
You should always load it when working on Lean, such as by invoking
There is a `.vscode/` directory that correctly sets up VS Code with settings, tasks, and recommended extensions.
Simply open the repository folder in VS Code, such as by invoking
It describes the old test suite, which is currently in the process of being replaced.
The new test suite's documentation can be found at [`tests/README.md`](../../tests/README.md).
After [building Lean](../make/index.md) you can run all the tests using
```
cd build/release
make test ARGS=-j4
```
Change the 4 to the maximum number of parallel tests you want to
allow. The best choice is the number of CPU cores on your machine as
the tests are mostly CPU bound. You can find the number of processors
on linux using `nproc` and on Windows it is the `NUMBER_OF_PROCESSORS`
environment variable.
You can run tests after [building a specific stage](bootstrap.md) by
adding the `-C stageN` argument. The default when run as above is stage 1. The
Lean tests will automatically use that stage's corresponding Lean
executables
Running `make test` will not pick up new test files; run
```bash
cmake build/release/stage1
```
to update the list of tests.
You can also use `ctest` directly if you are in the right folder. So
to run stage1 tests with a 300 second timeout run this:
```bash
cd build/release/stage1
ctest -j 4 --output-on-failure --timeout 300
```
Useful `ctest` flags are `-R <name of test>` to run a single test, and
`--rerun-failed` to run all tests that failed during the last run.
You can also pass `ctest` flags via `make test ARGS="--rerun-failed"`.
To get verbose output from ctest pass the `--verbose` command line
option. Test output is normally suppressed and only summary
information is displayed. This option will show all test output.
## Test Suite Organization
All these tests are included by [src/shell/CMakeLists.txt](https://github.com/leanprover/lean4/blob/master/src/shell/CMakeLists.txt):
- [`tests/lean`](https://github.com/leanprover/lean4/tree/master/tests/lean/): contains tests that come equipped with a
.lean.expected.out file. The driver script [`test_single.sh`](https://github.com/leanprover/lean4/tree/master/tests/lean/test_single.sh) runs
each test and checks the actual output (*.produced.out) with the
checked in expected output.
- [`tests/lean/run`](https://github.com/leanprover/lean4/tree/master/tests/lean/run/): contains tests that are run through the lean
command line one file at a time. These tests only look for error
codes and do not check the expected output even though output is
produced, it is ignored.
**Note:** Tests in this directory run with `-Dlinter.all=false` to reduce noise.
If your test needs to verify linter behavior (e.g., deprecation warnings),
explicitly enable the relevant linter with `set_option linter.<name> true`.
- [`tests/lean/interactive`](https://github.com/leanprover/lean4/tree/master/tests/lean/interactive/): are designed to test server requests at a
given position in the input file. Each .lean file contains comments
that indicate how to simulate a client request at that position.
using a `--^` point to the line position. Example:
```lean,ignore
open Foo in
theorem tst2 (h : a ≤ b) : a + 2 ≤ b + 2 :=
Bla.
--^ completion
```
In this example, the test driver [`test_single.sh`](https://github.com/leanprover/lean4/tree/master/tests/lean/interactive/test_single.sh) will simulate an
auto-completion request at `Bla.`. The expected output is stored in
a .lean.expected.out in the json format that is part of the
- [`tests/plugin`](https://github.com/leanprover/lean4/tree/master/tests/plugin/): tests that compiled Lean code can be loaded into
`lean` via the `--plugin` command line option.
## Writing Good Tests
Every test file should contain:
* an initial `/-! -/` module docstring summarizing the test's purpose
* a module docstring for each test section that describes what is tested
and, if not 100% clear, why that is the desirable behavior
At the time of writing, most tests do not follow these new guidelines yet.
For an example of a conforming test, see [`tests/lean/1971.lean`](https://github.com/leanprover/lean4/tree/master/tests/lean/1971.lean).
## Fixing Tests
When the Lean source code or the standard library are modified, some of the
tests break because the produced output is slightly different, and we have
to reflect the changes in the `.lean.expected.out` files.
We should not blindly copy the new produced output since we may accidentally
miss a bug introduced by recent changes.
The test suite contains commands that allow us to see what changed in a convenient way.
First, we must install [meld](http://meldmerge.org/). On Ubuntu, we can do it by simply executing
```
sudo apt-get install meld
```
Now, suppose `bad_class.lean` test is broken. We can see the problem by going to [`tests/lean`](https://github.com/leanprover/lean4/tree/master/tests/lean) directory and
executing
```
./test_single.sh -i bad_class.lean
```
When the `-i` option is provided, `meld` is automatically invoked
whenever there is discrepancy between the produced and expected
outputs. `meld` can also be used to repair the problems.
In Emacs, we can also execute `M-x lean4-diff-test-file` to check/diff the file of the current buffer.
To mass-copy all `.produced.out` files to the respective `.expected.out` file, use `tests/lean/copy-produced`.
We claim this unsafe implementation is correct because an array cannot have more than `usizeSz` elements in our runtime.
We claim this unsafe implementation is correct because an array cannot have more than `USize.size` elements in our runtime.
This kind of low level trick can be removed with a little bit of compiler support. For example, if the compiler simplifies `as.size < usizeSz` to true. -/
This kind of low level trick can be removed with a little bit of compiler support. For example, if the compiler simplifies `as.size < USize.size` to true. -/
Some files were not shown because too many files have changed in this diff
Show More
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.