These release notes describe a release candidate, not the final release. They may be incomplete and are subject to change.
Lean 4.35.0-rc1 (2026-09-15)
For this release, 193 changes landed. In addition to the 69 feature additions and 52 fixes listed below, there were 17 refactoring changes, 7 documentation improvements, 19 performance improvements, 2 improvements to the test suite, and 27 other changes.
Language
-
#15090 adds erased state to
donotation:erased x := e,erased mut x := e, anderased x ← actdeclare verification-only variables that loopinvariantclauses and assertions can read while compiled code carries only a dummy in their place. -
#15079 makes
Array.modifyandArray.modifyMkernel-reducible across module boundaries. -
#15078 makes
Array.zipWithand the correspondingVector.zipWithoperation kernel-reducible across module boundaries. -
#14996 lets
Array.mapand the delegatingVector.mapreduce in the kernel across module boundaries. -
#14989 lets
Array.ofFnand the delegatingVector.ofFnreduce in the kernel across module boundaries. -
#14988 lets the derived
VectorDecidableEqinstance reduce in the kernel across module boundaries. -
#14270 lets
decideandrflreduce nonemptyArrayequality in the kernel across module boundaries. -
#15050 adds and
--from-export file.ndjsonflag toleanchecker. This instructs it to load the provided ndjson export format file and run it through the kernel. -
#15019 removes the
withPositionmarker from the body ofmacroandelabdeclarations. -
#14982 introduces a new file
mimalloc.cppwhich contains our object allocation logic and includes the mimalloc implementation. -
#14912 adds an option to
TerminationHintsto disable warnings for redundant hints. This can be useful for generating declarations with termination hints usingaddPreDefinitions, avoiding warnings if these hints turn out to be redundant. -
#13815 fixes an issue where ambiguous syntax would have missing terminfo or missing context (such as the metavariable context), leading to errors in the infoview. Closes #8108.
-
#14960 improves the way
#printdescribes recursors. For example,#print Nat.recnow gives the following:recursor Nat.rec.{u} {motive : Nat → Sort u} (zero : motive Nat.zero) (succ : (n : Nat) → motive n → motive n.succ) (t : Nat) : motive t number of parameters: 0 number of motives: 1 (position 1) number of minor premises: 2 (positions 2–3) number of indices: 0 major premise position: 4 rules: Nat.rec zero succ Nat.zero ==> zero Nat.rec zero succ n.succ ==> succ n (Nat.rec zero succ n) -
#14940 changes the behaviour of
deprecated_syntaxwarnings for things that were generated via macro expansion. If a piece of syntax is a result of macro, but comes with.originalsource info, we focus the warning on that piece of syntax. -
#14937 redesigns the
rwatactic for consistency and user-friendliness. -
#14554 fixes #14540 by separating the type-theoretic and runtime implementations of
Fin.foldl. -
#14925 makes
casesOnandrecOnof a proposition apply the minor premise to the projections of the major premise, instead of going through the recursor. A recursor only reduces once its major premise is a constructor application, which a proof may never become, soAnd.casesOnand friends no longer require the proof itself to reduce. -
#14855 adds strong (co)induction proof principles for lattice-theoretic (co)inductive predicates. For predicates defined by
coinductive_fixpoint, the generatedstrong_coinductprinciple strengthenscoinduct: in the hypothesis, occurrences of the candidate predicate are joined by disjunction with the coinductive predicate itself, so a proof by coinduction may conclude as soon as it re-enters the predicate. Dually,inductive_fixpointdefinitions receive astrong_inductprinciple whose induction hypothesis additionally provides membership in the predicate itself, and mutual (including mixed) definitions receivestrong_mutual_inductwith the connective chosen per component. The conclusions of the generated principles are now also beta-reduced, e.g.star_ind tr q₁ q₂ → pred q₁instead of(fun q₁ => star_ind tr q₁ q₂) q₁ → pred q₁. -
#14909 fixes elaboration of sort-polymorphic inductive types such as
inductive T : Sort u | a | b, which previously failed withUnknown constant `T.ctorIdx`. It also fixes the same failure forset_option genCtorIdx false. -
#14350 enforces that elements of singleton notation need to indent e.g. nested application arguments, as otherwise the overlap with structure notation is simply too confusing for both humans and the upcoming formatter (would require blocking on elaboration).
-
#14861 adds a
monotonicity_byclause tocoinductiveandinductivepredicate declarations, allowing users to prove monotonicity of the underlying fixpoint functor with an explicit tactic block when the automaticmonotonicityproof search does not succeed. The clause enters tactic mode directly, analogously todecreasing_by, and may be attached to individual members of mutual cliques, including mixedinductive/coinductiveones:mutual coinductive tick : Prop where | mk : ¬tock → tick monotonicity_by repeat monotonicity inductive tock : Prop where | mk : ¬tick → tock end -
#14899 adds the built-in
recallandrecall?commands for checked expository restatements without requiring any imports. -
#14834 adds terminfo on
structure/classfields so that "go to definition" on dependent usages of a field goes to the field's definition. This also helps with finding uses of a field in astructuredefinition. The PR additionally fixes a bug where terminfo and docstrings weren't applied to private fields of public structures when using the module system. -
#14844 ensures that
DiscrTreeoperations collapse any trie nodes that end up empty. -
#14860 makes
invariant,decreasing, andassertin adoblock elaborate withoutopen Std.WP, matching therequiresandensuresclauses of a contract. -
#14858 removes the match-alternatives form of the
ensuresclause. Except for that, every clause of a contract (requires,ensures,assert,invariant,decreasign) elaborates like afuntelescope, including tuple patterns as inensures (lo, hi) => lo ≤ hi. -
#14854 makes the
constructortactic emit a warning if multiple constructors match, and adds aconstructor!tactic that has the previous behavior of silently applying the first matching constructor. -
#14845 gives the invariant of a
whileorrepeatloop its own type,def Std.WP.WhileInvariant α Pred := Bool → α → Pred, a predicate over the loop'sexitflag and the loop state. Previously, it reusedStd.WP.RepeatInvariant α α Pred = α ⊕ α → Pred, a type which is awkward to use in practice. -
#14590 names the variables of a verification condition after the program, and makes those names accessible. Take
-
#14825 adds a
givenclause todefcontracts, written beforerequires. It binds the logical variables of the contract and scopes them overrequiresandensures. -
#14748 adds the infrastructure to collect code quality metrics from linters. Entries logged during elaboration are saved in the
.oleanfile for the module. Build tools can then collect these entries for each module without re-elaborating the code. -
#14816 makes the
@[deprecated]attribute warn when the given replacement declaration is itself deprecated. When the replacement has a replacement of its own, the warning suggests deprecating directly in favor of that declaration instead. The check can be disabled withset_option linter.deprecated.deprecatedTarget false. -
#14826 reports the intrinsic verification syntax as experimental wherever it is used: the
requiresandensurescontract clauses of adef, theassertelement, and theinvariantanddecreasingclauses of a loop each report at their keyword. Settingexperimental.intrinsictotrueacknowledges the experimental status and silences the reports. -
#14821 types the
funbinders of ado←argument from the wrapper's signature, so field notation such asbs.extractresolves in the forwarded body. A binder with a type ascription is accepted as well, and a pattern binder gets a dedicated error message. -
#14624 fixes #9077 by ensuring that, during instance search, all metavariables for instance-implicit arguments are only assigned values of the expected type, up to instance transparency. The backward compatibility option
set_option backward.isDefEq.instanceTypes falserestores the old behavior. This is a breaking change that affects a few declarations in Mathlib. All of them have been fixed with a backward compatibility option, analyzed and extensively annotated with suggested actions. Note: This fix does not enforce the type of out-params at instance transparency; synthesis ofClass X, whereXis in an out-param position, can still yield aClass Yinstance, as long asX =?= Yat a higher transparency. The latter phenomenon is of a somewhat different nature; it would be easy to enforce, but it would cause ~1000's of broken declarations in Mathlib, for unclear benefit. -
#14705 adds a clickable hint (and thus also a code action) to
deprecatedlinter. -
#14771 fixes using
Environment.find?and its variants on the result ofofKernelEnv. -
#14583 changes definitional equality and unification heuristics. Before
isDefEqApp's fallback throws a stuck exception, it tries the remaining few heuristics, and only if they don't succeed, the exception is thrown. This PR at the same time improves performance and robustness of unification and instance search. This change is a preparation for #14624. The backward compatibility flagset_option backward.isDefEq.throwOnStuckAfterApp truerestores the old behavior. -
#14703 lets a
repeatorwhileloop state the loop invariant and the termination measure thatvcgenneeds, so a loop inside adefwith a contract verifies without manual proof steps:def countDown (n : Nat) : Id Nat ensures r => r = 0 := do let mut i := n while i > 0 invariant exit => if exit then i = 0 else True decreasing i do i := i - 1 return i -
#14600 adds a warning when deprecating a declaration in favor of another declaration that is not reducibly defeq.
Library
-
#15144 allows the RUP component of the LRAT checker to accept hint clauses that are themselves redundant.
-
#14796 fixes reference count, mark_mt and error messages in libuv modules.
-
#15122 adds the missing order instances on
UIntXandIntX. -
#15114 exposes
Fin.addNat?, so that it reduces across module boundaries. Ranges overFin nare built from it via theUpwardEnumerable (Fin n)instance, so previouslycbvand kernel reduction got stuck onFin.addNat?applications in any file using the module system. -
#15092 adds a series of missing order instances on
Fin, includingMin,Max,LawfulOrderOrd, etc. -
#15088 provides
LinearOrderPackage Int, which in turn provides the missing instanceLawfulOrderBEq Int. -
#15080 deprecates
Lean.MVarId.liftReflToEqand its helper theoremLean.Meta.Rfl.rel_of_eq_and_refl. Neither is hooked up to a tactic in core, and downstream users should keep their own copies. -
#15049 introduces
markLinearfunctions for hash maps, akin toArray.markLinear -
#15071 adds the missing instance
LawfulOrderBEq Nat. -
#15069 introduces
Vector.markLinearin a similar vein toArray.markLinear. -
#15062 registers
extandext_ifftheorems forULift,PULift,PLift, andMProd, so theexttactic applies to equalities of these structures. -
#15043 fixes
Std.Http.Serverstalls caused by idle connections retaining connection slots past the configured keep-alive timeout. -
#15059 annotates some
ForInandForIn'instances withdefault_instance. -
#15054 adjusts the precedence of the range syntax so that
1 + 2...3is(1+2)...3anda...b |>.toListis(a...b).toList. -
#15018 adds a missing
Decidableinstance forbif(cond) expressions, analogous to the existing instance forif(ite) expressions. -
#14794 adds
isEmpty_inter_commacross the synchronized associative-container APIs, starting from associative lists and lifting the result through hash maps, tree maps, and sets. This makes it possible to prove symmetry of disjoint containers without unfolding membership. -
#14995 adds two lemmas exposing the recursion of
List.mergeSortwithout reference toMergeSort.Internal.splitInTwo:-
mergeSort_append: merging the sorted halves of any balanced split (l₂.length ≤ l₁.length ≤ l₂.length + 1) gives(l₁ ++ l₂).mergeSort. This is the primary statement: it has no index arithmetic, holds uniformly for every list length, and any specific unfolding (take/drop at the midpoint, cons-cons forms) is a two-line corollary. -
@[simp] mergeSort_pair:[a, b].mergeSort le = if le a b then [a, b] else [b, a], completing themergeSort_nil/mergeSort_singletonprogression. UnlikemergeSort_appendit genuinely simplifies, so it is marked@[simp].
-
-
#14890 swaps the names of
Dyadic.not_ltandDyadic.not_le, so thatDyadic.not_ltreads¬x < y ↔ y ≤ xandDyadic.not_lereads¬x ≤ y ↔ y < x. This is consistent with the corresponding lemmas forNat,Int,Rat, and with_root_.not_ltand_root_.not_lein mathlib. -
#8204 adds the lemma
Int.tdiv_eq_zero_iff_natAbs_lt_or_eq_zerowhich shows that T-division equals zero iff the absolute value of the numerator is less than the denominator, or the denominator equals zero:@[simp] theorem tdiv_eq_zero_iff_natAbs_lt_or_eq_zero {a : Int} {b : Int} : a.tdiv b = 0 ↔ (a.natAbs < b.natAbs ∨ b = 0):= by -
#14545 renders
Vectorvalues using#v[...]literal notation instead of exposing their underlying structure representation, making evaluated vectors more concise and readable. It replaces the derivedReprinstance. -
#14953 removes
Lean.reduceBool,Lean.reduceNat,Lean.ofReduceBool,Lean.ofReduceNatandLean.trustCompiler, along with the kernel's support for reducing applications of the first two by running the compiler. They have been deprecated since 2026-02-01 in favour of asserting native evaluations with axioms, which is whatnative_decideandbv_decidealready do throughLean.Meta.nativeEqTrue. Nothing in the toolchain used them any more. -
#14842 refactors the LRAT checker and massively extends the publicly available CNF API in doing so, in particular we:
-
formalize basic CNF properties like entailment, negation, unit clauses
-
formalize the RUP and RAT property
-
refactor the clause data structure to a more memory efficient one to support storing huge CNFs more efficiently
-
rewrite the LRAT checker from scratch on top of this new API. The new LRAT checker is both slightly faster and supports bounded variable addition.
-
-
#14916 introduces
BitVec.ofNatClampas a generalization of the already existingUIntX.ofNatClampedfamily of functions. -
#14915 adds support for evaluating
Nat.log2to the ground evaluators ofSym.simpandMeta.simp. Despite working by recursion, it still manages to evaluate efficiently by reduction because it only runs logarithmically many kernel-accelerated operations. -
#14905 makes
BitVece-matching annotations that convert "accidentally" fromgetElemtogetLsbDless aggressive. This is done by instead encoding them into a dependent and +getElem. -
#14903 makes
List.Nodup.getElem_injonly fire if we already seeNodup xsand indexing intoxs. -
#14895 adds
ReflBEqandLawfulBEqinstances forSumand exposes its derivedBEq, so that==on sums reduces outside the module that defines it. -
#14872 removes the
⦃ P ⦄ c ⦃ v, Q ⦄form of the Hoare triple notation. The⦃ P ⦄ c ⦃ fun v => Q ⦄expansion is easier to understand. -
#14836 replaces the exception postcondition types
EPost.NilandEPost.Conswith products. An exception postcondition stack is now(ε₁ → Pred) × (ε₂ → Pred) × EStack⟨⟩, so theProdAPI applies to it. The base monads carry bare postconditions:Except εusesε → Prop, andOptionusesUnit → Prop. The notationEStack⟨A, B⟩writes a stack type, andestack⟨e₁, e₂⟩writes a stack value. Both print back as written.vcgensplits⊥and⊤exception postconditions with the same cached backward rules as the other lattice connectives. -
#8309 changes the definition of
Decidable pto a structure containing aBooland a proof of eitherpor¬p. -
#14824 adds
applyequations for thePredTransoperations that had none, so thatsimpreducesget,set,modifyGet,read,throwandtryCatchthe way it already reducespure,bindand the rest. -
#14813 adds
Triple.and,Triple.mpandTriple.observetoStd.WP. Each combines two Hoare triple specifications for one program into one. -
#14801 ports the soundness class
WPSoundfromStd.DotoStd.WP, where it is calledLawfulWPMonadAttach. -
#12330 removes an Iff-True from two statements about arrays. This makes them harder to use, because you cannot use them directly to rewrite. Additionally, they are also not in simp formal form due to
iff_true. -
#14751 gives a
repeatorwhileloop one gadget per set of annotations it states,forInLoopWithInvariant,forInLoopWithVariantorforInLoopWithInvariantAndVariant, replacing a single gadget that carried both annotations inOptionslots. -
#14744 lets
grinddischarge the verification conditions of arepeatorwhileloop that states a termination measure in a monad with state, where the proof had to evaluate the measure withsimp_allfirst.RepeatVariant.EvalsToandRepeatVariant.EvalsBelowgain the fixed-arity ground instances thatgrindcan key on, at arities 1 through 5. -
#14711 corrects the three lemmas
contains_empty,not_mem_empty,singleton_eq_insertinStd.ExtDHashMapthat were accidentally aboutStd.DHashMap.
Tactics
-
#15124 fixes a non-linearity in LRAT trimming which causes the original and the trimmed proof to stay alive at the same time instead of reusing the memory.
-
#15116 lets
liaandgrobnertake the same[...]parameter list asgrind, so extra facts and lemmas can be supplied inline (e.g.lia [foo n]orgrobner [= sq_def]) instead of first adding them to the local context withhave. -
#14688 makes
vcgenreportNo spec found for program …when the program head is one that no strategy steps and no spec keys on, such as the barefun s => …left by unfolding aliftMof an anonymous state transformer. Previously this failed withFailed to decompose weakest precondition … This should not happen. -
#14956 introduces definitions for
min/maxonBitVecas well as support inbv_decideformin/maxonBitVecand theUIntX/IntXfamily of functions. -
#14928 introduces support for symbolic
Natshifts andextractLsb'. This is done by re-interpretingx >>> nasx >>> BitVec.ofNatClamped (log2 w + 1) nandextractLsb'as a shift +setWidth.bv_decidewill still not perform reasoning over thenitself but it will at least know that e.g. inx >>> nall output bits are0or some of the input bits ofx. This is achieved by making theBitVec.ofNatClampedas an uninterpreted bitvec atom. -
#14921 fixes
simpanddsimppanicking withPANIC at Lean.Expr.appArg!/Lean.Expr.appFn!: application expectedwhen one simproc rewrites a term to one with fewer arguments. The panic is logged atinfoseverity, so the build still exits successfully while emitting it. -
#14922 stops the simprocs
Lean.Elab.WF.paramProj,paramMatcherandparamLetfrom taking part in everysimpanddsimpcall. They implement one step of the preprocessing of definitions by well-founded recursion and are of no use elsewhere. -
#14883 makes
vcgencanonicalizeWPinstances, so monads may register a diamondWPinstance in addition to the low priorityWPinstance synthesized fromWPMonad.toWP. -
#14874 deprecates the
mvcgenandmvcgen?tactics in favor ofvcgenviadeprecated_syntax, so each use reports a deprecation warning controlled bylinter.deprecated.syntax. -
#14870 adds the
experimental.vcgenoption and makesvcgen invariants?warn that invariant suggestions have not been ported frommvcgenand that the feature is slated for removal. -
#14857 makes
vcgensucceed on a goal whose local context is inconsistent. Previously it failed withNo goals to be solved. -
#14856 moves the
vcgen framesclause lookup fromapplySpecintoapplySpecs, so a matching clause is consumed once per goal instead of once per spec candidate. A candidate that fails to apply after the lookup, for example because one of its instance arguments cannot be synthesized, no longer retires the clause, and the next candidate still sees the same provided frame. -
#14848 fixes a bug in
sym =>initialization. It now correctly handles the case the goal is closed during preprocessing. -
#14828 removes the
@idhint in the proof term thatvcgengenerates whenever it replaces the target. Removing it speeds up kernel checking of the resulting proof. -
#14829 fixes literal canonicalization in
grind. -
#14823 head-reduces every verification condition that
vcgenemits, so a loop over two mutable variables states its entry condition as0 ≤ 0rather thanmatch (0, 0) with | (lo, hi) => lo ≤ hi, and a condition that reduces torflcloses on the spot instead of reaching the user. -
#14819 makes the conjunctive-precondition classification of
@[spec]theorems look throughbinderNameHint. -
#14820 adds support for unfolding definitions in
Sym.simpwhen a function symbol is provided as a parameter.sym => simp [f]now uses the equational theorems off, likeMeta.simpdoes, instead of failing. -
#14814 fixes an internal error (
unexpected bound variable #3) when a declaration that is not a proposition is used as aSym.simptheorem, as insym => simp [HAdd.hAdd]. It now produces a proper error message. -
#14802 implements a
let_to_havetactic to the interactivesym =>mode. It converts the nondependentletdeclarations of the goal target intohavedeclarations, producing a definitionally equal goal. This unblocks the efficienthave-telescope machinery ofSym.simp(simpLet), which does not process dependentlets. -
#14799 eliminates two sources of overhead that made case splits in
grind/symslow on goals containing large terms. On the new benchmark (a singlecases_nexton a goal with a chain of 6400BitVecoperations), the time drops from 5.9 s to 0.11 s. -
#14787 makes
mvcgenandvcgensplit programs headed bycond(bif c then t else e) into one verification condition per branch, with the hypothesisc = trueorc = falsein scope, matching the treatment ofifandmatch. -
#14118 introduces new cost metrics for
grind's e-matching graph that can be optionally enabled in its diagnostics viaset_option grind.ematch.diagnostics true.grindis now able to detect:-
individual instances that have a lot of direct follow up children. Configurable via
grind.ematch.diagnostics.branchThreshold -
instances that participated in a large, transitive closure of follow up instances. For this
grindcomputes a cost metric that is roughly equivalent to the size of the transitive closure but fairly distributed among multiple parents. Configurable viagrind.ematch.diagnostics.costThreshold. The metric is based on the cost heuristic of https://github.com/viperproject/smt-scope.
-
-
#14785 turns the fix latency of 50ms when waiting for the SAT solver into an exponential backoff starting at 1ms and going up to 64ms. This should lower the latency for en-mass solving of small SAT problems.
-
#14770 fixes a performance issue where proof terms produced by
grindcould trigger kernel deterministic timeouts.grindcanonicalizes nestedDecidableinstances under the identity wrapperGrind.nestedDecidable, leaving the kernel to checkt =?= Grind.nestedDecidable t. The kernel does not see the[reducible]attribute, and its lazy-delta heuristic unfoldedtinstead of the wrapper. Whentis an instance such asUInt32.decLeapplied to a symbolic argument and a large literal (e.g.,97 ≤ c.val + 4294967264coming fromChar/UInt32wraparound), the check descended throughBitVecandFinintoNat.bleon a2^32-sized literal with a free variable inside, where the fast numeral path does not apply, and effectively never terminated. MarkingnestedDecidableas an abbreviation stores theabbrevreducibility hint in the declaration itself, which the kernel does honor, so the wrapper side is unfolded first and the check succeeds immediately. -
#14769 fixes an internal
grinderror (mkEqProofinvoked with terms of different types). An equivalence class can contain terms of different types when they are merged viaHEq(e.g.,BitVecterms of different widths related throughcastterms). The=-injection performed by the[grind hom]hooks applies only to homogeneous equalities, soprocessNewEqandprocessNewDiseqnow skip pairs whose types differ. -
#14768 adds a
lift_letstactic forsym =>mode. The tactic moves thelet/havedeclarations of the goal target as far toward the root as their dependencies allow, flattening nested declarations and merging declarations with syntactically equal definitions. The new goal is definitionally equal to the original one. Declarations underfun/∀binders are not lifted, and hypotheses are never modified. -
#14766 makes
vcgenkeep the exception postcondition of a@[spec]theorem that states it asepost⟨E⟩withEschematic. Such a spec was applied with its exception postcondition weakened to⊥, leaving the verification condition⊥. -
#14727 implements homomorphism simplification sets for the
grindtactic. Theorems tagged with the new[grind hom]attribute translate terms from a source type into a target type that has a dedicated solver (e.g.,Fin,BitVec, and the fixed-width integer types intoNat/Intarithmetic), and[grind hom_pred]theorems supply the range facts for the injection functions (e.g.,Fin.isLt). The translation is applied to fixpoint outside the E-graph during internalization, so only the final normal form is internalized. The feature is on by default and can be disabled withgrind -hom. -
#14763 introduces a new
grind/symmode tactic calledbv_decide_push. Users can call this tactic at arbitrary points in their proof and it will run pre-processing on everything that has been internalized into thegrindstate so far.bv_decide_pushthen stores the results of this pre-processing step in the goal state in order to speed up future invocations ofbv_decideorbv_decide_push. On pre-processing heavy benchmarks where many subgoals share the same hypotheses, this can lead to 2x performance improvements and higher. Note that this does not yet implement incremental SAT solving. -
#14765 lets the
@[spec]-annotated theorems of a file elaborate in parallel with one another. Previously, any such annotation would block elaboration waiting for the completion of the proof. -
#14757 changes two things about how
bv_decidecollects facts in grind mode:-
It will no longer even consider enum/structure/UIntX equivalence classes if the features are disabled
-
It will not eagerly internalize the goal state on its own as this might be costly. Thus any facts that would be derived from grind internalizing the goal will not be used. However, the goal will still be used as part of bv_decide's contradiction proof.
-
-
#14747 lets
vcgencontinue through a spec whose program applies a continuation variable under a binder, such as forkinLang.bnd (fun x => Lang.add (k x) (Lang.nat 0)). First-order matching leaveskopen and unification assigns it while the pending constraints are processed, which happens after the emitted goal's type has been built, so that goal's program is the metavariable standing forkapplied to its arguments.vcgenused to give up on such a goal with "Failed to decompose weakest precondition … This should not happen". Now it instantiates in the right place. -
#14745 eagerly simplifies the state arguments of a
wpgoal asvcgensteps the program whensimplifying_assumptionsis present, so the VCs become cleaner and the simplification work isn't duplicated. Ontests/bench/vcgen/vcgen_get_throw_set_grindthis cuts kernel checking from 862ms to 291ms at n=300, and VC generation is slightly faster as well. -
#13968 adds detection and optimized lowering of if-then-else/XOR/XNOR gates to
bv_decide's AIG to CNF lowering. This detectsc ? t : fgates of the form(c → t) ∧ (¬c → f)in the AIG, up to permutation/negation of inputs. This pattern also covers XOR/XNOR which are expressed asa ? ¬b : b/a ? b : ¬b. When a gate is lowered, if it matches these patterns it is lowered to a 4-clause encoding instead of the 12 clauses used by lowering as AND gates.
Compiler
-
#15107 allows the
ReduceAritypass to remove all parameters of a function. If this does happen, it places avoidparameter to avoid promoting the value to a constant. -
#15089 makes
floatLetInpessimistic when considering whether to float things intocasesthat operate onEST.OutorST.Out. These cases indicate the potential presence of side effects which, throughST.Ref, can cause non-linearities that are not easily predictable through static analysis. Thus we now refuse floating into thecasesto preserve linearity for sure. -
#15075 fixes an off-by-one error in the max size computation for objects.
-
#15052 introduces dynamic tracking primitives to detect non-linearity issues. Users can call
markLinearonString,ByteArray,FloatArray, andArray, this has two effects. First, if they are not already unique they will be forcibly copied and thus made unique. Second, every further write access to them must occur linearly. If the access does not occur linearly and the environment variableLEAN_ABORT_ON_NONLINEARis set, the program aborts. Users can attach a breakpoint onlean_internal_panicto figure out where precisely the program gets aborted to track down the linearity issue. Follow up PRs will addmarkLinearoperations for other built-in linear data structures. -
#14969 fixes a crash when more than 16 arguments are applied at once to a closure whose arity is at most 16.
-
#14948 builds mimalloc with
MI_MAX_ALIGN_SIZE=8, which means that objects likeList.constake 24 bytes instead of 32, improving memory usage and cache efficiency. -
#14531 upgrades LLVM to 23.1.0. This brings slight improvements across the bank for Lean binaries as well as reduced C compilation time.
-
#14888 fixes attribute/extern "C" ordering affecting builds with some versions of gcc.
-
#14811 writes Lean's C output files atomically do their destination by first creating a temp file and then atomically moving it to the real destination. This is necessary as
clangreads C files usingmmapso concurrent writes to the same C file may cause issues. Most notably this should resolve the probabilistic clang lexer segfault we've been observing for a while. -
#12814 changes the handling of proof-over-applied cases expressions in
ToLCNFto avoid generating function declarations that are called immediately. This is a less invasive version of #12284 (which has since been reverted) that only affects proof overapplications, not all overapplications. However, this is also the only case we really need for #8309 (and other occasions likematch h : _). -
#14775 fixes the remaining issues with
ST.Ref. We now enforce thatST.Refacts truly like a spinlock by introducing two changes:-
ST.Ref.setis now justdiscard <| ST.Ref.swapand fulfills no special purpose for closing the critical section opened byST.Ref.takeanymore -
We introduce an
unsafefunction calledST.Ref.putwhich may only be called after a critical section has been opened using theunsafeST.Ref.take. We then implementmodifyand friends using these operations.
-
-
#14773 restores the interpreter's argument-passing path to the form it had before #14749, which regressed some benchmarks. A borrow annotation on a scalar parameter carries no information, and
Lean.IR.ToIR.lowerParamnow drops it. The interpreter can therefore assume that every borrowed parameter is a reference, and the test that #14749 added at each argument becomes alean_assert. -
#14749 corrects a memory leak. The leak occurs when interpreted code calls a compiled function with a scalar parameter that is marked
@&. -
#14585 fixes a bug with reference count corruption in
Ref.swapwhen raced againstRef.get, by replacing the buggy atomic exchange operation with a CAS that prevent swapping with null pointers.
Pretty Printing
-
#14971 adds completions to the
@[delab]attribute for theappexpression kind prefix.
Lake
-
#15141 adds a
--packageoption tolake buildandlake cache put. Onlake build,-owith--packagewill track the specified package's build outputs instead of the root's. The outputs can then be uploaded vialake cache put --package. Also, to minimize incorrect uploads,lake cache put-stagednow requires--revto be manually set. -
#15147 introduces
comparator.jsonas the default location for the config file oflake comparator. -
#15146 rename lake challenge to lake comparator
-
#15096 ensures the output of
lean4exportis never fully materialized in comparator. Instead the output is spooled to a file and then exclusively read using streaming parsers. -
#15005 builds and exports the code
lake challengejudges inside a further restrictedbubblewrapsandbox rather thanlandrun, which stops that code from being able to access the invoking user's files and gives the steps that must not reach the network no network at all. -
#14990 adds
lake check, a challenge-less variant oflake challenge, which builds the current project's default targets, exports them, replays the result through the kernel, and fails on any use of non-standard axioms. -
#15015 adds two new configuration options that are subsets of
precompileModules:precompileLibraryforlean_libtargets andprecompileImportsfor all Lean configurations (e.g., settable onpackage,lean_lib, orlean_exe).precompileImportscompiles a module's imports but not the module itself.precompileLibrarycompiles the whole library for importers, but the library's modules do not compile their own imports during elaboration. -
#15042 fixes a Lake bug where the
moreServerOptionsconfiguration was only applied to modules outside the package (e.g., when editing scratch fiiles) and not to modules within a package. -
#14885 ships
lake challengeas a frontend tocomparator, significantly simplifying its setup.landrunremains a hard requirement with no unsandboxed mode, so the command is available on Linux only for now. -
#14933 makes
lake lint --code-qualityemit the code quality entries that linters record during elaboration. Entries logged viaLean.Linter.logCodeQualityEntryIfare attributed to the producing linter's option name and are filtered by--lint-only; entries logged viaLean.Linter.logCodeQualityEntrycarry no attribution and are always emitted, so no linter selection flag can suppress them. Disabling a recording linter (e.g.--linters=-linter.foo) suppresses its attributed entries at elaboration time, and a module shared between several lint targets contributes its entries only once. -
#14902 fixes Lake omitting imports from a module's setup when one of its imports is both an
import alland reached transitively through ameta import. Theimport allsuppressed the latermeta import, so modules reachable only through the latter were left out of the setup passed to lean. -
#14797 adds a
--fail-fastoption tolake buildthat stops scheduling new build jobs as soon as the first required target fails, letting already-running jobs drain to completion before reporting failures and exiting. Previously Lake always ran every scheduled job to the end, so a build with an early error still paid for the whole workspace. -
#14853 includes
lean.h(and its transitive includes) from an overridden Lean include directory in the trace of built object files, ensuring they are rebuilt if the header changes (e.g., when bootstrapping). To avoid such changes affecting the public API in the future,leanIncludeDir?has been removed from the public APIbuildLeanOand moved to an internalbuildLeanO. -
#14716 makes
lake lint --code-qualityrun the package code quality checks registered with the@[package_code_quality_check]attribute. The checks are discovered in each lint target's import closure and run once per target, and their results are emitted as JSON entries alongside the linter-derived ones. Additional modules providing checks can be supplied with the new--checksCLI option (which implies--code-quality) or the newcheckspackage configuration option; these modules are built and then imported alongside each linted module, so checks can be used without adding them to the package's own imports. -
#14782 overhauls the way Lake clones dependencies. Dependencies are now fetched as treeless partial Git clones of a single revision, minimizing the amount of data downloaded. In addition, switching between branches will now avoid a fetch if the new revision (and its trees and blobs) are already present in the repository. Lake also now reuses the repository directory if the dependency URL changes, avoiding file system churn and the potential loss of the dependency on a failed fetch. To further minimize disk usage, Lake will prune remote references and perform Git garbage collection on a dependency repository after fetching a new version.
Other
-
#15130 bundles the
con-lecheexternal checker with release toolchains, so it can be used as an independent checker without a separate install. -
#15099 bundles the
nanodaexternal checker with release toolchains, so it can be used as an independent checker without a separate install. -
#15048 bundles the
lean4leanexternal checker with release toolchains, so it can be used as an independent checker without a separate install. -
#15055 runs the default kernel in a privilege separated process in
lake checkandlake challenge. This does not fix any concrete security issues but merely serves as an additional hardening measure. -
#14884 adds
bin/leanchecker-paranoid, a variant ofleancheckerbuilt with allocator hardening.