These release notes describe a release candidate, not the final release. They may be incomplete and are subject to change.
Lean 4.36.0-rc1 (2026-10-14)
For this release, 216 changes landed. In addition to the 64 feature additions and 81 fixes listed below, there were 11 refactoring changes, 1 documentation improvement, 30 performance improvements, 3 improvements to the test suite, and 26 other changes.
Language
#15572 adds a hint/code action the the "[deprecated]` attribute should specify the date or library version at which the deprecation was introduced" warning.
#15499 makes the statements of generated equation lemmas (
f.eq_1,f.eq_2, …) independent of the options where the equations are first requested. A localset_optionon the first proof that used the equations could change their statements. Proofs elaborate in parallel, so the statements could differ between builds of the same file, and a proof could compile with some thread counts and fail with others.#15485 skips running non-stateful linters if the command has a syntax error and
showPartialSyntaxErrorsis disabled.#14316 makes the type class resolution cache persist across commands: a result whose query contains no metavariables, local instances or free variables and which is itself closed is kept for later commands, so identical queries there are served from the cache instead of being searched again. Such an entry is only served while the dependencies it recorded are unchanged, so new instances, unification hints and reducibility changes invalidate exactly the affected entries. On the build of the standard library this reduces type class resolution time by about 14% and total instructions by 1.2%; Mathlib gains 0.2% overall, as most of its queries involve local instances, which are not (yet) persisted.
#15399 fixes the type class resolution cache serving a result after a declaration it could have observed changed, most notably through a later
attribute [reducible]or a scoped or local reducibility attribute coming into or going out of effect. As the cache currently lives within a single command, this is rarely visible yet, but it is a necessary prerequisite for sharing the cache across commands, where such changes are routine.#15319 adds
Expr.hasAnyMVarandExpr.containsMVarfunctions analogous to the existingExpr.hasAnyFVarandExpr.containsFVarfunctions.#15355 makes
SavedState.restoredrop the type class resolution cache entries that were recorded after a tracked environment change it rolls back. The generations validating an entry roll back with the environment whileMeta.Cachesurvives the restore, so a different change made afterwards could bring the recorded generations back and let a stale entry be served; for example, an instance added and rolled back in a failed attempt could still be returned after a different instance was added. Apart from closing such edge cases, this is a necessary prerequisite for cross-command caching, whose entries must survive backtracking without being revalidated against a rolled-back state.#15354 makes a type class resolution cache entry record the state of the instances and unification hints its search consulted, so that the entry is no longer served once they change, even through environment changes that do not clear the cache. For example, an instance added by a command run through
liftCommandElabMis now found by later queries in the same computation, and so are scoped instances and unification hints activated atCoreMlevel. Apart from closing such edge cases, this is a necessary prerequisite for cross-command caching.#15325 makes type class resolution cache results depend on the options the search read. Previously a cached result was reused regardless of options within a command, so a result obtained under a term-level
set_option, for example ofsynthInstance.maxSize, could be served where the option had a different value. Options the search never reads still do not partition the cache. Apart from fixing such in-command edge cases, this is a prerequisite to caching across commands.#15313 makes realizing the equation lemmas of large mutual
partial_fixpointblocks much faster (about 13× for a block of 200 functions), by no longer re-deriving and re-checking the fixpoint equation of the whole block for every function.#11386 adds theorems tagged with
wf_preprocessfor making it possible to conveniently write functions over nested inductive types such as trees that useList.any, in the same way that they're currently able to useList.mapand other common list functions.#15286 makes a tactic marked by
deprecated_syntaxwarn no more than once per call. The tactics that its expansion uses no longer warn again. For example, ifmintrois deprecated,mintro a b cexpands tomintro a; mintro b cand printed five warnings; now it prints one.#15216 makes
inferInstanceAsreuse the existing instances of a non-exposed definition. This is done by checking the reusability of an instance in the private scope, as neither the auxiliary definitions being generated nor the ones in the existing instance are exposed in this case.#15204 rejects the
←simp-argument modifier when the argument is a simproc or a simp extension, instead of silently ignoring it.#15237 adds
Environment.realizationEnvContains forConst c, which reports whetherrealizeConst forConstcallbacks can accessc. This is the criterion for choosingforConstwhen a realization depends on multiple constants: the target must be one whose realization environment contains all others.#14537 improves on the error messages of
rfland a few other tactics that fail due to a failed definitional-equality check. There was already a mechanism that analyzed what parts of the term need to be shown explicitly so that the mismatch becomes visible, but it sometimes ran with the wrong configuration. This change fixes these problems.#15242 speeds up the elaboration of structurally recursive definitions by removing work from the construction of the
brecOnapplication that has no effect on the result.#15240 makes
Meta.isProofQuickreturn a definite answer for applications such asmatchauxiliary functions,casesOn,brecOn, and recursors, whose type is amotivevariable applied to arguments. Previously these fell through toisProof's slow path, which infers the type and reduces it to a sort.#15239 removes a redundant
abstractNestedProofspass when elaborating structurally recursive definitions, which reduces elaboration time for files with many such definitions.#15238 removes the per-declaration overhead #15232 introduced in the LCNF declaration extensions, which cost about 1% of instructions when compiling modules with many declarations, while keeping the linear-time replay of realized constants.
#15232 makes the cost of realizing a constant (e.g. the equation lemmas needed by
simp only [f]) independent of the size of the current file. Previously, files with many such realizations slowed down quadratically: on a file with 4,000 definition/theorem pairs elaboration time drops from 99 s to 32 s, and the time per pair is now constant.#15230 speeds up the elaboration of definitions using
match, and of other auxiliary definitions created withmkAuxDefinitionandmkAuxTheoremwithzetaDelta := false. On a file with 12,800 small pattern-matching definitions, cumulative elaboration time drops from about 19s to about 17s.#15228 fixes a per-command overhead in the frontend that grew linearly with the position of the command in the file, making files with many commands quadratic.
#15223 fixes a quadratic slowdown when elaborating files with many recursive definitions. The
share common exprsstep of definition elaboration hashed the whole input file once per recursive call, so a file with thousands of structurally or well-founded recursive definitions could spend most of its elaboration time there.#15091 adds
throws e => Rclauses todefcontracts, afterensures, stating the exception postcondition of the generated specification theorem. A clause binds the raised exception and, in a state monad, the state at the throw. An ascriptionthrows (e : ε) s => Rselects the exception layer of typeε, and an unascribed binder selects the outermost layer. Every unmentioned exception layer keeps the postcondition⊥, so the specification still rules out its exceptions, while an omittedensureskeeps the postcondition⊤.#15064 replaces the parser used for Verso docstrings with one that correctly tracks all details of the underlying concrete syntax. Along the way, the API for docstring extensions was improved and parser error messages were made much more actionable. This unblocks automatically formatting Verso docstring syntax.
#15199 removes the
internal.parseQuotWithCurrentStageoptions fromLean.Elab.BuiltinDo.LetandLean.Elab.Do.InferControlInfothat were introduced for bootstrapping reasons, recovering the build-instruction regression that #15090 introduced.
Library
#15654 fixes a memory leak in
String.toByteArrayandString.toUTF8that has been present for a long time but was uncovered by a recently added test.#15581 redefines the order on
Stringto be the lexicographic order of the UTF-8 encoding, not of the character list.#15659 exports the lemmas declared in
Init.Data.Int.DivMod.Pow.#15651 adds
String.fromUTF8Lossy, a function that attempts to parse aByteArrayinto aString, replacing invalid UTF-8 with the Unicode replacement character in a certain well-defined way.#15647 adds missing simprocs for bitgwise operations on
UIntX/IntX.#15628 makes
Lean.Json.parsereject JSON objects that contain the same key twice (compared after unescaping), instead of silently keeping the last value.#15594 renames error function names and other helper functions.
#15547 add RAII-based scoped lock guard
#15545 removes the allocation of the libuv handle in uv sockets, timer and signal objects.
#15571 fixes how
0is handled in theLT JsonNumberinstance. Before the fix,(0 : JsonNumber) < 5e-2would evaluate tofalse.#15523 fixes crashes in libuv timers and signals:
One shot timer and signal callbacks resolved their promise before releasing the event loop's reference to the handle (sync := true) caused this.
libuv treats a repeat period of 0 as a oneshot timer, so a repeating
Timerwith a 0 ms period fired once and every later promise stayed pending. It now ticks every millisecond.Signal.cancelonly drops the pending promise and keeps the handler listening, and a signal that arrives with no promise pending resolves the promise of the nextnext.Signal waiters resolve with the number from
Signal.toInt32on every platform, instead of the operating system's number for that signal.
#15469 makes the
Std.Httpserver generate itsDateheader in the HTTP-date format of RFC 9110 §5.6.7, for exampleSun, 06 Nov 1994 08:49:37 GMT, instead of with a numeric offset (+0000). Clients that parse HTTP dates strictly, such as Apache HttpClient's cache, Go'shttp.ParseTimeand Rust'shttpdate, did not accept the previous form.#15533 reimplements
String.toListin Lean. In doing so it also removes all hints thatString.dataever existed as it has now almost been a year since its deprecation.#15443 makes the
Std.Httpserver send responses on systems without a timezone database (for example theubuntu:24.04Docker image, which does not shiptzdata). Previously the server closed every connection without a response, because generating theDateheader looked upUTCin the zoneinfo files.#15517 changes the index of
Lean.Order.iSupandLean.Order.iInfto{ι : Sort v}, so⨆ h : P, f hover a propositionPelaborates. The lemmasle_iSup,iSup_le,iInf_le,le_iInf,iSup_applyandiInf_applytake the sameSortindex.#15175 stops
Loop.configurefrom leaking and rejectsSystem.randomvalues above 2^31-1 before allocating a huge array.#15329 adds efficient
LEandOrdinstances forByteArrayand deduces all other order instances from that.#15316 removes the duplicate
BEq (Option a)instance derived inInit.Data.Option.Basicand instead makes the one derived inInit.Coreexposed.#15315 changes the attributes at
Array.compareLexandVector.compareLexto make sure that they actually get specialized. Previously, the comparison function was just passed by closure.#15304 adds the missing order instances for
List,ArrayandVector.#15299 makes
grind, and with itvcgen … with finish, prove goals about assertions that use lattice operations more reliably. For example, thenodupproof of thevcgentutorial now closes withvcgen … with finish, as it used to withmvgen.#15414 tunes down some overeager grind e-matching annotations that would over-eagerly trigger cross-theory reasoning.
#15387 adds lemmas to the lattice theory in
Std.Internal.Orderand computation rules for predicate transformers.#15192 fixes
ContextAsync.raceandContextAsync.raceAll,ContextAsync.runandContextAsync.backgroundcancellations andNotify.notifyOneno longer spends its wakeup on a waiter whose select was already decided.#15274 adds the missing order instances on
BitVec.#15174 solves multiple issues in the TCP and UDP modules.
#15266 adds logical models for the
Float.minimumandFloat.minimumNumberoperations that were added in #15257.#15257 defines
Float.minimumandFloat.minimumNumber, which correspond to the IEEE-754-2019 operations of the same name. It redefinesMin Floatto meanFloat.minimum.#15115 exposes
Char.ordinal,Char.ofOrdinal,Char.succ?andChar.succMany?, so that they reduce across module boundaries. Ranges overCharare built fromChar.succ?andChar.succMany?via theUpwardEnumerable Charinstance, so previouslycbvand kernel reduction got stuck on these in any file using the module system.#15247 introduces a basic FFI module for CaDiCaL as well as a small wrapper around it to interact with our
Std.SatAPI.#15252 removes the
withPtrEqfunction.#15217 renames the
EPredtype variable toEPostsand theepostvalue variable toeposts, since they denote a bundle of exception postconditions rather than a single predicate, and adds deprecated_arg aliases so code passingEPredby name keeps compiling.#15171 renames several
Std.WPnames for consistency and deprecates the old ones. TheWP.wpTransprojection becomesWP.trans, its monotonicity fieldWP.wp_trans_monotonebecomesWP.trans_monotone, and thewpTrans_apply_eqlemma becomestrans_apply_eq. The weakest-precondition monotonicity lemmasWP.wp_consequence/wp_econsbecome a uniformwp_monotonefamily:wp_monotonefor both channels,wp_monotone_postandwp_monotone_epostfor one channel,wp_monotone_botfor the empty exception postcondition, and the_letransitivity variants.#15201 refactors the AIG to CNF transformation from only acting on
AIG Natto acting on genericAIGtypes. This has two advantages:We don't have to perform the
relabelNatstep anymore and the CNF representation gets slightly more efficient. This is because instead of using theNatfrom theAIGatom as the identifier we now use the indices into the AIG as an identifier and thus avoid producing one tseitin atom per AIG atom.In the future we will be able to use the AIG to CNF API in an incremental fashion across multiple runs. Towards this we also already introduce the
State.castAPI for reusing a cache state across runs.
#15067 extends the frame mechanism to the exception channel of
Std.WP. For example, a program logic whose exceptional exits carry state now supports a frame rule such as⦃P⦄ x ⦃Q; E⦄ → ⦃F ⋆ P⦄ x ⦃F ⋆ Q; F ⋆ E⦄. The frame closure, the frame condition, and the frame rule gain a companion operatoropE : R → EPred → EPredfor the exception postcondition that defaults to ignoring the frame.#14793 fixes #14776
#14795 fixes two issues in recv functions of the UDP and TCP modules.
Tactics
#15678 fixes
grindfailing to prove goals such asc = b → a ≤ b + c + d → a ≤ b + b + din an ordered ring when theSym.simpnormalizer is enabled (set_option backward.grind.normalizer false). The normalizer rewritesb + b + dto2 * b + d, so the two inequalities are no longer congruent afterc = b, and no module related the two sums.#15675 fixes
grindfailing with the internal error "structure does not have a lawful LT instance" (or "structure does not support LT") whenlinarithhas to reason about<on a type without aLawfulOrderLTinstance. For example,[IntModule α] [LE α] [LT α] [IsLinearOrder α] [OrderedAdd α] (a b c : α) : a < b → b < c → a < cnow fails with a regulargrind failedmessage.#15672 fixes
grindfailing to usek⁻¹for a numeralkin a field when theSym.simpnormalizer is enabled (set_option backward.grind.normalizer false). For example,[Field R] [IsCharP R 0] (h : f (a * 2⁻¹) = 1) (h2 : a = 2 * b) : f b = 1was not proved.#15642 adds the option
Grind.Config.distrib(defaultfalse) to theSym.simp-basedgrindnormalizer. By default, a product of two non-constant polynomials such asx * (y + 1), a power of a sum, and the inverse of a product are kept as atoms whose factors are normalized, as the legacy normalizer does;grind +distribexpands them into polynomial normal form. Linear arithmetic procedures treat nonlinear terms as atoms, so keeping them unexpanded preserves facts such asx * (y + 1) = x * zaftery + 1 = zis derived.#15624 makes
grindputk#wliterals with a symbolic widthwin theOfNat.ofNatform it uses forBitVecliterals, so with theSym.simp-based normalizer theorems such asBitVec.sdiv_zeroapply tox.sdiv 0as they do with the legacy normalizer.#15623 makes the
Sym.simp-basedgrindnormalizer rewrite dependent propositional binder domains, so∀ h : i < a.size, q hbecomes∀ h : i + 1 ≤ a.size, ...as with the legacy normalizer.#15619 fixes a panic in the
Sym.simp-basedgrindnormalizer onmatchequations whose patterns contain arithmetic, e.g.grind [h.eq_def]for a function matching onVec α (n + 1 + 1).#15618 fixes spurious
ring term with unexpected instanceissues ingrindfor casts over abstract rings, e.g.(m + n) • a = m • a + n • awith[CommRing R], and the corresponding failures of theSym.simp-based normalizer.#15616 adds
BitVec.zero,BitVec.ofNat _ (Bool.toNat _), andBitVec.carryto the list of supported terms bybv_decide#15612 fixes
grindlosing a hypothesis whose type is marked[grind cases eager]when the hypothesis is introduced byintrosor asserted as a parameter fact. Only the constructor fields survived in each branch, soexample : P → ¬P → False := by grindandgrind [(mkP)]on the goalPfailed for a predicatePwith field-less constructors.#15611 fixes
grind?failing with "proof contains unresolved internal metavariable" when a parameter fact has a type marked[grind cases eager]. Asserting such a fact splits the goal during initialization, andgrind?only produced a script for the first goal. It now handles every goal, and the suggested script carries one·block per goal.#15609 makes
grind [mkP]accept a theorem whose statement is not a∀and is not indexable, e.g.,theorem mkP : Pfor a nullary inductive predicateP. The parameter is asserted as a fact, so the workaroundgrind [(mkP)]is no longer needed.grind?andfinish?keep such identifiers in their suggestions.#15606 fixes a spurious "generated tactic cannot close the goal" warning from
finish?when its parameters alone close the goal, e.g.grind => finish? [inj f_inj].#15602 fixes
grind?andfinish?dropping parameters such asinj f_injandfunCC qfrom their suggestions, which then failed when pasted.#15600 fixes
grindfailing on instances wrapped in@[implicit_reducible]definitions.#15599 fixes a
grindinternal error,ring term has not been internalized, triggered when the ring solver learned a disequality such asa ≠ bwhile a term containinga - bwas still being internalized. The Rabinowitsch step then built(a - b) * (a - b)⁻¹and internalized it before(a - b)⁻¹had anENode. Closes #15590.#15589 fixes a panic in
grind ringwhen propagating implied equalities in rings withoutNoNatZeroDivisors, such asBitVec n.#15588 fixes a
grindinternal error (ring term has not been internalized) triggered when a user-definedPow Int Natinstance with higher priority is in scope. Mathlib'sMonoid.npowinstance onInthits this case after #15506.#15555 makes
vcgentake the program type and theWPinstance from the goal'swpapplication when it applies a spec. Thusvcgenworks on a goal whose program is not of the instance's program type, for example a programe : Expin a goalwp (Prog := Tagged n Exp) e Q Efordef Tagged (n : Nat) (α : Type) := α.#15582 makes
cbvevaluatecond(bif c then a else b) lazily, likeiteanddite. Before this change,cbvevaluated both branches ofcond, even when the condition was the literaltrue. An expensive or non-terminating branch that the condition does not select cost time or exhaustedcbv.maxSteps. The fix gives thecondsimproc pattern all four arguments ofcond.#15497 adds a guard to
WP.Frames, so thatvcgencan frame a resource across a program whose footprint depends on the state.#15526 fixes a kernel error when
sym => simprewrites inside a non-dependent arrow (e.g.x + 0 = y → p) in a file usingmodule.#15515 makes
vcgensplit a spec preconditionfun _ => ∃ h : i < xs.size, Q () xs[i]into the VCi < xs.sizeand the entailment forQ () xs[i]under a hypothesish, so that thewpof the program tail in the body is stepped. The preconditionfun _ => ⨆ h : i < xs.size, Q () xs[i]splits the same way.#15524 fixes a
grindinternal error (unknown free variable) on hints such asgrind [h _ _ pf]whose only hole is a proof. It brokeList.pairwise_iff_forall_infixin Mathlib after #15506.#15506 fixes a
grindinternal error (semiring term has not been internalized) triggered when power propagation rebuilds a term whose arguments are still being internalized (#15479).#15503 fixes
symwitharithandgrindwith theSym.simp-based normalizer on goals whose equations are over a type inSort u, such as{α : Sort u} (a b : α). The ring classifier computed the universe level of the carrier withgetDecLevel, which fails with "invalid universe level, u is not greater than 0" for such types; it now usesgetDecLevel?and reports that the type has no ring structure, as the order classifier already did.#15491 makes
vcgenrecognize more@[spec]preconditions as conjunctive in the schematic postconditionQ/E.vcgenapplies such a spec directly and runs no frame inference for it.#15487 makes
vcgensplit a pointwise∧in a spec's precondition like a lattice meet⊓.#15483 makes
Sym.simprewrite a rawNatliteral that is not the argument of a numeral into theOfNat.ofNatform, asMeta.simpdoes. Rewrite rules whose pattern variable binds the raw literal of a numeral, such as the[grind hom]rule(OfNat.ofNat a : Fin n).val = a % n, produced terms likenat_lit 0 % (p + 1)that no later step recognized as a numeral, sogrindwith theSym.simp-based normalizer failed on goals overFin (p + 1)andBitVec n.#15482 makes
grindprove goals overUIntNandBitVecthat multiply by a numeral close to the modulus, such as-1 * aor4294967295 * aoverUInt32; these previously exhausted theliastep budget. The[grind hom]injection intoNatturned(c * a).toNatintoc * a.toNat % 2^w, andcutsatsplits on the coefficientcwhen it has to eliminatea.toNatexactly, while(-a).toNatis(2^w - a.toNat) % 2^w, with coefficient1.#15478 fixes a kernel-rejected proof in
grind's ring module. For an inversea⁻¹with a numerala,processInvassertsa * a⁻¹ = 1ora⁻¹ = 0with theorems whose numeral isdenoteInt k, i.e. anOfNatnumeral or its negation, but the recognizer also accepted↑(0 : Nat),↑(3 : Int),-0, and- -3, which are only propositionally equal todenoteInt k;Field.inv_zero : 0⁻¹ = 0was then pushed as a proof of(↑0)⁻¹ = ↑0.#15476 makes the
Symarithmetic normalizer rewrite a cast of a numeral (↑(0 : Nat),↑(-2 : Int)) to the numeral of the carrier wherever it occurs, as the legacygrindnormalizer does. Casts were interpreted only inside+,*, ...; a cast standing alone, as a function argument or under⁻¹, was kept, so(↑0)⁻¹reached the ring solver ofgrind, which appliedField.inv_zeroto it and produced a proof rejected by the kernel (grind_9321withbackward.grind.normalizer := false).#15475 makes the
Sym.simp-basedgrindnormalizer rewrite everyFinliteral to its canonical form. A literal such as@OfNat.ofNat (Fin (1 + 1)) 0 Fin.instOfNat, with a nested numeral as value and a ground type index, was left as is, andgrindtreated it as a literal distinct from(0 : Fin 2), producing proofs rejected by the kernel (grind_canon_ofnat).#15473 adds a
Sym.dsimppass afterSym.simpin theSym.simp-basedgrindnormalizer, so that beta-redexes and projections of structure instances in the fixed arguments of an application are reduced, as the legacy normalizer does withSimp.Config.dsimp.Sym.simpdoes not visit these arguments, and a redex left in the implicit objects of a morphism composition made the goals ofFunctor.flipingrind_cat.leanmissNatTrans.naturality.#15467 makes the projection reduction of
Sym(Sym.reduceProjApp?, used bySym.simp,Sym.dsimp, and the canonicalizer) expose the structure argument at.reducibletransparency, assimpdoes, instead of the ambient one. With theSym.simp-basedgrindnormalizer, E-matching patterns are normalized at attribute time at.defaulttransparency, so a pattern such as(asIso f).homunfoldedasIsoand collapsed to a variable, and the theorem was rejected withinvalid pattern.#15466 fixes two ways a
grindcongruence table entry could go stale, which madegrind.debugfail nondeterministically on goals withmatchconditions, and adds a deterministic invariant check for the table.#15465 makes the
Sym.simp-basedgrindnormalizer respect the annotations onmatchequations:Grind.simpMatchDiscrsOnly(only the discriminants of thematchare simplified) andGrind.PreMatchCond(the condition is left as is). E-matching instances of match equations, e.g. fromf.eq_def, now work withbackward.grind.normalizer := false, where they used to fail or produce proofs rejected by the kernel.#15464 adds the option
backward.grind.normalizer. It defaults totrue, which selects the legacysimp-basedgrindnormalizer; withfalse,grindnormalizes hypotheses, the target, E-matching instances, generalized patterns, and E-matching patterns at attribute time with theSym.simp-based normalizer developed withgrind_norm. The default behavior is unchanged.#15463 extends
Sym.Simp.evalGroundto evaluateInt.toNat,Int.natAbs, andtoNatonUInt8,UInt16,UInt32, andUInt64literals, e.g.(-7 : Int).natAbsbecomes7.grindis affected too, since the[grind hom]rewriter usesevalGround.#15461 makes the realized constants required by
bv_decideprivate to avoid name clashes while importing multiple modules that realized them.#15460 extends
Sym.Simp.evalGroundto evaluateString.pushandString.singletonon literals, e.g."abc".push 'd'becomes"abcd".grindis affected too, since the[grind hom]rewriter usesevalGround.#15458 makes the
Sym.simp-basedgrindnormalizer produce bit-vector literals inOfNat.ofNatform, the formgrindkeeps, instead ofBitVec.ofNat. Ground bit-vector terms such as3#8 + 5#8,3 &&& 5,(3#8).zeroExtend 16, and300#8are now evaluated to the same literals as the legacy normalizer.#15448 fixes a kernel error in
grindon goals mentioning bothChar.ofNat 97and'a', and makesgrindevaluateChar.toNat,Char.val, andChar.ofNatwhen the argument is equal to a literal, e.g.y = 'a' → y.toNat = 97andn = 97 → Char.ofNat n = 'a'.#15445 makes
grindprove goals aboutFin.log2and about the scopedNatandIntcasts intoFin nthrough theirvalinterpretation, such as(x.log2).val = Nat.log2 x.val,((i : Int) : Fin n).val = (i % n).toNat, and((k : Nat) : Fin n).val = k % n.#15434 makes the
Sym.Arithnormalizer solve equations over commutative rings with a nonzero characteristic, such asFin n,UInt8, andBitVec w, when it runs in the mode used by thegrindnormalizer. An equationk * m + k' = 0ork * m₁ = k * m₂whose coefficientkis invertible modulo the characteristic is solved for the monomial: inFin 5,4 * x + 2 = 0becomesx = 2, as2 * x = 6already becomesx = 3overInt.#15433 extends
Sym.Simp.evalGroundto evaluate theFinfunctions on literals:succ,castSucc,rev,last,val,pred,castAdd,addNat,natAdd,castLT,castLE,subNat,mk, andofNat. It also normalizesFinliterals:(7 : Fin 5)becomes2, and a type index that is not a numeral, as in(4 : Fin (5 + 1)), is evaluated.Finarithmetic and comparisons were already supported.#15398 introduces a CEGAR/Lemmas on Demand procedure into bv_decide for supporting uninterpreted functions even without interacting with
grindmode. Towards this it refactors the solving pipeline to be fully incremental and makes use of incremental SAT solving. The congruence feature is available behindbv_decide +uf, it is currently limited to:Only functions that return Bool/BitVec
Only performing congruence reasoning on non-dependent Bool/BitVec arguments (see the bv_uf test for examples of what this entails)
#15420 fixes a
grindregression introduced by #15378: a goal such asImp (∀ i, i < n → ∃ j, j = i + 1 ∧ p j) (p 4), whereImp a bunfolds toa → bthrough a[grind norm]rule, was no longer proved, while the same goal written with a syntactic→still was.grindnow normalizes the goal before negating it, and the normalizer no longer rewrites(∀ x, p x) → qinto(∃ x, ¬p x) ∨ q.#15419 extends
Sym.Simp.evalGroundto evaluateChar.valon character literals, e.g.'a'.valbecomes97. It complements #15411, which added the otherCharoperations.#15417 fixes an
Application type mismatcherror ingrindwhen the homomorphism rewriter translates an equality whose hypothesis mentions alet-bound literal, e.g.b = m.cpopwithlet m : BitVec 2 := 1#2. Closes #15388.#15411 extends
Sym.Simp.evalGroundto evaluateCharoperations on character literals:toNat,toLower,toUpper,toString,isWhitespace,isUpper,isLower,isAlpha,isDigit,isAlphanum,==, and!=. It also convertsChar.ofNat ninto a character literal whennis a numeral, so that a character has a single representation.#15409 adds
Sym.Simp.reduceControl, a variant ofsimpControlthat does not block the simplifier: it simplifies the condition ofite/dite/condand the discriminants ofmatch, reduces the term when they are decided, and otherwise lets the simplifier visit the branches. TheSym.simp-basedgrindnormalizer uses it in place ofreduceMatcher.#15405 fixes a sharing violation in the
arithsimproc ofSym.simp, e.g., ona + f (b + 0). Withsym.debugenabled, it surfaced as a panic inassertShared.#15322 makes
Sym.introsintroduce a binder whose name starts with__as an implementation-detail local, as the elaborator does for such binders.vcgenintroduces the join point__do_jpof adoblock withSym.intros, so its verification conditions no longer show the join point and its body in the local context.#15389 fixes a sharing violation in
Sym.dsimpwhen it descends into afun,∀, orletbinder whose bound variable occurs in the body or in a later binder type. Withsym.debugenabled, the violation surfaced as a panic inassertShared.#15383 adds the
fallbackmodifier to the[grind hom]attribute. A fallback rule is applied to a term only when no other homomorphism rule matches it, using the second tierSym.simpalready has foreq_defunfolding.#15378 fixes the
pushNotsimproc of thegrindnormalizer so that¬(p → q)becomesp ∧ ¬qand¬∀ x, p xbecomes∃ x, ¬p x. These rewrites have been dead code since #9271: the simproc matched theforallEcase against the whole¬pinstead ofp, and checked the domain with the syntacticExpr.isProp, which is false for a proposition variable. The sameisPropcheck is fixed in theSym.simp-based normalizer.#15377 removes the
grind =attribute from the lemmas that are already[grind hom]rules, so thatgrindderives thetoNat,toIntandvalimage of a term once, through the homomorphism rewriter, instead of twice.#15370 makes
grindprove goals that mention theBool-valued signed comparisonsBitVec.sltandBitVec.sle, such as(x < y) ↔ (x.toBitVec.slt y.toBitVec = true)forInt64,x.slt y = true ↔ x.toInt < y.toInt,x.slt y → y.sle z → x.slt zandx.slt y = !(y.sle x).#15369 makes
grindprove goals about the signed value of a bitwise complement, such as(~~~x).toInt = (~~~x.toInt).bmod (2 ^ 16)forBitVec 16and the corresponding goals for the fixed-width signed integer types, as well as plainIntfacts such as~~~a = -a - 1.#15367 makes
grindprove goals about the signed value of a bit vector shifted left by a variable amount, such as(x <<< n).toInt = (x.toInt <<< n).bmod (2 ^ 16), and lets nested signed results compose, such as((x <<< m) <<< n).toInt = (x.toInt * 2 ^ m * 2 ^ n).bmod (2 ^ 16),((x + y) <<< n).toInt = ((x.toInt + y.toInt) * 2 ^ n).bmod (2 ^ 16)and((x * y) * z).toInt = (x.toInt * y.toInt * z.toInt).bmod (2 ^ 16).#15366 makes
simp [f]anddsimp [f]insym =>mode behave like the default simplifier whenfis a definition or a conditional theorem. Side conditions of conditional theorems, such as the equational theorems of definitions with overlapping patterns, are now discharged usinggrind. Non-recursive definitions are unfolded when no equational theorem applies.dsimpaccepts definitions andrfl-theorems. Reducible definitions are ignored with a warning instead of failing.#15363 fixes a
grindfailure on goals containing-0 : Int, such asf (-((0 : Nat) : Int)) = f 0, wheregrindproduced a proof rejected by the kernel.-0is now normalized to0.#15365 makes
grindrelate the unsigned value of an arithmetic shift to the signed value of its operand: goals such as((x.sshiftRight n).toNat : Int) = (x.toInt / 2 ^ n) % 2 ^ 16overBitVec 16are now proved.#15364 makes
grindreason about bit-vector rotations through its[grind hom]injection intoNat: goals such as(x.rotateLeft n).toNat = (x.toNat * 2 ^ (n % 16) + x.toNat / 2 ^ (16 - n % 16)) % 2 ^ 16overBitVec 16, and therotateRightcounterpart, are now proved.#15362 fixes
Sym.DSimp.zetaDeltaandSym.DSimp.zetaDeltaAllso that they unfold a let-bound variable that appears as the head of an application. Previously,dsimp [foo]anddsimp [*]ingrind/sym, and thebv_decidenormalization passes, left terms such asfoo a buntouched whenfoo := fun x y => x + ywas a local definition, whileMeta.DSimpreduces them toa + b.#15350 makes
grindreason about bit-vector exponentiation through its[grind hom]injections: goals such as(x.pow z).toNat = x.toNat ^ z % 2 ^ 16and(x.pow z).toInt = (x.toInt ^ z).bmod (2 ^ 16)overBitVec 16are now proved.#15348 makes
grindreason about bit-vector concatenation through its[grind hom]injection intoNat: goals such as(x ++ y).toNat = x.toNat * 2 ^ 8 + y.toNatoverBitVec 8are now proved. It also fixesSym.simprules whose left-hand side hasNatarithmetic in a type index, such as the widthm + nof(x ++ y).toNat, which never fired on terms whose width had been normalized to a numeral.#15347 lets the
cutsatsolver ingrindreorder its variables more than once during a proof search. Variables created after the first model search, e.g. by later E-matching rounds, were appended to the variable order; when such a variable has a large coefficient in its bound constraints, the Cooper case split that eliminates it enumerated that coefficient and exhaustedliaSteps. For example,(x <<< 1).toInt = (x.toInt <<< 1).bmod (2 ^ 16)overBitVec 16failed because the quotientx.toNat / 2 ^ 16, introduced by the instance ofBitVec.toInt_eq_toNat_bmod, ended up last in the order with a coefficient of2 ^ 16. Such goals are now proved.#15345 adds support for
&&&with a constant mask togrind, for masks of the form1…10…0(a run of ones followed by a run of zeros), overNatand, through the[grind hom]injections,BitVecand the fixed-width integer types. The mask is rewritten into%,/, and*by literals, whichcutsatsupports:x &&& (2^n - 1)becomesx % 2^n, andx &&& ((2^n - 1) * 2^k)becomesx / 2^k % 2^n * 2^k. For example,(x &&& 0xe0).toNat = x.toNat / 32 % 8 * 32overBitVec 8andx &&& 0xC0 = 0 → x &&& 0x40 = 0overUInt8are now proved bygrind. Masks with holes (e.g.0b101) andIntare not handled yet.#15344 makes
Sym.Arith.normalize?(and hence thearithsimproc ofSym.simp) cancelx * x⁻¹for an atomxwhen a discharger provesx ≠ 0, in any field.a * b / abecomesb,a ^ 3 / abecomesa ^ 2, anda / (2 * a)becomes2⁻¹. The simproc DSL acceptsarith with d, soarith with grinduses the goal's hypotheses. A result that asked for a side condition is context-dependent.#15343 makes numeral inverses rational coefficients in
Sym.Arith.normalize?(and hence in thearithsimproc ofSym.simp) for fields of characteristic zero.a / 2 + a / 2now normalizes toa,a / 2 + b / 3to(3 * a + 2 * b) * 6⁻¹, and relations become denominator-free:a / 2 + b / 3 = 1becomes3 * a + 2 * b = 6. Without anIsCharP _ 0instance, numeral inverses remain atoms.#15259 registers
BitVec.toNat_ofIntas a[grind hom]rule, sogrindcan reason about the unsigned value ofBitVec.ofInt. In particular,cutsatcan now follow carries computed throughBitVec.ofInt.#15262 registers
BitVec.toInt_signExtendas a[grind hom]rule, sogrindcan reason about the signed value ofBitVec.signExtend: narrowing reducestoIntmodulo2 ^ v, and widening leaves it unchanged.#15311 fixes
grindnever activating E-matching theorems whose ground pattern contains a lambda, a dependent∀, or a nonparametric literal such as aCharor a negativeInt. For example,@[grind .] theorem myPred_of_true : myPred (fun _ => True)was never used even when the goal containedmyPred mySetandmySet = fun _ => True, and@[grind =] theorem fc_a : fc 'a' = 1could not provefc 'a' = 1. Passing such theorems explicitly withgrind [myPred_of_true]worked, since explicit parameters bypass symbol indexing.#15310 fixes
Sym.dsimpproducing terms with dangling free variables when a binder type depends on an earlier binder, e.g.∀ k : Nat, k < 32 → .... This causedbv_decideto fail withunknown free variable. Closes #15195.#15046 fixes
Lean.Meta.Sym.Simp.toHave, which passed the dependencies of ahavetoExpr.betaRevin forward order even thoughbetaRevexpects them reversed. Anyhavewhose value depended on two or more earlierhaves of the same telescope was rebuilt with its dependencies swapped, producing an ill-typed proof term that the kernel rejects with an application type mismatch.#14827 makes
vcgenstate exception postcondition verification conditions pointwise for more exception postcondition types, and makesthrowsclauses work for custom types. ForExcept ε, a spec with the exception postconditionfun e => e = "x"now yields∀ e, e = "x" → …in place of an entailment between two functions. A custom type such asstructure Thrown where onThrow : String → Propgets the same pointwise verification conditions from aToEStackinstance, which converts it to an exception postcondition stack. With anEPostSlotinstance, the type also supportsthrowsclauses ofdefcontracts.#15290 moves the syntax of the
vcgentactic to the new moduleStd.WP.Tacticand deprecates the tactics of theStd.Doproof mode.import Std.WPnow providesvcgen. Each call of a proof mode tactic, such asmintroormspec, reports one deprecation warning that points tovcgen. The optionmvcgen.warningis deprecated because it has no effect.#15298 teaches
Sym.Arith.normalize?(and hence thearithsimproc ofSym.simp) the field rewrites that need no side conditions. In aField,a / bbecomesa * b⁻¹, and inverses are pushed down to the atoms:(a * b)⁻¹ ↦ a⁻¹ * b⁻¹,(-a)⁻¹ ↦ -a⁻¹,a⁻¹⁻¹ ↦ a,0⁻¹ ↦ 0,1⁻¹ ↦ 1, after normalizing the term under the inverse.x⁻¹for an atom or numeralxis then an atom of the polynomial, soa / b + a / bnormalizes to2 * (a * b⁻¹).#15297 extends
Sym.Arith.normalize?(and hence thearithsimproc ofSym.simp) to non-commutativeRingandSemiringstructures. Monomials keep the order of their factors, soa * bandb * astay distinct and(a + b)^2normalizes toa^2 + a*b + b*a + b^2. Terms, relations, and the characteristic are supported as for the commutative case.#15296 fixes the
arithsimproc ofSym.simpso that reflexive relations between atoms are closed:x ≤ xandx = xnow normalize to0 ≤ 0and0 = 0and are closed bypost, likex + 0 ≤ xalready was.#15295 makes the relation normalizer of
Sym.Arith(and hence thearithsimproc ofSym.simp) use the characteristic of the ring, so that256 * u + v = vonUInt8normalizes to0 = 0andu + 2 = vis recognized as already normal. Previously only terms were reduced modulo the characteristic.#15293 extends
Sym.Arith.normalize?(and hence thearithsimproc ofSym.simp) to relationslhs = rhs,lhs ≤ rhs, andlhs < rhsover aCommRingorCommSemiring. Everything is moved to one side and split by sign, sox + y = z + 2 * xbecomesy = x + z, anda ≤ b + abecomes0 ≤ b; when both sides normalize to the same polynomial, the goal is closed.#15292 adds the
arithsimproc toSym.simp, which normalizes ring and semiring terms into polynomial normal form usingSym.Arith.normalize?. It is opt-in throughregister_sym_simpand intended as apresimproc: it sees the maximal arithmetic subtree first, simplifies its atoms with the full simplifier, and reifies it once. Normal forms are final (done := true) and cached.#15280 makes
Expr.toPolySandExpr.toPolyS_nchandle a zero exponent likeExpr.toPolydoes, sox ^ 0in a semiring normalizes to1instead of a monomial with exponent0. TheSym.Arithnormalizer now rewritesx ^ 0 : Natto1.#15279 adds
Sym.Arith.normalize?, which rewrites a term of aCommRingorCommSemiringinto polynomial normal form with a proof by reflection. It is the normalizer thatSym.simpwill use for arithmetic; this PR provides theSymM-level API and its tests, not the simplifier integration.#15267 fixes regressions in bv_decide around projection and match reduction.
#15263 moves the classification of order structures (
LE,IsPreorder,LT,LawfulOrderLT,IsPartialOrder,IsLinearPreorder,OrderedRing) fromgrind's order module intoSym.Arith, so otherSymcomponents can reuse it. No user-visible change.#15190 makes the
grindring solver use the ring classification, operator caches, reification, and denotation fromSym.Arith, and deletes the copies it kept underGrind/Arith/CommRing. A type is now classified once perSymMrun instead of once per goal, so every goal of agrindrun and every tactic of asymblock share the result. Code that drivesgrindprogrammatically sees the new layout: the per-goal ring state holds solver data only,Resultcarries theSym.Arithring records, andgoalToMessageDataandevalChecktake them.
Compiler
#15516 removes the limit of 255 object fields per constructor, and thus per structure.
#15539 fixes an RC race condition that occurs exclusively in debug code associated with the IR interpreter by marking some global objects as persistent. Additionally, it marks one other global object as persistent that is currently not used in a multi threaded way but might be in the future for hardening purposes.
#15520 makes the symbol cache return references instead of copying the cache contents out. In addition, it removes uses of references after potential recursive calls into the interpreter. While this is not necessary with the current bucket-based
std::unordered_mapa futureflat_mapdesign might invalidate these references due to re-hashing which we want to avoid.#15131 introduces new fast paths to high arity closure applications. Previously, they would always maintain ownership of their arguments as they pass them to their underlying function pointer, causing non-linearities. This PR introduces the same fast paths as with the other low arity closures for passing the arguments linearly if the closure is passed linearly. Furthermore, closures may now tail call, exact behavior is platform and compiler dependent at this point in time and should not be relied upon.
#15493 makes the IR interpreter resize its stack frames correctly upon function entry instead of dynamically on variable access. This ensure that variable access remains branch free.
#15486 pre-computes some information about IR decls, to be used in future work.
#15407 makes
ctorIdxjust read the constructor tag in almost all cases. There are a few notable exceptions to be had here:types with only one ctor may end up being unwrapped to other types if they turn out to be trivial so we want to statically return
1built-ins like
NatandInt
#15277 makes constant folding on
USizeslightly smarter by relaxing the forced equivalence between theUInt32andUInt64result of the same operation. Previously we required theUInt32output and theUInt64output to be literally the same. However, in order for aUSizeliteral to be interpreted the same on both platform widths, we only need theUInt32truncated version of theUInt64output to be the same as theUInt32output.#15276 changes the heuristic for the "equivalent case" folder in LCNF simp. Previously it compared every alternative with every other alternative in a cases up to alpha equivalence, causing O(n^2) alpha equivalence queries per cases. The new heuristic is inspired by GHC and instead takes either:
the default or
the first alternative with all of its parameters unused
#15275 introduces constant folders in a more systematic fashion that attempt to cover all interesting and simple connections for
Nat,UInt, and a few special cases in other types.#15256 extends the derived values analysis by allowing the compiler to take any form of potentially relevant allocation as a parent for a derived value.
#15251 changes the way that ExplicitRC determines derived borrows. Previously it would eagerly compute the entire derived values tree for the entire program. This would mean that a single variable
swould always have all derived values possible in all control flow branches as its children. As all of these children are walked upon use of the value, this causes a lot of excess work. The new pass avoids this by only keeping the derived values in the current program scope around in the tree.
Server
#15331 fixes the goal view showing "no goals" instead of the goal state after an empty
grind =>,sym =>orimpossible byblock, e.g. right after the token or on the following indented line where the next tactic is about to be typed.
Lake
#15630 adds the options
--add-downstream <MODULE>and--add-public-downstream <MODULE>tolake shake, which ensure that the given module is imported, directly or transitively, in every checked module where that does not create an import cycle. This gives projects a built-in way to check and enforce that a module providing e.g. linters or code actions, such as a project'sInitmodule, is in effect everywhere.#15474 makes Lake fetch facets named by partial target keys under their resolved keys, so package-elided, package-named and module requests share the same build-store entries as direct target requests.
#15231 fixes an issue where Lake would not reuse the cached
.ltarof a module for-oif its contents where not already cached and/or the package was platform independent.#14906 splits
leanircode generation into a separate Lake job and target . TheleanArtsfacet on modules and libraries id deprecated and replaced with two distinct facets:elabArtsandirArts. For backwards compatibility, uses ofleanArtsdelegate toirArts.#12545 adds a
lake samplycommand that records a CPU profile of a Lean executable using samply, symbolicates addresses, demangles Lean compiler names, and serves the result to Firefox Profiler.
Other
#15559 stops a
modulefrom exporting constants such asfoo.eq_1when they were realized only inside a proof, so that such proofs no longer trigger downstream rebuilds. Importing modules realize these constants again on demand.#15558 stops a
modulefrom exporting the entries of declaration-keyed environment extensions, such as projection function info, that belong to private declarations, so that changes to them no longer trigger downstream rebuilds.#15565 fixes a panic in
leanirand three smaller issues found while enablingcompiler.postponeCompilefor core.#15512 speeds up module system imports outside the language server and reduces their memory use by no longer building a redundant copy of the imported environment extension entries. Mathlib import time is reduced by ~6%.
#15150 ensures that
Lean.toolchainincludes avfor release versions, e.g."leanprover/lean4:v4.34.0-rc2", to match the standard format of toolchains inlean-toolchainfiles. (This also enablesToolchainVer.ofStringto succeed onLean.toolchain.)
Uncategorised
#15002 adds HTML-like syntax to
Lean.Data.Html.Syntax, together with an elaborator from the syntax intoLean.Data.Html.#14935 adds a type
Lean.Data.Htmlof HTML forests — aimed primarily at authoring HTML (as opposed to internal representation in a web server, etc) — as well as a renderer that produces aStringobject.