chore: bump Lean toolchain to v4.32.2 - #30
Merged
Conversation
Exactly two commits touch `src/kernel` in v4.31.0..v4.32.2, and both are fixes: * leanprover/lean4#14498, "fix: kernel to check opaque values for fvars", adds a `check_no_metavar_no_fvar` call to `environment::add_opaque`. `addOpaque` now makes the same call in the same position, between `checkConstantVal` and the `checkType` of the value. * leanprover/lean4#14577, "fix: missing check at kernel inductive declaration", is already mirrored: `Lean4Lean/Inductive/Add.lean` type checks the nested applications against the post-declaration environment, covered by `Lean4Lean.Tests.NestedInductive`. lean4lean had the #14498 bug verbatim, not merely by transcription. `addOpaque` runs `checkConstantVal` and the value's `checkType` inside one `M.run`, so they share one `TypeChecker.State` exactly as C++ shares one `type_checker`. A type that beta-reduces to `False` but whose inference pushes a free variable through the local context leaves that variable in `inferTypeI`; the value can then name it, inference answers from the cache rather than the popped local context, and the declaration is accepted. The leaked variable is `_kernel_fresh.2`, the same name as in the lean4 test for this issue, since `TypeChecker.State.ngen` uses the same prefix and starts at the same index. `Lean4Lean.Tests.OpaqueFVar` pins both directions and is a real regression test: with the `checkNoMVarNoFVar` line removed it fails with "opaque value containing a free variable was accepted". The rest of the diff is not a kernel change. leanprover/lean4#13305 made the new `do` elaborator the default in v4.32.0, which reshapes the terms that `do` notation produces. The `Verify` proofs are written against the legacy shape, and `Lean4Lean/Experimental/ShapeLogRel.lean` proves things about `Option`-monad `do`/`return` definitions the same way, so those four files pin `backward.do.legacy true` with a comment saying why. That keeps the elaborated implementation identical to v4.31.0, which is the conservative choice for a kernel. Migrating the proofs to the new elaborator is follow-up work. Also refresh the toolchain-pinned `Lean.Level` divergence link, and make `stringProof` a `theorem` for the new `linter.defProp`. Validated with `lake build`, `lake build Lean4Lean.Experimental`, and both CI replay commands: `lean4lean Init.Core` checked 1036 declarations and `lean4lean --fresh Init.System.IO` checked 43721. No statement changes and no new `sorry`s. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RFCAQgoGN4ndJA1rxNx9RE
kim-em
force-pushed
the
agent/bump-v4.32.2
branch
from
August 4, 2026 00:07
78ddec2 to
7afe199
Compare
`check_no_metavar_no_fvar` is called on the value in three places in the C++ kernel: `add_definition` (safe branch, environment.cpp:184), `add_theorem` (:203), and, since leanprover/lean4#14498, `add_opaque` (:217). This branch added the third; the first two were removed deliberately, and `divergences.md` recorded them as redundant. That argument is wrong, and all three are soundness bugs. A free variable in the value is harmless only while inference always consults the local context, and inference also answers from its cache: the type and then the value are checked by the same `TypeChecker.State`, exactly as C++ shares one `type_checker`, so a declaration whose *type* is inferred by pushing a free variable through the local context leaves that variable's type in `inferTypeI`. The value can then name the variable, inference answers from the cache instead of the already popped local context, and the declaration is accepted. With a type that beta-reduces to `False`, the result is a proof of `False` -- confirmed against the v4.31.0 tree, where `theorem Bad : (fun _ => False) id := _kernel_fresh.2` was accepted and `theorem FalseFromBad : False := Bad` then went through on top of it. `addTheorem` is the more serious of the two, being both the common path and, unlike the opaque case, not an upstream bug: lean4 has always had this call. `Lean4Lean.Tests.DeclFVar` replaces `Tests.OpaqueFVar` and pins all three sites, in both directions, and asserts that the rejection comes from the free variable check rather than from some other path, so that it cannot quietly stop testing the cache route. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This was referenced Aug 4, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR bumps the Lean toolchain to v4.32.2, mirrors the one kernel change in the interval that lean4lean did not already have, and pins the legacy
doelaborator in the four files whose proofs depend on the shapedonotation elaborates to.git log v4.31.0..v4.32.2 -- src/kernellists exactly two commits, and the diff over that range touches onlyenvironment.cpp(1 insertion) andinductive.cpp(15 insertions, 3 deletions). Both are fixes, and both are covered:fix: kernel to check opaque values for fvars, in v4.32.1 and v4.32.2. It adds
check_no_metavar_no_fvar(*this, v.get_name(), v.get_value())toenvironment::add_opaque, betweencheck_constant_valandchecker.check.Lean4Lean.addOpaquenow callscheckNoMVarNoFVar env v.name v.valuein exactly that position.fix: missing check at kernel inductive declaration, in v4.32.2 only. Already mirrored on master:
Lean4Lean/Inductive/Add.leantype checks the nested applicationsI Dsagainst the post-declaration environment, usingResult.lctxfor C++'sm_params_lctx, andLean4Lean.Tests.NestedInductivepins that an ill-typed dropped parameter is rejected. No change needed here.lean4lean had the #14498 bug verbatim rather than by loose transcription, so this is a real fix and not a formality.
addOpaquerunscheckConstantValand the value'scheckTypeinside a singleM.run, so both share oneTypeChecker.Stateexactly as the C++ kernel shares onetype_checker. A type that beta-reduces toFalsebut whose inference pushes a free variable through the local context leaves that variable's type ininferTypeI. The value can then name the variable: inference answers from the cache instead of the already popped local context, and the declaration is accepted with typeFalse. The leaked variable is_kernel_fresh.2, the same name the lean4 test for this issue uses, becauseTypeChecker.State.ngenhas the same prefix and starts at the same index.Lean4Lean.Tests.OpaqueFVarpins both directions and discriminates the fix: with thecheckNoMVarNoFVarline removed it fails with "opaque value containing a free variable was accepted", and with it present the build is clean.Worth flagging separately, because it is live on master right now: the same hole exists in
addTheoremand in the safe branch ofaddDefinition, where it is not a lean4 bug at all.add_theoremandadd_definitionhave calledcheck_no_metavar_no_fvaron the value for a long time, and lean4lean does not. The identical cache-priming declaration is accepted as a theorem and as a safe definition. Verify front-end declaration checking already adds both calls, so this PR deliberately leaves them alone rather than widening scope, but that PR is still a draft.The remainder of the diff is not a kernel change. feat: make new do elaborator the default flipped
backward.do.legacytofalsein v4.32.0, which reshapes the termsdonotation produces: join points becomehave __do_jpinstead of thelets thatextract_letsdestructures, andifchains are inlined.Lean4Lean/TypeChecker.lean,Lean4Lean/EquivManager.leanandLean4Lean/Verify/TypeChecker/Reduce.leanare affected because theVerifyproofs pin the elaborated shape of the implementation, andLean4Lean/Experimental/ShapeLogRel.leanbecause it proves things aboutOption-monaddo/returndefinitions the same way. Each of those four files now setsbackward.do.legacy truewith a comment saying why. This keeps the elaborated implementation identical to v4.31.0, which is the conservative choice for a kernel, and it is the whole of the fix: the single pin inShapeLogRel.leancleared all 38 errors in that file. Migrating the proofs to the new elaborator is follow-up work and a much larger change, tracked in Migrate theVerifyproofs off the legacydoelaborator, which each pin points at.Also refresh the toolchain-pinned
Lean.Leveldivergence link, whose claim is about the current toolchain, and makestringProofatheoremfor the newlinter.defProp. fix: alignLean/Level.leanandkernel/level.cppnormalization rules is still open, so that divergence note stands.Validated with
lake build,lake build Lean4Lean.Experimental, and both CI replay commands:lean4lean Init.Corechecked 1036 declarations andlean4lean --fresh Init.System.IOchecked 43721. No statement changes and no newsorrys.🤖 Prepared with Claude Code