Skip to content

Bump Lean toolchain to v4.31.0 - #29

Merged
digama0 merged 1 commit into
digama0:masterfrom
kim-em:agent/bump-v4.31.0
Aug 3, 2026
Merged

Bump Lean toolchain to v4.31.0#29
digama0 merged 1 commit into
digama0:masterfrom
kim-em:agent/bump-v4.31.0

Conversation

@kim-em

@kim-em kim-em commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Updates Lean and Batteries to v4.31.0.

Lean kernel changes

The direct v4.30.0..v4.31.0 diff under src/kernel contains only three non-semantic edits:

  • a typo correction in a comment in declaration.h (declations to declarations)
  • noexcept on the type_checker move constructor declaration
  • the matching noexcept on its definition

There are therefore no trusted type-checking or reduction changes to mirror in lean4lean.

Compatibility updates

Lean 4.31 rejects non-portable module names, so Lean4Lean.Std.Variable! is renamed to Lean4Lean.Std.VariableBang. A handful of proofs also now require explicit unfolding or simplification hints where Lean 4.30's simplifier reduced the expressions automatically; their statements and proof strategy are unchanged.

The versioned Lean.Level source link in divergences.md is also refreshed to v4.31.0.

Validation

  • lake build (146 jobs)
  • lake exe lean4lean Init.Core (1,036 declarations)
  • lake exe lean4lean --fresh Init.System.IO (43,602 declarations)

🤖 prepared with assistance from Codex

The v4.30.0..v4.31.0 src/kernel diff has no semantic changes: it fixes one comment typo and adds noexcept to the type_checker move constructor declaration and definition.

Rename the non-portable Variable! module and make simplifier-dependent proofs explicit for Lean 4.31. Refresh the versioned Lean.Level divergence link.

Validated with lake build and both CI replay commands.
@kim-em
kim-em force-pushed the agent/bump-v4.31.0 branch from ae29fbf to af60c9a Compare August 3, 2026 02:56
@digama0
digama0 marked this pull request as ready for review August 3, 2026 04:06
@digama0
digama0 merged commit 095c0a9 into digama0:master Aug 3, 2026
1 check passed
digama0 added a commit that referenced this pull request Aug 3, 2026
`Experimental` is not a default target, so nothing built it. Two rounds of
breakage had accumulated unnoticed:

* `7842f38` moved the `Std.Basic` prelude into `namespace Lean4Lean`, which
  makes `List.Forall₂.imp` and friends resolvable only under an explicit
  `open Lean4Lean` -- the enclosing namespace does not count for generalized
  field notation. Every `Theory`/`Verify` file got that `open`; the
  `Experimental` files did not, so eight call sites across five files stopped
  resolving. This already failed on v4.30.0.
* The v4.31.0 bump then broke eight more proofs, all cases of `simp` no longer
  unfolding a definition on its own: `VExpr.instL`/`VLevel.inst` in
  `Stronger.IsDefEqStrong.instL` (the same fix #29 applied to the `Theory`
  copy in `Strong.lean`), plus `HasTypePi`, `Pattern.WF`, `Nat` associativity
  and a `Sigma` eta step in `ShapeLogRel`.

`WShape.ctor'_join` was separately relying on a `by_contra` that does not
elaborate here (also already broken on v4.30.0); it is now `Classical.not_imp`,
which is core and needs no tactic import.

`SoundEq.rfl` is marked `protected` so that a bare `rfl` in that namespace
still means `Eq.refl`.

No statement changes and no new `sorry`s -- every edit is an `open`, a `simp`
hint, a `protected` marker, or a term-mode replacement for a tactic that no
longer applies.

`lake build Lean4Lean.Experimental` is now a CI step so this cannot rot
silently again.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
vasnesterov pushed a commit to vasnesterov/lean4lean that referenced this pull request Aug 23, 2026
…d a guard class the audit missed

**The systemic finding: a guard class §6/§7.1 did not account for.** Every
`@[extern]` C entry point in scope that takes a `Nat` begins with a
**`lean_is_scalar` bignum test -- before** the range test the audit
records. `LEAN_MAX_SMALL_NAT = SIZE_MAX >> 1`, so it fires at `2^63`.
§6 describes `lean_expr_instantiate_range` as "starts with
`if (b > e || e > sz) lean_internal_panic`" -- that is the function's
**second** statement, not its first. **Seven axioms in scope sit on that
guard**, and it resolves four different ways.

## #21 `Lean.Expr.liftLooseBVars_eq` is FALSE

    if (!lean_is_scalar(s) || !lean_is_scalar(d)) { lean_inc(e); return e; }

Witness `e := .bvar 0`, `s := 0`, `d := 2^63`. Both halves
machine-checked, deliberately **by different instruments**:

- C side, differential test on compiled code:
  `#eval idx ((Expr.bvar 0).liftLooseBVars 0 (2^63))` prints `"bvar 0"`.
- Model side, kernel reduction:
  `liftLooseBVars' (.bvar 0) 0 (2^63) = .bvar (2^63)`, and `≠ .bvar 0`.

So the axiom asserts `.bvar 0 = .bvar (2^63)`.

Worse than #26/digama0#27 in one specific way: **the call completes.** No
panic, no exotic input -- `2^63` is an ordinary literal and `.bvar 0` an
ordinary `Expr`. Only the *model's* output is not runtime-constructible,
which is why the two sides cannot be evaluated in one expression
(`#eval` of the model's answer trips `lean_expr_mk_data`'s own panic --
observed).

**Not an inconsistency**: `liftLooseBVars` is `opaque @[extern]` and the
toolchain has no core theorem about it, so there is no second fact to
contradict. Same category as #12/#17. Fix is `d < 2^63` or a `USize`
restatement -- frozen file, needs sign-off.

## Three failed attacks, recorded as failed attacks

- **#22, digama0#30** carry the same guard and **survive**: the C fallback
  coincides with the model on every input a real `Expr` can supply,
  with machine-checked agreement lemmas for both. One of the stream's
  own witnesses was wrong -- `(.bvar 7).lowerLooseBVars (2^63) 1`
  returns `bvar 7` from *both* sides -- and the correction is recorded
  rather than quietly dropped.
- **digama0#29 `abstractRange_eq`**, the only unconditional range axiom, also
  survives: its fallback uses `lean_array_size` and `Array.extract 0 n`
  clamps to the same thing, agreeing at **every** input, logical or not.
  Verdict unchanged, for a reason §7.1 did not state.

## #26/digama0#27: the side condition is necessary but NOT sufficient

`start ≤ stop ≤ subst.size` excludes the second guard, not the first --
`stop` may be non-scalar provided `subst.size` is too, which needs
`subst.size ≥ 2^63`: expressible, not constructible. Strictly weaker
than #21, no differential test possible. **Source reading only, and
marked as such.**

## The two other priority targets, answered

- **#17 `Expr.mkData_eq` -- no analogous consequence.**
  `lean_expr_mk_data` panics twice, and its hypothesis implies **both**
  are passed; the `approxDepth` clamp matches too. Unlike #12, correctly
  guarded.
- **#3 `PersistentArray.toList'_push` -- hypothesis adequate**, in the
  strongest available sense: `WF` is generated by `empty`/`push` only,
  and a reverse scan shows lean4lean uses **no other `PersistentArray`
  operation**. So `WF` is exactly the reachable set, not an
  approximation.

Not reached: #4-#7. **digama0#31's dependency on #15 deliberately not pursued**
-- #15 belongs to another stream's section, and a cross-section attack
run from one side only is the weaker version of the test.

**No `False` was derived.** §11.9 keeps the evidence strengths separate:
differential-test-plus-source, source-only, and proof are three
different things, and none of this is a proof.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants