Lean 4.34.0 (2026-09-14)
For this release, 159 changes landed. In addition to the 55 feature additions and 63 fixes listed below, there were 5 refactoring changes, 5 documentation improvements, 6 performance improvements, 2 improvements to the test suite, and 23 other changes.
Highlights
Lean 4.34.0 focuses on the kernel: three soundness issues, found with AI adversarial testing, have been analyzed and fixed, and a series of additional defensive checks have been implemented for further reinforcement. In the automation side, bv_decide gets integrated with sym and grind interactive modes, while being ported to the SymM preprocessor that makes it up to six times faster. Work has continued on the floating-point API after Float and Float32 models being introduced in 4.33.0; linters can now carry state across commands and attach code actions to their warnings, and Lake improves its linting, caching, and error reporting.
vcgen has also undergone significant development as part of a major release scheduled for v4.35.0.
This highlights section was contributed by Juanjo Madrigal.
Kernel Soundness and Hardening
Continuing the kernel work of v4.33.0, this release closes three routes to a proof of False β two in the kernel proper and one in the runtime's reference counting β and adds a round of defensive checks on top.
All three were reported by Daniel Selsam (OpenAI) using their internal models, and all three need a deliberately constructed input rather than ordinary source code: they matter when checking proofs that arrive from an untrusted source, not for a development that only elaborates its own code.
-
#14806 makes
is_def_eqcaching order-independent. The kernel cached successful queries in a union-find structure; since the implemented test is sound but incomplete, and therefore not transitive, the transitive closure that union-find computes made a query's answer depend on which queries had been asked before. A crafted input used this to build a recursor whose type and computation rule disagreed, and deriveFalse. The union-find is replaced by a plain cache keyed on the query pair, so the answer is again a function of the two arguments. An OpenAI agent went on to produce two distinct exploits from the issue; both are caught bynanodaand by the newlean-inductive-models. -
#14807 makes the kernel's
is_proprequire a sort. When a term's inferred type was stuck rather than reducible to a sort,is_propansweredfalseinstead of rejecting the term, which skipped the proof-irrelevance guard on projections and allowed a data field to be projected out of a value used as aProp. The bogus proof was also accepted bynanoda;lean4leanis believed not to have the bug. #14843 applies the same fix to a copy of the old check inlined ininductive.h. -
#14838 freezes objects whose 32-bit reference count overflows, following the sticky-counter approach used by Koka. Forcing the counter to wrap corrupted the object's state, and on machines with at least 18GB of free RAM this could be turned into a use-after-free in the official kernel and extended into a proof of
False. Kernels not built on the Lean runtime were unaffected.
The rest of the section is hardening rather than bug fixing:
-
#14808 type-checks the recursors the kernel generates for an inductive type, and checks that each computation rule is type-preserving rather than merely well-typed. A minor premise that expects an induction hypothesis the rule does not supply reduces to an under-applied function, which is still well-typed but no longer has the recursor's result type β so checking that the right-hand side has some type would not catch it. This is defense in depth: it rejects only declarations that were already malformed.
-
#14582 makes the kernel reject inductive declarations in which a datatype being declared occurs applied to anything other than the declaration's parameters and universe levels. The frontend already enforced this, so only declarations built with
addDecldirectly are affected. -
#14849 bounds the size of
Natnumerals the kernel computes at 128 MB, so a compact proof can no longer direct it to evaluate a numeral of many gigabytes. Workloads that legitimately need larger numerals can raise the limit with theLEAN_NAT_MAX_SIZEenvironment variable. -
#14833 makes Lean require GMP 6.3.0 or newer, since earlier versions contain bugs that can make Lean produce unsound results in corner cases. Building against an older GMP now fails at configuration time;
-DUSE_GMP=OFF(Lean's own bignum implementation) and-DFORCE_GMP=ONare the two escape hatches.
bv_decide Is Faster and Cooperates with grind
Bit-blasting Inside grind =>
#14672 makes bv_decide available from within sym => mode, and #14713 connects it to the grind state: the relevant equivalence classes are encoded into the SAT problem alongside the goal, as are facts learned by theory solvers and by E-matching.
opaque g : UInt8 β UInt8
example (a b d : UInt8) (h0 : d = a ||| b)
(h1 : g d &&& 0xC0 = 0) :
g (a ||| b) &&& 0x40 = 0 := a:UInt8b:UInt8d:UInt8h0:d = a ||| bh1:g d &&& 192 = 0β’ g (a ||| b) &&& 64 = 0
grind =>
All goals completed! π
On its own, bv_decide abstracts g d and g (a ||| b) as two unrelated opaque variables and reports a spurious counterexample.
Inside grind =>, congruence closure has already merged them using h0, so the SAT problem it hands to the solver is the one that actually needs solving.
Choosing Which Types Get Analyzed
By default bv_decide guesses which structures and enum inductives in the context might matter and tries to incorporate them.
#14681 adds a types [...] clause that names them explicitly and turns the automatic discovery off, which keeps preprocessing tractable on goals mentioning many types of which only a few are relevant:
inductive Color where
| red | green | blue
@[ext] structure Pair where
x : BitVec 8
y : BitVec 8
example (a b : Pair) (c d : Color) (h1 : a = b) (h2 : c = d) :
a.x = b.x β§ d = c := a:Pairb:Pairc:Colord:Colorh1:a = bh2:c = dβ’ a.x = b.x β§ d = c
All goals completed! π
Pinning is a restriction rather than a hint: everything not listed is treated as an opaque variable, so naming the wrong type leaves the goal outside the supported fragment.
A type that is neither a non-recursive structure nor an enum inductive is rejected outright.
The clause is accepted by bv_normalize, bv_decide?, and bv_check as well, and inside sym =>.
Note the @[ext] above: bv_decide takes a structure equality apart through the structure's extensionality lemma, so a structure without one stays opaque whether or not it is listed.
A Faster Preprocessor
#14215 ports bv_decide's preprocessor to SymM, the rewriting engine shared with grind and cbv.
On large, rewriting-heavy problems this is a speedup of up to 6x, and substitution of embedded constraints becomes linear in the total size of the hypotheses.
Breaking change: @[bv_normalize] is now a Sym.simp set, which differs in pattern-matching power and in the shape it requires of a theorem, and bv_normalize's proving power shifts slightly in both directions.
Smaller improvements round this out: #14683 teaches the embedded-constraints pass to read both a = true and (!a) = true, and #14460 extends ground evaluation to more BitVec operations.
Elsewhere in the same engine, #14425 lets grind discharge the side conditions of conditional Sym.simp theorems, and #14459 adds an option for Sym.dsimp to rewrite inside instances, which can make more ground terms syntactically equal.
SymM also collects a batch of correctness fixes: the matcher no longer unifies metavariables unsoundly when matching nonlinear patterns (#14404), grind no longer drops E-matching theorems from custom attributes (#14426) or misses a valid contradiction because the canonicalizer resynthesized an instance inside a skipped binder (#14439), and lia/grind no longer emit a proof the kernel rejects when an integer expression's structure differs from that of its polynomial representation (#13587).
Further fixes: #14401, #14405, #14424, #14428, #14444, #14664, #14691, #14694, #14709.
Linters and Deprecation Warnings
Two changes extend what a linter can do.
#14357 introduces stateful linters, which persist state across command elaboration and can read the state of other linters, in an early/late two-phase architecture registered from an initialize block.
#14402 lets linters produce code actions, so a linter warning can now carry a fix that is one click away in the editor.
Deprecation reporting, introduced in v4.31.0, got a round of polish.
#14478 moves option deprecation onto the @[deprecated] attribute, so a deprecated option warns both when it is used with set_option and when it is read from meta code:
open Lean in
@[deprecated "use `demo.newOpt` instead" (since := "2026-08-21")]
register_option demo.oldOpt : Bool :=
{ defValue := true, descr := "an old option" }
open Lean in
def readsOldOpt (o : Options) : Bool := demo.oldOpt.get o
#14564 re-parses the header so that each deprecated-module warning points at the import that actually pulls that module in, instead of collapsing every one of them onto the start of the header, and #14533 silences deprecated-syntax warnings inside definitions that are themselves deprecated β the same rule that already applied to deprecated constants:
syntax "oldThing" : term
macro_rules | `(oldThing) => `(42)
deprecated_syntax termOldThing "use `42` instead"
(since := "2026-08-21")
def fresh : Nat := oldThing
A definition that is on its way out, though, is allowed to keep using the syntax that is on its way out with it, and stays quiet:
@[deprecated "use `fresh` instead" (since := "2026-08-21")]
def stale : Nat := oldThing
Lake
#14622 adds a --code-quality mode to lake lint that emits builtin linter results as machine-readable JSON entries instead of human-readable diagnostics, each keyed by the linter's option name.
Text-linter warnings are aggregated per module and linter into a single entry holding the count; environment-linter findings are reported per flagged declaration.
The entries are data rather than failures, so lake lint --code-quality succeeds even when violations are found.
Two entries β one aggregated from a text linter, one from an environment linter (a fixture from Lake's own test suite) β look like this:
{"value":{"scalar":{"value":2}},
"source":{"module":{"name":"Violations"}},
"name":"linter.unusedVariables"}
{"value":{"scalar":{"value":1}},
"source":{"declaration":{"name":"fooDummyMarker",
"module":"Violations"}},
"name":"linter.dummyMarker"}
On the caching side, #14720 demotes cache failures during a build to trace-level messages, so a build run with --wfail or --iofail no longer fails because of the cache alone, and #14651 fixes several ways a failed artifact transfer could go unrecorded, abort a whole transfer batch, or leave a corrupted artifact behind.
#14724 adds lake cache get --package, which fetches the outputs of a specific package in the workspace rather than only the root.
Error reporting improves in three places: error: Lean exited with code 1 is elided when lean has already printed the real diagnostics (#14629), a lean_lib root module with no source file reports the underlying file error instead of a vague bad-imports message (#14625), and lake update <pkg> now fails on a package name the manifest does not know instead of silently ignoring it (#14630).
Finally, #14723 makes MACOSX_DEPLOYMENT_TARGET configurable through the Lake API and includes it in build traces.
Performance and Robustness
#14520 fixes an exponential blowup in instantiateMVars on proof terms that repeatedly reference hypotheses introduced by MVarId.assert/intro β as MVarId.note, replaceLocalDecl, and simp at h do.
Lifting substituted values is now memoized and canonicalized across a whole pass, restoring linear behavior: the reproduction from #14329 went from exceeding 41GB to elaborating in about 1.2 seconds, and one Mathlib module dropped 26G instructions (-55%).
#14397 makes the set_option ... in tactic elaborate incrementally, so editing inside such a block reuses the results of the unchanged leading tactics instead of re-running the whole block.
For diagnosing where the time goes, #14386 adds store_traces_as name in cmd, which runs cmd, reports its trace as usual, and additionally keeps the trace tree in memory under name, together with #postprocess_traces name post for re-viewing that tree through any postprocessor.
Where postprocess_traces from v4.33.0 transformed a trace as it was produced, this separates producing from viewing β which matters when the command took a minute to run.
Two robustness fixes are worth knowing about: #14204 detects failures when flushing a module's .olean, so an exhausted disk no longer leaves a silently truncated file behind, and #14687 fixes a use-after-free in String.Pos.Raw.extract when it is called with gigantic slice limits.
#14717 fixes the same function's model/runtime mismatch and adds a fast path for String.extract when the positions are known to be valid.
On the FFI side, #14505 fixes segfaults caused by private imports of the Lean library by making each module's initializer call lean_initialize when it needs to.
The call to lean_initialize_runtime_module became implicit in the same cleanup, so users of Lean as an FFI library no longer need to call either function themselves.
Library Highlights
The floating-point work that landed in v4.33.0 continues.
#14481 upstreams Float.nan and Float.inf (with their Float32 and model counterparts), adds Int.toFloat and Int.toFloat32 alongside the existing Nat.toFloat, and exposes Float.ofNat/Float.ofInt; #14495 redefines the conversions between Float and the fixed-width signed integers in terms of the logical model, which had been written but not connected.
/-- info: 42.000000 -/
#guard_msgs in
#eval (42 : Int).toFloat
/-- info: true -/
#guard_msgs in
#eval (1.0 / 0.0) == Float.inf
#14788 redefines Bool.and, Bool.or, and Bool.not directly in terms of Bool.rec, which the kernel reduces much faster than a match; the compiler, which does not support Bool.rec, is pointed back at the old definitions with @[csimp], so generated code is unchanged.
The new shape is visible if you print one of them:
#print Bool.and
It also retires the kernel-friendly duplicates grind had been carrying for exactly this reason: Bool.and', Bool.or', and Bool.not' are now deprecated abbreviations for the real operations.
The HTTP client gains redirect support: #13901 adds Std.Http.Protocol.H1.RedirectPlan, which validates redirect responses following RFC 9110 and follows them automatically, and #13900 adds Std.Http.Body.Replayable for deciding whether a body can be replayed in the redirected request.
#14062 makes the HTTP/1.1 client finish reading responses that carry no body, and #14059 adds closeWithError so a body stream can fail.
Beyond that, the release is mostly incremental lemma work and naming cleanups; the naming changes are collected below.
Breaking Changes
-
#14501 establishes
iteandditeas the spelling for theifanddifsyntax in identifiers, andleft/rightas the markers for the two branches. Many lemmas are renamed accordingly; most visibly,if_posandif_negare nowite_eq_leftandite_eq_right, anddif_pos/dif_negaredite_eq_left/dite_eq_right. Migration: the old names remain as deprecated aliases, so existing proofs keep working and the warnings point at the replacement. -
#14462 renames
Nat.div_eqtoNat.div_eq_iteandNat.mod_eqtoNat.mod_eq_ite, freeingNat.div_eqfor a lemma analogous toNat.add_eq. Deprecated aliases are in place here too. -
#14215 turns
@[bv_normalize]into aSym.simpset, as described above. -
#14391 moves
Lean.Environment.replaytoLean.Kernel.Environment.replay, so that replaying an environment produces aLean.Kernel.Environmentand no longer goes through the unstableEnvironment.ofKernelEnv. Tools that replay environments β proof checkers and similar β need to follow the rename. Migration: the old name survives as a deprecated alias, and its warning spells out both the changed type and the fact that dot notation has to becomeKernel.Environment.replay x. -
#14523 deprecates
letFun, which went out of use a year ago; usehaveinstead. -
#14479 changes the meaning of
osCodeinIO.Errorso that it emulates POSIXerrnorather than forwarding libuv error codes cast to unsigned integers, while fixing a thread-safety bug inlean_decode_io_error. -
#14538 moves
eq_false_of_ne_trueinto theBoolnamespace asBool.eq_false_of_ne_true, leaving a deprecated alias behind. -
#14412 removes the unused
s : Ξ΅parameter fromExceptCpsT.runK. There is no alias for this one: call sites that passed the argument have to drop it. -
#14294 drops
@[implicit_reducible]fromString.toList, making it semireducible, since unfolding it dragged the definitional equality checker deep into its internal implementation. Code that relied onString.toListunfolding at implicit or reducible transparency β a defeq check, an instance, asimplemma whose statement is only well-typed after unfolding β now has to go through an explicit rewrite instead. -
#14833 and #14849, described above, affect anyone building Lean from source or computing very large numerals in the kernel.
Language
-
#14582 makes the kernel reject inductive declarations in which a datatype being declared occurs applied to anything other than the parameters and universe levels of the declaration. Such non-uniform occurrences could previously hide in positions that escape the kernel's checks: behind a reduction that erases them, or in the parametric arguments of a nested occurrence, which are dropped from the auxiliary declaration the kernel generates and were therefore only checked for well-typedness.
-
#14830 backports #14826, which warns that the intrinsic verification syntax is experimental and adds
set_option experimental.intrinsic trueto silence the warning. -
#14701 lets the
ensuresclause of adefcontract be written like afun, so a postcondition may be stated per shape of the result:ensures | none => False | some v => 2 * v β€ n. A contract clause now also starts on its own line when pretty printed, as it is written in source. -
#14686 makes the
requires,ensuresandinvariantclauses accept a type ascription on their binders, asfundoes:requires s : Nat => s = 0now elaborates as a binder form instead of being read as a term. An ascription covering all binders of aninvariantclause is reported as an error, since its first two binders are the loop's consumed prefix and remaining suffix. -
#14682 lets a
forloop that destructures its binder carry aninvariantclause, so a loop over a map may bind(k, v)and still state its invariant. A container that the clause cannot verify is reported where the clause appears, naming thePureForIninstance it lacks, instead of surfacing later as avcgengadget with no applicable specification. -
#14596 makes
vcgen's loop invariants available for every container whose iteration produces its elements without effects. Hash maps, tree maps, their sets, the polymorphic ranges, slices and iterators now supportfor β¦ invariant, including containers whose element type is universe-polymorphic, which previously had no loop specification at all. A new container is supported by declaring that its loop is effect-free, rather than by adding a loop specification for it. -
#14604 adds
cbv atfeature to runcbvon local hypotheses, but now it is safe with respect toSymMinvariants, namely, eachcbvcall (to a local hypothesis) is contained within a singleSymMcontext, which remains incremental -
#14602 adds an
assertelement todonotation for intrinsic verification.assert Pstates thatPholds at that point in the program;assert s => P sbinds the arguments of the assertion itself, such as the state of a state monad, using the same bindersfunaccepts.vcgenreads the assertion from the program and proves it as a verification condition; at runtime the element does nothing. -
#14603 lets the
requiresandinvariantclauses bind the arguments of the assertion itself, so a monad whose assertions are functions no longer needs an explicitfun. For a state monad, the state can be named directly:def sumIntoState (xs : List Nat) : StateM Nat Unit requires s => s = 0 ensures _ s => s = xs.sum := do for x in xs invariant pref _ s => s = pref.sum do modify (Β· + x) -
#14601 makes the loop invariant of
Std.Internal.Doa plain function of the elements consumed so far and the elements remaining, rather than a cursor indexed by the list being iterated. Thefor β¦ invariantclause binds two lists,invariant pref suff => β¦, and verification conditions mention them directly instead of{ prefix := β¦, suffix := β¦, property := β― }.prefix. -
#14589 spells the precondition clause of a
defcontractrequires, pairing withensures. -
#14586 warns about
public/privatevisibility modifiers on unnamedinitializeblocks - they do not do anything which can be confusing. -
#14581 generalizes
withSetOptionInover the result type of the wrapped function. The previous signature only accepted aCommandElab, which returnsUnit. The phases of a stateful linter (#14357) return values, so they could not use the helper (see for example leanprover-community/mathlib4#42186). All existing call sites instantiate the result type withUnitand do not change. -
#14579 lets a
defcontract discharge the verification conditionsvcgencannot prove on its own, in aspecsection ofwhere β¦ finally. The section is an ordinary tactic block, run on whatevervcgenleaves open, so the conditions are addressed by their case names and their binders name the variables the condition speaks about:def sumEvens (xs : List Nat) : Id Nat ensures r => β k, r = 2 * k := do let mut acc := 0 for x in xs invariant _cur => acc % 2 = 0 do acc := acc + 2 * x return acc where finally | spec => case vc1 acc h => exact β¨acc / 2, by omegaβ© -
#14567 allows
cbvto handle stacks of dependent projections, whose composite is non-dependent. -
#14533 changes the way deprecates syntax warnings are displayed. Inside of definitions, which are themselves deprecated, deprecated syntax warnings are silenced.
-
#14564 changes the handling of deprecated module warnings. Previously, deprecation warnings were displayed at the syntax ref corresponding to the first command of the file. Now, headers are re-parsed and used to extract correct position for displaying the deprecation warning.
-
#14389 adds intrinsic verification syntax for
Std.Internal.Dodo-notation: loop invariants and function contracts thatvcgendischarges automatically. -
#14402 adds the support for code actions to linters. When
Elab.asyncis enabled, before a linter task is dispatched, we create a promise for the info tree node. Then, we accumulate newly added info trees through the linter execution and we resolve the promise inside of the linter task. Finally, on the main task, we modify the info tree (wrapped in command context) and add a new leaf, with an mvar id, that will eventually be filled with a promise value. -
#14520 fixes an exponential blowup (time and memory, typically surfacing as an out-of-memory failure) in
instantiateMVarson proof terms that repeatedly reference hypotheses introduced viaMVarId.assert/introβ as done byMVarId.note,replaceLocalDecl,simp at h, and, per step, by LNSym'ssym_ntactic. Fixes #14329. -
#14478 changes the way we deprecate user-registered options (added via
register_option). To ensure we get warnings both when interacting with option usingset_optionand in meta code, we require the deprecation to happen via@[deprecated]attribute, and we populate the internaldeprecation?field using the information from that attribute. -
#7577 generalizes the
convandsimptactics to applypi_congrinstead offorall_congr. The test case for #7507 has examples that work now, but only worked at universev=0before. -
#14391 refactors
Lean.Environment.replaytoLean.Kernel.Environment.replay, so that environment replays work onKernel.Environmentinstead ofEnvironment, avoiding using the unstableEnvironment.ofKernelEnv. See #13783 for more context. -
#14357 introduces stateful linters, which allow linters to persist and share state across command elaboration.
-
#14418 changes the behaviour of
checkUnivslinter to take all declarations and constructors (if dealing with an inductive type) when calculating universes that do not appear on their own. -
#14437 fixes
inferInstanceAsmarking its wrapper auxiliary definitions@[expose]even when their bodies are well-typed only in the private scope, which made instances defined viainferInstanceAsfor types without an exposed body publicly ill-typed. -
#14386 is a follow-up to #14352 (introducing
postprocess_traces). It provides a new commandstore_traces_as myTraces in cmdthat runs the commandcmdand stores its traces in-memory under the namename. The stored traces can be transformed and viewed using#postprocess_traces tracePostprocessor myTraces. -
#14397 makes the
set_option ... intactic support incremental elaboration, so edits inside its tactic block reuse the results of unchanged leading tactics instead of re-running the whole block. -
#14387 changes the level at which
logLintExtdata is persisted toserver. Previously, it was all persisted atpubliclevel, thus causing negative performance regression.
Library
-
#14788 changes
Bool.and,Bool.or,Bool.notso that they are defined directly in terms ofBool.recfor better kernel performance. -
#14728 makes
Expr.getUsedConstantscollect thetypeNamefield ofExpr.projso we get a full list of constants that are directly used. -
#14699 changes the statement of the theorem
Nat.div_lt_div_right, whose conclusion isb / a < c / a β b < c, to not requirea β£ bas an assumption. -
#14726 weakens the hypothesis of
List.dropLast_takefromi < l.lengthtoi β€ l.length. -
#14507 generalizes the termination measures of
vcgen'swhileloop specifications. A measure may map into any type with aWellFoundedRelationinstance and may read monadic state:case inv2 => exact .ofMeasure fun i => i -- Nat measure case inv2 => exact .ofMeasure fun (i, j) => (i, j) -- lexicographic case inv2 => exact .ofMeasure fun _ s => n - s -- reads the monadic state
-
#14707 adds missing
cbv_evalannotations toofList/ofArray,get!,getD,insertoperations onHashMap/HashSet. -
#14687 fixes a use after free in
String.Pos.Raw.extractwhen calling it with gigantic slice limits. -
#14623 generalizes the
MonadTail (StateT Ο m)instance to work without needingNonempty Ο. This means that proving specifications aboutwhilewith aStateTmonad now works even if there is noNonemptyinstance for the state type. -
#14268 adds a HTTP Server benchmark
-
#14541 fixes a possible time-sensitive overwrite of the known size by the
Builder.streamfunctions. -
#14571 deflakes an HTTP unknown-size stream test. In some specific scenarios, it can fail because
tryRecv?runs in the interval between sending the response header and when"aaa"is sent. -
#14588 turns
cond_eq_iteinto asimplemma. -
#14538 moves
eq_false_of_ne_trueinto theBoolnamespace to be consistent with all otherBoolfunctions, and movesBool.and'(agrindhelper function) toInternal.Bool.and'. -
#14501 establishes
diteanditeas the recommended spelling for thedifandifsyntax. -
#14168 lets a Hoare
Tripleuse an assertion typePredat a universe independent of the program's value type, so specifications can quantify over assertions likeΟ β Propwhile values stay atType 0, andvcgenreasons over such specifications directly. -
#14523 deprecates the
letFunfunction, which went out of use in #9086 a year ago. -
#14062 makes the HTTP/1.1 client correctly finish reading responses that carry no body (head responses)
-
#14059 adds
closeWithErrorthat enables the body stream to fail. -
#13901 adds a
RedirectPlantype that uses the RFC9110 logic to validate redirect responses and automatically redirect. -
#14253 makes
Selectable.oneand other related functions handle errors and simplify them by using aSelectorononeandcombine. -
#14502 scopes
Lean.Order.instCCPO_stdintoStd.Internal.Doso Hoare triple notation (which defaults the exception postcondition toβ₯) elaborates afteropen Std.Internal.Dowithout also requiringopen Lean.Order. -
#12166 removes the dependency of
pairwise_iff_getElemonInit.Data.List.Nat.TakeDropand implementsnodup_iff_getElem_inj. -
#14495 redefines
IntN.toFloatandFloat.ofIntN(and the correspondingFloat32andISizefunctions) in terms ofFloat.ModelandFloat32.Model. The model already existed but was not used because of an oversight. -
#13900 adds a
Replayabletype class that is useful for checking if someBodycan be replayed in a redirect request. -
#14481 improves the API surrounding
Float/Float.Model/Float32/Float32.Model/UnpackedFloatin the following ways:-
The declarations
Float.nan/Float.inf/Float32.nan/Float32.infand their corresponding modelsFloat.Model.nan/Float.Model.inf/Float32.Model.nan/Float32.Model.infare added (upstreamed from batteries, if you will). -
The abbreviations
Int.toFloatandInt.toFloat32are added, analogous to the existingNat.toFloatandNat.toFloat32. -
Float.Model.Formatnow requires2 β€ exponentBitsinstead of just0 < exponentBits; which is a necessary condition forpackandunpackto behave correctly -
The definitions
Float.ofNat/Float.ofInt/Float32.ofNat/Float32.ofIntare now exposed. -
The type
Float.Model.UnpackedFloat.Signnow hasderiving DecidableEqinstead of justderiving BEq. -
The definitions for
unpackMantissa/unpackExponent/unpackSignnow useBitVec.extractLsb'instead ofBitVec.extractLsb
-
-
#14462 renames
Nat.div_eqtoNat.div_eq_iteandNat.mod_eqtoNat.mod_eq_ite. -
#14458 adds somme lemmas about
Nat.nextPowerOfTwo. -
#14412 deprecates and removes the unused parameter
s : Ξ΅fromExceptCpsT.runK. -
#14294 makes
String.toListsemireducible because unfolding it throws the definitional equality checker deep into the weeds of its internal implementation.
Tactics
-
#14713 adds support for
bv_decideto make use of thegrindstate when used insym/grindinteractive mode.bv_decidenow picks up on the (relevant) equivalence classes, encodes them into the SAT problem and then handles the problem as normally. -
#14709 ensures beta-reduction is applied when canonicalizing types in
grind. -
#14694 ensures assigned metavariables are properly handled in the
SymMdiscrimination tree module. -
#14691 ensures that the
SymMmatcher/unifier does not get confused byExpr.mdata. -
#14683 makes
bv_decide's embedded constraints pass understand botha = trueand(!a) = truecorrectly. This allows us to solve slightly more problems in pre-processing. -
#14681 adds support for restricting the set of complex types that
bv_decideis going to analyze as a user. By defaultbv_decideguesses that enums and structures in its context might be relevant and tries to incorporate them into the solving process. Now users can supply a restricted set of types viabv_decide types [MyEnum, MyStruct].bv_decideis only going to work these types and disable automated discovery once this option is passed. -
#14672 makes
bv_decideavailable from withinsym =>mode. -
#14669 makes
vcgentry the@[spec]theorems matching a program in priority order and apply the first one that fits the goal, so a spec whose instance argument the call site cannot synthesize no longer shadows a more specific one. -
#14215 ports
bv_decide's pre-processor toSymM. For large, rewriting heavy problems we observe a performance win of up to 6x. Furthermore, it fixes the asymptotics of embedded constraint substitution to be linear in the size of all hypotheses. There are also some breaking changes included:-
bv_normalize's proving power got slightly changed (both positively and negatively) -
@[bv_normalize]is now aSym.simpset which comes with some differences in terms of pattern matching power and required shape of the theorem.
-
-
#14664 fixes a bug in
mkTheoremFromDeclinSymM. It did not correctly handled polymorphic theorems that require adapters. -
#14529 reworks how a
@[frameproc]procedure discharges its split verification condition so that frame inference scales to operators whose residual the built-in lattice split cannot decompose. A procedure for separating conjunctionβused to leave behind aβthat no split rule could discharge, haltingvcgen; a procedure may now discharge its split VC however it wants, so separation-logic framing closes withvcgen β¦ with finish. -
#14535 fixes
vcgen [f, h, β¦]reportingNo spec foundfor a sibling call inside a self-recursivefwhen the list both bracketsfto unfold and supplies a spechforf, whetherhis named or pulled by*. A bracketed definition's unfoldings now rank below both a named spec and a*hypothesis for the same program, so at a recursive callvcgenapplies that spec and stops rather than unfoldingfagain into a branch whose sibling call has no matching spec. The regression came from #14528, which had raised these unfoldings to the named-spec priority. -
#14530 fixes a panic in
vcgenwhen an equation or unfold spec supplied viavcgen [someDef]is used for a program in a deep embedding, i.e. a program type with a bareStd.Internal.Do.WPinstance rather than a monadic one. -
#14528 makes every
vcgen [f]argument enter the spec database at the call-site priority band, so a definition to unfold or a spec supplied as a term outranks an ambient@[spec]on the same program. -
#14524 fixes
vcgen β¦ with finishon a provably unreachablematchbranch: it no longer reports success while leaving an unassigned metavariable that the kernel rejects (declaration has metavariables), nor fails withfinish failedon a verification condition whose proof needs a lifted precondition. -
#14492 makes
vcgenprefer a spec named in avcgen [...]argument over one collected from an ambient local hypothesis, and preferfooover a hypothesis pulled in by*invcgen [foo, *], so the spec you supply at a call site wins when several match. -
#14497 teaches
vcgento decompose a rawβ/βon the RHS of aPropentailment and aniInfon anyPiassertion lattice. -
#14490 makes
vcgenreport a clean missing-spec error when the spec it selects for a program turns out not to unify with it, instead of dumping the internal backward rule and its type. -
#14487 lets
vcgen [...]accept arbitrary term arguments, not just bare identifiers, mirroringsimp [...]. A term that proves a Hoare-triple orβ wpspecification is registered as a spec, and any other term proof is handled as a simp lemma, so forms likevcgen [show l = r from h],vcgen [foo x], andvcgen [@foo]now work. -
#14429 makes
vcgen [f]handle a definitionfwhose body is amatchon its arguments likesimp [f]does. A call with an opaque discriminant now rewrites through the unfold theoremf.eq_defand splits the exposedmatch, instead of reporting a missing spec. -
#14475 fixes a spurious "Too many variable names provided" error from
fun_induction(andinduction/cases) when an alternative had alet-bound field, so that all hypotheses of such an alternative can now be named. -
#14469 makes
vcgenwork after a preceding tactichave,let, orsuffices, which previously failed with "vcgen: could not determine the program type of the goal". -
#14468 migrates the standard library to the
[grind hom]and[grind hom_pred]attribute modifiers and removes the deprecated[grind homo]and[grind homo_pred]spellings. -
#14460 adds additional
BitVecoperations to the set of operations supported bySimp.Simp.evalGroundandSym.DSimp.evalGround. -
#14459 adds an option for
Sym.dsimpto rewrite in instances. This is usually not desirable as it can lead to non-standard instances. However, we might for example want to rewrite ground terms in instances to make more terms syntactically equal. -
#14464 renames the
[grind homo]and[grind homo_pred]attribute modifiers to[grind hom]and[grind hom_pred]. The previous spellings remain as deprecated aliases with identical behavior, and will be removed once the standard library migrates to the new spellings in a follow-up PR. -
#14457 records the homomorphism source types of a
[grind homo]theorem set: when an=-injection rule (a rule translatingEq Ο) is registered, the head constant ofΟis added to a new environment extension, and rules whose source type is not headed by a constant are rejected. The source types identify the terms thegrindhomomorphism engine must track in the E-graph. Thereset_grind_attrs%command clears the new extension. -
#14454 annotates theorems for
BitVec,Fin, and fixed (signed and unsigned) integers using then new[grind homo]and[grind homo_pred]attributes. This PR is based on the prototype implemented by Andres Erbsen at https://github.com/AeneasVerif/kraken/pull/122 -
#14452 rejects
[grind homo]theorems that are conditional rewriting rules. Conditional theorems are rejected with an error pointing to the E-matching attributes. Thereset_grind_attrs%command now also clears the[grind homo]and[grind homo_pred]extensions. -
#14451 adds the attribute
[grind homo_pred]. This attribute is used for a separate mechanism which complements[grind homo]. It is not a rewrite set but an eager fact injector keyed by head symbol. Where[grind homo]`` rules translate terms,[grind homo_pred]` theorems generate new facts about terms the moment they enter the E-graph. -
#14446 adds the attribute
[grind homo]. This is just the first step. We are going to use it to implement the approach described at https://hackmd.io/Qd0nkWdzQImVe7TDGSAGbA -
#14444 ensures
grinddoesn't timeout checking for definitionally equality while trying to propagatematch-expressions conditions. -
#14439 fixes a
grindbug where the canonicalizer could resynthesize a propositional instance (e.g.Nonempty Ξ±) occurring in a binder body skipped by preprocessing, producing a closed nested proof lacking theGrind.nestedProofwrapper. Congruence closure then treated the term as distinct from correctly wrapped occurrences of the same application, andgrindmissed valid contradictions. Closes #13655. -
#14431 fixes
vcgenfailing withFailed to apply rulewhen the same equality spec matches two different programs within one run, e.g. the equations of a recursive function registered viavcgen [f]: the cached backward rule was specialized to the first matched program and could not be applied to the next one. -
#14428 fixes the
grindfilter syntax. It preventedgrind =>from being used nested inmatchexpressions. -
#14426 fixes
grinddropping E-matching theorems from customgrindattributes when a partially activated theorem was reinserted under the same symbol. -
#14425 implements support for using
grindto discharge hypotheses in conditionalSym.simptheorems. -
#14424 fixes a maximal-sharing violation in
Sym.simp: when a conditional rewrite discharged a hypothesis that occurs in the theorem's right-hand side., the discharger-provided proof was spliced into the resulting term without restoring maximal sharing, violating theSymMsharing invariant (detected bysym.debug). Dischargers are not required to return maximally shared proofs. This issue was reported by @hargoniX -
#14416 fixes
vcgenandmvcgenfailing to splitmatch h : e with ...expressions, whose alternatives bind an equalityh : e = pattern. Fixes #12275. -
#14405 improves the support for offsets in
SymMmatcher/unifier. See new test for example that could not be handled. -
#13587 fixes a kernel type mismatch raised by
lia/grindwhen internalizing an integer expression whose syntactic structure differs from the structure of its polynomial representation. The mismatch occurred because theeq_defproof term bridgedx.denote ctx = e.denote ctxtoPoly.denote' ctx p = 0via a plainEq.refl e, butPoly.denote'collapses sub-structure such as a trailing+ 0(the(.num 0)monomial is dropped) whileekeeps it. The kernel then rejected the application because the equality betweenx.denoteandPoly.denote' pdid not hold definitionally. -
#14404 fixes
Sym.simpfailing to rewrite terms containing unassigned metavariables, and prevents the matcher from unsoundly unifying such metavariables when matching nonlinear patterns. -
#14401 fixes
preprocessTypeinSymM. It must not performzetaDeltaby default.
Compiler
-
#14838 prevents memory corruption when an object's 32-bit reference count overflows. On machines with at least 18GB of free RAM, it could be used to trigger use-after-free in the official kernel, which could be extended into a proof of False. Other kernels such as nanoda not based on the Lean runtime were not affected.
-
#14791 makes the compiler
macro_inlineresults ofcsimplemmas. -
#14717 the model/runtime mismatch in
String.Pos.Raw.extractand adds a faster variant (lean_string_utf8_extract_fast) forString.extractthat assumes that the positions are valid positions. -
#14505 fixes a compiler issue where private imports of the
Leanlibrary could lead to segfaults by ensuring the necessary call tolean_initializehappens in each module's initializer when necessary. As a follow-up clean up, the call tolean_initialize_runtime_moduleis made implicit as well, meaning users of Lean as an FFI library do not need to call these functions themselves anymore. -
#14332 adds
DT_SONAMEentries to the shared librarieslibInit_shared, libleanshared*, libLake_sharedon Linux. This is analogous toLC_ID_DYLIBon Mac which we already set via-install_name. Fixes #9420. -
#14479 prevents possible corruption if two threads simultaneously call
lean_decode_io_error. It also changes the semantics ofosCodeinIO.Error, such that it emulates posixerrnorather than forwarding uv error codes cast to unsigned integers. -
#14471 fixes a sanitizer warning where
initializefunctions were passed uninitialized memory as theirworldargument, by failing to callio_mk_world. -
#14463 reverts #14423 until we can get the situation on Windows figured out.
-
#14423 prevents possible corruption if two threads simultaneously call
lean_decode_io_error. It also changes the semantics ofosCodeinIO.Error, such that it emulates posixerrnorather than forwarding uv error codes cast to unsigned integers. -
#14204 prevents silent olean truncation when disk space is exhausted.
Pretty Printing
-
#14512 makes a
fordo-element pretty-print with a space beforedo. The do-elementforparser emitted"do "with no leading space, so reformatting afor β¦ doblock glued the range to the keyword (for x in xs doprinted asfor x in xsdo). Every sibling do-keyword (while,unless, term-levelfor) already emitsdo; this alignsfor. -
#14367 fixes an issue where the
@[simp β]attribute would pretty-print as@[simpβ ], along with analogous issues with@[grind norm β],@[wf_preprocess β],@[bv_normalize β], etc. See also discussion on Zulip.
Documentation
-
#14436 removes references to the unfolding lemma from the
repeatMdocstrings and moves that lemma into therepeatM.Internalnamespace.
Server
-
#15093 fixes the goal view showing the state after the enclosing tactic instead of the nested block's goal on the line after the last tactic of a nested
have ... := byblock orΒ·bullet, e.g. after an emptyΒ·where the next tactic is about to be typed (#15053). -
#15095 fixes the goal view showing the state after
haveinstead of the goal of an empty nestedhave ... := byblock on the following line, where the next tactic is about to be typed (#15053, regression of #1927 from #13229).
Lake
-
#14723 makes the
MACOSX_DEPLOYMENT_TARGETconfigurable via the Lake API -- both across a build and for custom builds of shared libraries or executables. It also includes the target in traces, ensuring a rebuilding if the value changes (e.g., if the environment variableMACOSX_DEPLOYMENT_TARGETis set). -
#14724 adds the
--packageoption forlake cache get, which fetches outputs for a specific package in the workspace (not just the root). This is particularly useful for downloading dependency outputs from a custom service. In addition, the undocumented--revsupport has been removed fromputand documented forput-staged. -
#14720 demotes all cache-related failures during a build to
trace-level messages. This ensures that builds run with--wfailor--iofaildo not fail solely due to the cache. -
#14622 adds a
--code-qualityoption tolake lintthat emits builtin linter results as machine-readable JSON entries instead of human-readable diagnostics. Text-linter warnings are aggregated per module and linter into one entry holding the warning count, and environment-linter findings are reported per flagged declaration; both are keyed by the linter's option name. The option implies--builtin-lintand--builtin-only. -
#14617 refactors
lake lint --builtin-lintinternals so that it has aModeflag (reporting vs recording exceptions) in the anticipation of the third mode of running upcoming code quality checks. -
#14629 suppresses Lake's wrapper line
error: Lean exited with code 1whenleanhas already emitted error-level diagnostics and exited with code 1, which is the usual type-error path and was pure noise next to the real errors. -
#14625 makes Lake report the underlying file error when a
lean_libroot module has no source file, instead of only reporting that some modules have bad imports. -
#14651 fixes a number of ways a failed artifact transfer in
lake cache get/lake cache putcould fail to be recorded or could lead to an early abort of the entire transfer batch. Sometimes this would leave a corrupted artifact in the local Lake cache, which could break downstream builds. -
#14630 makes
lake update <pkg>...fail with a clear error when a specified package name is not known to the current dependency manifest. Previously, unknown or misspelled names (including case mismatches) were silently ignored, which was confusing.
Other
-
#14833 makes Lean require GMP 6.3.0 or newer and builds the official releases against GMP 6.3.0. Earlier GMP versions contain bugs that can cause Lean to produce unsound (i.e., incorrect) results in corner cases; independent kernels that do not depend on GMP will catch such unsoundness. The portable Linux releases were previously linked against GMP 6.1.2 (inherited from the old glibc nixpkgs used for portability).
-
#14849 makes the kernel reject
Natliterals and computations whose representation would exceed a configurable size limit (128 MB by default). This prevents pathological or adversarial inputs from driving the kernel to spend unbounded memory and time constructing enormous numerals, and keeps the kernel's arithmetic comfortably within the range where its arbitrary-precision integer backend is well exercised. The limit can be raised with theLEAN_NAT_MAX_SIZEenvironment variable for the rare workloads that legitimately compute very large numerals in the kernel. -
#14847 adds another test for the
is_propbug in the kernel. The exploit was submitted by Daniel Selsam (OpenAI) and was generated using OpenAI's internal models. -
#14843 applies the fix from #14807 to
inductive.h. As the comment ininductive.hpoints out, the code should check whethere_typeis a proposition usingis_prop, but it was still inlining the old, buggy version ofis_prop. -
#14808 adds a new defensive check to the kernel.
-
#14807 fixes a soundness issue.
-
#14806 fixes a soundness issue in the kernel.
-
#14161 adds support for compiling with thread sanitizer. This both increases memory consumption and slows lean down massively so we only run a very small subset of tests to remain in a reasonable time. Developers need to add additional tests to the set themselves.