This PR fixes an `AppBuilder` exception in the `cbv` tactic when
simplifying projections whose projection function is dependent (closes
#12457).
Previously, `handleProj` unconditionally used `mkCongrArg` to prove `e.i
= e'.i` from `e = e'`, but `mkCongrArg` requires a non-dependent
function. For dependent projections (e.g., `fun x => x.2 : (x :
String.Slice) → x.1.Pos`), this would fail.
Now, `handleProj` first checks whether the projection function type is
non-dependent (a simple arrow). If so, it proceeds with `mkCongrArg` as
before. Otherwise, it falls back to:
1. Attempting to reduce the projection directly.
2. If reduction fails, using a heterogeneous congruence lemma
(`mkHCongr`) converted to an equality via `mkEqOfHEq`, provided the
original and rewritten struct are definitionally equal.
This PR implements two changes to LRAT checking in `bv_decide`:
1. The LRAT trimmer previously used to drop delete instructions as we
did not act upon them in a meaningful way (as explained in 2). Now it
figures out the earliest point after which a clause may be deleted in
the trimmed LRAT proof and inserts a deletion there.
2. The LRAT checker takes in an `Array IntAction` and explodes it into
an `Array DefaultClauseAction` before passing it into the checking loop.
`DefaultClauseAction` has a much larger memory footprint compared to
`IntAction`. Thus materializing the entire proof as
`DefaultClauseAction` upfront consumes a lot of memory. In the adapted
LRAT checker we take in an `Array IntAction` and only ever convert the
step we are currently working on to a `DefaultClauseAction`. In
combination with the fact that we now insert deletion instructions this
can drastically reduce memory consumption.
In SMT-LIB's 20210312-Bouvier/vlsat3_a11.smt2 memory consumption went
from 8GB+ to 3.7GB through this combination of changes.
This PR fixes two issues discovered during the first test of the revised
nightly release workflow
(https://github.com/leanprover/lean4/pull/12461):
**1. Date logic:** The `workflow_dispatch` path used `date -u +%F`
(current UTC date) to find the base nightly to revise. If the most
recent nightly was from yesterday (e.g. `nightly-2026-02-12`) but UTC
has rolled over to Feb 13, the code would look for `nightly-2026-02-13`,
not find it, and create a fresh nightly instead of a revision. Now finds
the latest `nightly-*` tag via `sort -rV` and creates a revision of
that.
**2. Mathlib trigger:** The "Update toolchain on mathlib4's
nightly-testing branch" step was broken in two ways:
- Workflow renamed: `nightly_bump_toolchain.yml` →
`nightly_bump_and_merge.yml` (leanprover-community/mathlib4#34827)
- `MATHLIB4_BOT` PAT expired after mathlib migrated to GitHub Apps
(leanprover-community/mathlib4#34751)
- Replace with `actions/create-github-app-token` using the
`mathlib-nightly-testing` app, matching the pattern used in mathlib4's
own workflows.
🤖 Prepared with Claude Code
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This adds the ancillary materials for the IJCAR 2026 grind paper to
`doc/examples/IJCAR2026/`.
- `examples.lean`: interactive examples from the paper
- `analyze_grind_loc.py`: script used for the evaluation section
(analyzing grind adoption LoC changes in mathlib)
🤖 Prepared with Claude Code
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This PR adds support for manually re-releasing nightlies when a build
issue or critical fix requires it. When a `workflow_dispatch` triggers
the nightly release job and a `nightly-YYYY-MM-DD` tag already exists,
the CI now creates `nightly-YYYY-MM-DD-rev1` (then `-rev2`, etc.)
instead of silently skipping.
### Lake `ToolchainVer`
- Extend `ToolchainVer.nightly` with an optional `rev : Option Nat`
field
- Parse `-revK` suffixes from nightly tags in `ofString`
- Ordering: `nightly-YYYY-MM-DD` < `nightly-YYYY-MM-DD-rev1` < `-rev2` <
`nightly-YYYY-MM-DD+1`
- Round-trip: `toString (ofString s) == s` for both variants
### CI workflow
- "Set Nightly" step probes existing tags on `workflow_dispatch` to find
next available `-revK`
- Scheduled nightlies retain existing behavior (skip if commit already
tagged)
- Changelog grep updated from `nightly-[-0-9]*` to `nightly-[^ ,)]*` to
match `-revK` suffixes
### `lean-bisect`
- Updated `NIGHTLY_PATTERN` regex, sort key, error messages, and help
text
### Companion PRs
- https://github.com/leanprover-community/mathlib4/pull/35220: update
`nightly_bump_and_merge.yml` tag grep and `nightly_detect_failure.yml`
warning message
-
https://github.com/leanprover-community/leanprover-community.github.io/pull/787:
update `tags_and_branches.md` documentation
🤖 Prepared with Claude Code
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This PR bounds-checks `constants.size` directly instead of
`constNames.size` in `LazyDiscrTree.loadImportedModule`, eliminating the
unsafe `constants[i]!` access and the intermediate `constNames[i]`
lookup. The constant name is obtained from `constInfo.name` instead.
🤖 Prepared with Claude Code
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This PR verifies all of the `String` iterators except for the bytes
iterator by relating them to `String.toList`.
Along the way we define `String.posLE` and `String.posLT` analogously to
`String.posGE` and `String.posGT` and redefine `String.prev` to go
through `String.posLT`.
We also define and verify `String.positionsFrom` and
`String.revPositionsFrom`, which are the obvious generaliziations of
`String.positions` and `String.revPositions` starting at a positions
other than the start/end.
Finally, we get various lemmas about strings and positions, including
some nice induction principles `String.Pos.next_induction` and
`String.Pos.prev_induction`.
Of course, we also have all of the analogous results for `String.Slice`.
This is a mitigation for the fact that the upfront noncomputable checker
currently doesn't error out early enough in certain situations so we
violate invariants later on.
This PR adds a simplification rule for `Task.get (Task.pure x) = x` into
the LCNF simplifier. This
ensures that we avoid touching the runtime for a `Task` that instantly
gets destructed anyways.
This PR changes the way artifacts are transferred from the local Lake
cache to a local build path. Now, Lake will first attempt to hard link
the local build path to artifact in the cache. If this fails (e.g.,
because the cache is on a different file system or drive), it will
fallback to pre-existing approach of copying the artifact. Lake also now
marks cache artifacts as read-only to avoid corrupting the cache by
writing to a hard linked artifact.
Lake will also hard link binary artifacts into the cache. If this fails,
it will similarly fall back to copying them. Text artifacts are always
copied, not linked, as the line endings in the cache copy are
normalized.
This PR adds two more benchmarks for the Sym-based mvcgen prototype in
the style of `add_sub_cancel`.
The first is `deep_add_sub_cancel`, which is like `add_sub_cancel` but
with a much deeper monad stack:
```lean
abbrev M := ExceptT String <| ReaderT String <| ExceptT Nat <| StateT Nat <| ExceptT Unit <| StateM Unit
```
By specializing the specs for `get` and `set`, we get competitive
performance:
```
goal_100: 180.365086 ms, kernel: 79.634989 ms
goal_200: 313.465611 ms, kernel: 187.808631 ms
goal_300: 478.278585 ms, kernel: 270.210634 ms
goal_400: 638.884320 ms, kernel: 380.381127 ms
goal_500: 759.802772 ms, kernel: 472.662882 ms
goal_600: 933.575180 ms, kernel: 649.040746 ms
goal_700: 1174.367200 ms, kernel: 759.470010 ms
goal_800: 1298.866482 ms, kernel: 864.420171 ms
goal_900: 1475.315552 ms, kernel: 1008.662783 ms
goal_1000: 1627.957444 ms, kernel: 1078.627830 ms
```
Recall that `add_sub_cancel` had `goal_1000: 824.476962 ms, kernel:
477.069045 ms`, but that doesn't need to repeatedly unwrap 3 layers of
the monad.
The second benchmark is `get_throw_set`. Its kernel is
```lean
def step (lim : Nat) : ExceptT String (StateM Nat) Unit := do
let s ← get
if s > lim then
throw "s is too large"
set (s + 1)
def loop (n : Nat) : ExceptT String (StateM Nat) Unit := do
match n with
| 0 => pure ()
| n+1 => loop n; step n
def Goal (n : Nat) : Prop := ⦃fun s => ⌜s = 0⌝⦄ loop n ⦃⇓_ s => ⌜s = n⌝⦄
```
It will generate `n+1` VCs. We get `n` VCs of the form
```
s✝ : Nat
_ : ¬0 < s✝
...
_ : n < s✝ + 1 ...<n times>... + 1
⊢ ⌜s✝ = 0⌝ ⊢ₛ ⌜False⌝ (s✝ + ...<n times>...)
```
and one VC of the form
```
⌜s✝ = 0⌝ ⊢ₛ ⌜s✝ + 1 + <n times> ... + 1 = n⌝
```
which can be discharged by `grind`, but presently are discharged with
`sorry`.
Statistics:
```
goal_100: 209.435869 ms, kernel: 128.768919 ms
goal_200: 386.639441 ms, kernel: 482.244717 ms
goal_300: 559.795137 ms, kernel: 1251.777405 ms
goal_400: 753.243978 ms, kernel: 3020.878177 ms
goal_500: 1014.939522 ms, kernel: 5182.120327 ms
goal_600: 1229.173622 ms, kernel: 9296.551442 ms
goal_700: 1410.024180 ms, kernel: 16655.954682 ms
goal_800: 1684.059305 ms, kernel: 32065.951705 ms
goal_900: 1905.602401 ms, kernel: 55299.942894 ms
goal_1000: 2172.823244 ms, kernel: 84082.492485 ms
```
Need to look at kernel times here, but tactic time looks about alright.
Using `grind` to discharge just `n=100` goals took 8s.
This PR uses `getImpureSignature?` instead of the `findEnvDecl` from IR
in the LCNF compiler. We
were previously still relying on the IR function because only IR
contained proper borrow
annotations. Now we infer the borrow annotations on the LCNF level and
can thus use the LCNF
signatures.
This PR adds the attribute `@[univ_out_params]` for specifying which
universe levels should be treated as output parameters. By default, any
universe level that does not occur in any input parameter is considered
an output parameter.
This PR verifies the `String.Slice.splitToSubslice` function by relating
it to a model implementation `Model.split` based on a
`ForwardPatternModel`.
The proof is generic, so it works for splitting by characters, strings
etc.
From this, we will be able to give user-facing API lemmas for
`String.split` and friends in future PRs.
We also move the verification of string patterns from
`String.Slice.Pattern` to `String.Slice.Pattern.Model` to achieve better
separation between code that users run in their programs and code that
only supports the theory.
This PR changes the proof for `Char.toUpper` to make it easier to
typecheck for external checkers (nanoda in particular).
---------
Co-authored-by: Joachim Breitner <mail@joachim-breitner.de>
This PR removes the uses of `shared_timed_mutex` that were introduced
because we were stuck on C++14
with the `shared_mutex` available from C++17 and above.
This PR adds the lemma `Acc.inv_of_transGen`, a generalization of
`Acc.inv`. While `Acc.inv` shows that `Acc r x` implies `Acc r y` given
that `r y x`, the new lemma shows that this also holds if `y` is only
*transitively* related to `x`.
This PR sets the `irreducible` attribute before generating the equations
for recursive definitions. This prevents these equations to be marked as
`defeq`, which could lead to `simp` generation proofs that do not type
check at default transparency.
This issue is surfacing more easily since well-founded recursion on
`Nat` is implemented with a dedicated fix point operator (#7965). Before
that, `WellFounded.fix` was used, which is inherently not reducing, so
we did get the desired result even without the explicit reducibility
setting.
Fixes#12398.
This PR refactors the main loop of the `cbv` tactic. Rather than using
multiple simprocs, a central pre simproc is introduced. Moreover, let
expressions are no longer immediately zeta-reduced due to performance on
one of the benchmarks (`leroy.lean`).
Stacked on top of #12416
This PR introduces `Rat.abs` and adds missing lemmas about `Int` and
`Rat`.
For `Int`:
- Lemmas about the interaction of negation and order: `neg_le_iff`,
`le_neg_iff`, `neg_lt_iff`, `lt_neg_iff`
For `Rat`:
- Declaration of `Rat.abs`
- Lemmas for `Rat.abs`, `Rat.floor` and `Rat.ceil`
- More basic lemmas that would all be provable with `grind` but might
still be good to have in the library
- Type class instances: `Std.Associative`, `Std.Commutative`,
`Std.LawfulIdentity` for addition
This PR adds a finishing `decide_cbv` tactic, which applies
`of_decide_eq_true` and then tries to discharge the remaining goal using
`cbv`.
Stacked on top of #12408.
---------
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
This PR adds caching to `replaceRecApps`, the procedure responsible for
replacing recursive applications for wellfounded recursion, improving
performance when many references to the same recursive call exist, e.g.
when recursive calls exist in proof terms.
Closes#12404
---------
Co-authored-by: Joachim Breitner <mail@joachim-breitner.de>
This PR adds a user facing `cbv` tactic that can be used outside of the
`conv` mode.
Example usage:
```lean4
example : "hello" ++ " " ++ "world" = "hello world" := by cbv
```
---------
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
This PR gives a proof of `LawfulToForwardSearcherModel` for `Slice`
patterns, which amounts to proving that our implementation of KMP is
correct.
Note that this PR also changes the KMP implementation to make it
slightly more efficient and easier to verify. I also have a correctness
proof for the old implementation, so there were no bugs in the old
implementation.
This PR fixes a bug in `mvcgen` caused by incomplete `match` splitting.
In particular, if a program `match s with ...` matches on a state
variable `s` (presumably the result of a call to `get`), then `s` will
also occur in the stateful goal `H ⊢ₛ wp⟦match s with ...⟧ Q s`
*outside* the program expression; this was not anticipated before.
This PR adds `LawfulOrderOrd` instances for `Nat`, `Int`, and all
fixed-width integer types (`Int8`, `Int16`, `Int32`, `Int64`, `ISize`,
`UInt8`, `UInt16`, `UInt32`, `UInt64`, `USize`). These instances
establish that the `Ord` instances for these types are compatible with
their `LE` instances. Additionally, this PR adds a few missing lemmas
and `grind` patterns.
This PR changes the alters the file format of outputs stored in the
local Lake cache to include an identifier indicating the service (if
any) the output came from. This will be used to enable lazily
downloading artifacts on-demand during builds.
This PR adds comprehensive test running instructions to
`.claude/CLAUDE.md`,
replacing the single `test_single.sh` example with the full range of
test
commands documented in `doc/dev/testing.md`: full suite, filtered by
name,
rerun failures, and direct ctest usage.
🤖 Prepared with Claude Code
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This PR is similar to #12403.
We previously conjectured that "all type class fields that are types
should be marked as reducible." The problem is that propositions are
types, but they are also used as data (with `Decidable`). For example,
we often see the proposition `x <= y` as a Boolean. So, we refined the
conjecture to:
"All type class fields that are types (and not propositions) should be
marked as reducible."