These release notes describe a release candidate, not the final release. They may be incomplete and are subject to change.
Lean 4.32.0-rc1 (2026-06-17)
For this release, 102 changes landed. In addition to the 35 feature additions, and 20 fixes listed below, there were 7 refactoring changes, 2 documentation improvements, 9 performance improvements, 2 improvements to the test suite, and 27 other changes.
Language
-
#14039 fixes a bug where the builting docstring roles for asserting equalities did not properly highlight their contents for downstream consumers of rich docstring info, and exposes a structure that was mistakenly made private.
-
#14030 removes the hypothesis rewriting functionality of
cbv(introduced usingcbv atsyntax) in theSymmode. Additionally, this PR fixes the transparency level when dealing with projections incbv. -
#14025 adds
Lean.DoElemandLean.DoSeqas abbreviations forTSyntax `doElemandTSyntax `Lean.Parser.Term.doSeq, mirroringLean.Term, and uses them throughout thedoelaborator. -
#13961 adds a a
--record-exceptionsflag tolake lint, such that the definitions triggering builtin linting framework warnings will be silenced, by putting an appropriateset_optionflag. -
#13072 moves the status emoji from the stored trace header to the rendering layer.
withTraceNode/withTraceNodeBeforeno longer prependTraceResult.toEmojito the headerMessageData; instead,formatAuxandInteractiveDiagnosticprepend it when rendering.TraceResult.toEmojiis moved fromLean.Util.TracetoLean.Message(next to theTraceResultdefinition) so that both rendering paths can use it. -
#13868 adds
Lean.Environment.hasExposedBody— a small helper that asks "doesenvexport a body fornto downstream modules?". The idiom -
#13981 fixes a private inductive type not being usable as a namespace immediately after its definition.
-
#13970 makes the printed name of a variable in a
do-elaborator error message carry hover info so the infoview surfaces its type. The bulk of the change is a small refactor that introduces aMutVarstructure (declaration identifier + initialFVarId) and threads it through the do-elaborator helpers. -
#13954 prepares the
@[spec]attribute used bymvcgento support both specifications theorems for both new and oldmvcgen'meta theories. -
#13912 extends the
nestedActionparser (←insidedoblocks) to accept arbitrarydoElems after←instead of just terms. The newdoelaborator handles anydoElem; the legacy elaborator (set_option backward.do.legacy true) keeps the old restriction to terms and rejects more generaldoElems with an explicit error. -
#13931 introduces the
do← bodymarker (ASCIIdo<- body), which lets ordinary continuation-taking wrappers likewithReaderorMeta.withLocalDeclparticipate in the surroundingdoblock's control flow. Whendo← bodyappears as the last argument of an application inside adoblock, the body'sreturn,break,continue, andmut-variable reassignments are forwarded out through the wrapper to the enclosing block. -
#13852 adds builtin linter sets — linter sets registered from core Lean code during initialization, complementing the user-facing
register_linter_setcommand — and makeslinter.extraone of them. Enablinglinter.extra(e.g. viaset_option linter.extra trueorlake lint --extra) now activates the extra linters through the same set-membership mechanism as any other linter set. -
#13917 adds module linters, which run once at the end of elaborating a module rather than after every command. A module linter receives the full array of top-level command syntaxes for the module, making it suitable for checks that need a whole-module view (e.g. enforcing module-wide syntactic conventions) rather than per-command checks.
-
#13928 fixes a non-linearity in DiscrTree insertion, reducing the time it takes to
import Mathlibby ~10% -
#13916 fixes the silencing of
deprecatedwarnings inside of definitions that are themselves deprecated and usegrindwith adeprecatedtheorem. -
#13911 removes the
Lean.Parser.Term.liftMethodalias forLean.Parser.Term.nestedActionthat was kept for bootstrapping during the rename in #13910. Now that stage0 has been updated, the alias is no longer needed. -
#13910 renames the
liftMethodparser (the← <action>syntax insidedoblocks) and all of its associated helpers to use the more descriptive "nested action" terminology that the documentation already adopted. -
#13305 makes the new
doelaborator (#12459) the default by flippingbackward.do.legacytofalse. Legacy behavior remains available viaset_option backward.do.legacy true.
Library
-
#14054 upstreams
Nat.sqrtfrom Batteries and just enough theory from mathlib to characterize the function without having to expose its internals. -
#14051 cleans up the internal
Std.Internal.Doweakest-precondition library: the wp application lemmas and theTripleentailment field are renamed to follow the naming convention, the loop-invariant types are curried, and the monad spec lemmas reuse theTriplerules. -
#14048 adds a few lemmas about the way that
cpopandsetWidthinteract onBitVec. -
#3727 adds
BitVec.flattenList, which concatenates a list of bitvectors of a common width into a single bitvector, together with lemmas describing its bits:getLsbD_flattenListandgetMsbD_flattenListcompute an individual bit in terms of the corresponding element of the list, andextractLsb_flattenListdescribes extracting a contiguous range that falls within a single element. For efficient execution,flattenListis replaced at runtime via@[csimp]with a divide-and-conquer implementation costingO(n * L * log L)rather than theO(n * L²)of a naive left fold (≈900x faster at a million elements), while keepingO(log L)recursion depth so it remains stack-safe. -
#13458 adds
Nat.or_two_pow_eq_add_of_lt, a small missing bitwise lemma. -
#13459 adds some missing
ArrayandVectorset!convenience lemmas. -
#13865 adds lemmas to simplify sequencing with
pureinLawfulApplicative. -
#13988 removes
OPENSSL_init_sslfrominitialize_opensslso it helps with loading OpenSSL lazily. -
#13798 simplifies the
Std.TimeAPI by removing theDateTime (tz : TimeZone)and replacing it withZonedDateTimethat got renamed toDateTime. -
#12030 links OpenSSL
-
#13960 renames the
WPAdequatetypeclass toWPSoundto reflect that it encodes the directional soundness arrowwp x P → Internal.Ensures P x(not a bidirectional adequacy correspondence), and replaces theId-only*.of_wp_run_eqfamily with a uniform per-transformer soundness framework that works over any base monad withWPSound. -
#13908 deprecates the
Lean.RBMapandLean.RBTreecontainers in favour ofStd.TreeMapandStd.TreeSet, which offer a more complete and consistent API. Nothing in the Lean repository uses these types any longer, and downstream code should migrate to theStdcontainers. -
#13942 introduces
PersistentHashMap.alterin the same spirit asStd.HashMap.alter. -
#13938 adds tail-recursive
@[csimp]runtime replacements for the bounded-quantifierDecidableinstancesNat.decidableBallLT,Nat.decidableExistsLT, andNat.decidableExistsLT', so that running them no longer takes quadratic time or overflows the stack for largen. -
#13891 adds opt-in support for serializing closures (functions with captured values) to
.oleanfiles viaCompactedRegion.save (allowClosures := true), so a saved function can be loaded back and called, including from a separate process. Regular module data is unaffected and continues to reject closures.
Tactics
-
#14031 implements
SymMsimprocs for reducing bit-vector conversion operations. -
#14029 fixes a
Sym.dsimpbug that could produce an ill-typed term, causing the kernel to reject the resulting goal with errors such asapplication type mismatchorfunction expected. It triggered when alet/λ/∀binder's type or value referenced an earlier binder in the same telescope. -
#14022 fixes
grind?for goals containing hypotheses with inaccessible names. Distinct terms that differ only in inaccessible variables have identical anchors, so anchor references in generated tactic scripts could resolve to the wrong term during replay, producing emptygrind onlysuggestions and scripts that could not close the goal. Thecasestactic now supports anchor ordinal references (e.g.,cases #a56e/2selects the second candidate matching the anchor), andgrind?uses them to disambiguate colliding anchors. -
#14021 fixes an
unknown metavariableerror ingrindthat occurred when instantiatingmatch-congruence equations whose generalized-pattern equalities mention theorem parameters that cannot be determined by E-matching. -
#14020 fixes two bugs that made
grindconstruct proofs that are rejected by the kernel for goals involvingmatch-expressions with overlapping patterns and proof discriminants (#13773). -
#13971 makes the
cbvtactic available insidegrind's interactivesym =>mode. It reduces the goal target using call-by-value evaluation and supports the standardatlocation syntax (cbv at h,cbv at h ⊢,cbv at *) to reduce selected hypotheses, automatically closing equation goals viareflwhen reduction finishes the proof. -
#13983 adds
mvcgen' until $t, where$tis a conv-style pattern (holes_allowed); verification-condition generation stops as soon as the program matches the pattern, leaving it as a VC instead of applying a spec, similar to the existingstepLimitoption. -
#13925 consolidates
mvcgen''s syntax across tactic and grind (sym =>) modes. The grind-modewithclause is removed (use<;>instead), and the tactic-levelwithnow takes a single grind step that shares an E-graph withmvcgen'.mvcgen' invariants?(suggest mode) also works insidesym => …blocks. -
#13944 changes a
filterMapto amapinCNF.convertLRAT'so that tautological clauses becomenonein the array rather then being deleted. -
#13932 implements the
evalGrounddsimprocforSym.dsimp. -
#13621 fixes a bug in the
rcases-family tactics where the InfoView could give "unknown free variable" errors when the cursor was inside the pattern. It hoists applying the fvar substitution to giveaddTermInfo'andaddLocalVarInfothe correct expression. Previously, the substitution only happened inrfl/typed/tuple/alternative branches, which caused stale free variables to be recorded in the info tree. Repeated applications in recursive cases like.parenshould be fine, because the domain offsshould be old fvars and replacement exprs should only refer current-goal fvars, not old-domain fvars. Proof elaboration shouldn't be affected by this PR. -
#13909 makes the
intersperselibrary suggestion combinator honorratioat the endpoints, soratio = 0draws entirely fromselector₂andratio = 1entirely fromselector₁while both selectors still have results. Previously each element was chosen by comparing the achieved fraction ofselector₁contributions againstratiowith a strict<(seeded to0when empty), which leftratio = 1drawing one stray element fromselector₂before settling. The combinator now picks whichever candidate next state keeps the running fraction closest toratio, with ties going toselector₁. -
#13907 makes the
intersperselibrary suggestion combinator requestmaxSuggestionsresults from each of its two selectors instead of splitting the budget byratio, so that if one selector returns fewer suggestions than its allocation the other can compensate to still fill the request. The interspersing ratio and the finalmaxSuggestionscap on the combined result are unchanged. -
#13896 improves the support for universe constraints in the
SymMpattern matcher/unifier. Two new cases are supported -
#13887 factors the whnf-based projection step used inside
mvcgen''s head reducer into a newreduceProjAndUnfold?helper thatunfoldReducibles the projected field only when whnf reduced the structure. The outerSym.unfoldReduciblecall intryHeadReduceProgis no longer needed, so the per-step cost of normalizing abbrevs is proportional to the small unfolded instance body rather than the whole program expression. -
#13888 teaches
mvcgen'to register Triple-shaped local hypotheses as specs as they come into scope during VC generation. This is an existing feature ofmvcgen. -
#13883 teaches
mvcgen'to register Triple-shaped local hypotheses as specs as they come into scope during VC generation. This is an existing feature ofmvcgen. -
#13881 lets
mvcgen'decompose programs whose head is a typeclass method projection (e.g.Add.add inst a b) by reducing through the kernel projection to the instance body. -
#13880 reverts #13870 due to large performance regressions visible on radar that were not caught by benchmarking before merge.
-
#13878 fixes a bug when reducing
match-expressions in theSym.dsimptactic. -
#13870 lets
mvcgen'decompose programs whose head is a typeclass method projection (e.g.Add.add inst a b) by reducing through the kernel projection to the instance body.
Compiler
-
#14044 introduces constant folding for
Nat.reprFast. -
#13123 makes the task thread pool reclaim idle worker threads after 5 seconds of inactivity. Previously, pool threads were allocated on demand but never freed, which could waste significant memory given the new default 1GB stack size per thread.
-
#13991 adds constant folding for
USizeoperations that are already supported in other datatypes. It does so by checking whether the result of applying the operation is equivalent in bothUInt32andUInt64. Furthermore, it also adds constant folding operations for the most common bitwise operations. -
#13989 fixes a bug in the constant folding for
Boolwherein the compiler incorrectly determined 0-ary functions that participate in constant folding to be equal tofalse. -
#13974 adds constant folding for
USizerelation by evaluating them both in theUInt32andUInt64world and applying the fold if both worlds agree. -
#13926 makes
dbgTraceIfSharedprint the shared message in all non-linear situations. Previously it would only trigger ifRC > 1. However,RC = 0andRC < 0are also non-linearity triggers. -
#13924 fixes a code generator panic that occurred when a recursive definition (well-founded or structural) was marked by a
noncomputable sectionand then referenced from computable code. The compiler now reports a clean error, or accepts the second definition when everything occurs in anoncomputable section.
FFI
-
#13952 declares the
extern "C"parameter oflean_mk_bool_data_valueasuint8to match its@[export]ed Lean definition (where aBoolargument lowers touint8_tat the C ABI), fixing awasm32-emscripten/LTO ABI mismatch that trapped during module initialization.
Lake
-
#14060 has Lake deduplicate artifacts by their hash when uploading or downloading to the cache (e.g., in
lake cache putorlake cache get). This fixes possible errors whencurlwas asked to transfer to the same file and/or URL multiple times. -
#14036 refines when and how Lake decides to overwrite data while caching, and has Lake prefer outputs in a local trace file over those stored in the cache.
-
#13893 makes environment linters (the declaration-level checks run by
lake lint --builtin-lint) controlled byLean.Options, just like ordinary linters. Each environment linter is tied to a boolean option, so you enable or disable it per declaration withset_option linter.X false in ...and across a lint run with the newlake lint --linters=linter.X,-linter.Yflag. Using--lint-onlywith the same syntax will only collect information from the specified linters and will not run the default on linters. The previouslake lintflags--extra,--lint-all, and thebuiltin_nolintattribute, are removed in favour of this option-based control. -
#13949 adds a
LAKE_RESTORE_ARTIFACTSenvironment variable that overrides the workspace's defaultrestoreAllArtifactsconfiguration, mirroring howLAKE_ARTIFACT_CACHEoverridesenableArtifactCache. -
#13936 fixes an issue where
depPkgswas not properly set for a transitive dependency that was overriden by a package at a higher level in the dependency graph.
Other
-
#14028 fixes an issue where existence of potential stray files could influence whether a module is loaded under the module system, resulting in unexpected behavior
-
#14019 fixes
mkSimpleThunkTypeto use_instead ofName.anonymousas its binder name. A local declaration whose user name isName.anonymousmatches every identifier inresolveLocalName, shadowing all global constants and making the pretty printer render every constant in the local context as inaccessible (e.g.,True✝). Thematchcompiler usesmkSimpleThunkTypeto create the minor premises of parameterless alternatives, and tactics that introduce these binders using their binder name verbatim (e.g.,grind) ended up with a corrupted local context. Found while investigating #13773. -
#13965 adds experimental CLI flags that cache
lean's post-import elaboration state across invocations:--incr-save FILEwrites a full snapshot at end of run,--incr-load FILEreuses one at startup, and--incr-header-save FILEwrites a header-only snapshot (post-importEnvironment, no command bodies). A loaded snapshot will be reused as far as unchanged syntax (i.e. import header plus subsequent commands, if saved) allows for.