Lean 4.30.0-rc1 (2026-04-01)
For this release, 298 changes landed. In addition to the 120 feature additions and 68 fixes listed below, there were 17 refactoring changes, 8 documentation improvements, 19 performance improvements, 12 improvements to the test suite, and 54 other changes.
Language
-
#13188 extends the
missingDocslinter to detect and warn about empty doc strings (e.g./---/or/-- -/), in addition to missing doc strings. Previously, an empty doc comment would silence the linter even though it provides no documentation value. Now empty doc strings produce a distinct "empty doc string for ..." warning, while@[inherit_doc]still suppresses warnings as before. -
#13192 fixes the handling of anonymous dependent
if(if _ : cond then ... else ...) insidedoblocks when using the new do elaborator. -
#13011 adds a
@[deprecated_arg]attribute that marks individual function parameters as deprecated. When a caller uses the old parameter name, the elaborator emits a deprecation warning with a code action hint to rename or delete the argument, and silently forwards the value to the correct binder. -
#13153 registers the new
spec_invariant_typeattribute alongside the oldmvcgen_invariant_type, renames internal identifiers, and replaces the hardcodedInvariantcheck inSpec.leanwithisSpecInvariantType. -
#13117 re-enables
#print axiomsunder the module system by computing axiom dependencies at olean serialization time. It reverts #8174 and replaces it with a proper fix. -
#13142 replaces the per-level
OLeanLevel → Array αreturn type ofexportEntriesFnExwith a newOLeanEntries (Array α)structure that bundles exported, server, and private entries together. This allows extensions to share expensive computation across all three olean levels instead of being called three separate times. -
#13120 reverts the
mvcgen witnessessyntax addition and undoes the back compat hack inelabMVCGen. -
#13111 reverts #12882 which added the
@[mvcgen_witness_type]tag attribute andwitnessessection tomvcgen. Théophile Wallez confirmed he doesn't need this feature and can get by withinvariants, so there is no use in having it. -
#13059 switches
normalizeInstancefrom usingisMetaSectionto the existingdeclName?pattern (already used byunsafeinBuiltinNotation.leanandprivate_decl%inBuiltinTerm.lean) for determining whether aux defs should be markedmeta. -
#12973 makes theorems opaque in almost all ways, including in the kernel.
-
#12987 extracts the functional (lambda) passed to
brecOnin structural recursion into a named_fhelper definition (e.g.foo._f), similar to how well-founded recursion uses._unary. This way the functional shows up with a helpful name in kernel diagnostics rather than as an anonymous lambda. -
#13043 fixes a bug where
inferInstanceAsand the defaultderivinghandler, when used inside ameta section, would create auxiliary definitions (vianormalizeInstance) that were not marked asmeta. This caused the compiler to reject the parentmetadefinition with:Invalid `meta` definition `instEmptyCollectionNamePrefixRel`, `instEmptyCollectionNamePrefixRel._aux_1` not marked `meta`
-
#13029 removes the unused
change ... withtactic syntax. -
#12897 adjusts the results of
inferInstanceAsand thedefderivinghandler to conform to recently strengthened restrictions on reducibility. This change ensures that when deriving or inferring an instance for a semireducible type definition, the definition's RHS is not leaked when the instance is reduced at lower than semireducible transparency. -
#13005 further enforces that all modules used in compile-time execution must be meta imported in preparation for enabling https://github.com/leanprover/lean4/pull/10291
-
#12840 fixes an issue where the use of private imports led to unknown namespaces in downstream modules.
-
#12953 fixes an issue where the
inductionandcasestactics would swallow diagnostics (such as unsolved goals errors) when theusingclause contains a nested tactic. -
#12979 makes
#printshow the full internal private name (including module prefix) in the declaration signature whenpp.privateNamesis set to true. Previously,pp.privateNamesonly affected names in the body but the signature always stripped the private prefix. -
#12964 fixes an issue where
realizeConstwould generate auxiliary declarations (like_sparseCasesOn) using the original defining module's private name prefix rather than the realizing module's prefix. When two modules independently realized the same imported constant, they produced identically-named auxiliary declarations, causing "environment already contains" errors on diamond import. -
#12881 adds
Invariant.withEarlyReturnNewDo,StringInvariant.withEarlyReturnNewDo, andStringSliceInvariant.withEarlyReturnNewDowhich useProdinstead ofMProdfor the state tuple, matching the new do elaborator's output. The existingwithEarlyReturndefinitions are reverted toMProdfor backwards compatibility with the legacy do elaborator. Tests and invariant suggestions are updated to use theNewDovariants. -
#12880 applies
@[mvcgen_invariant_type]toStd.Do.Invariantand removes the hard-coded fallback inisMVCGenInvariantTypethat was needed for bootstrapping (cf. #12874). It also extractsStringInvariantandStringSliceInvariantas named abbreviations tagged with@[mvcgen_invariant_type], so thatmvcgenclassifies string and string slice loop invariants correctly. -
#12874 adds an
@[mvcgen_invariant_type]tag attribute so that users can mark custom types as invariant types for themvcgentactic. Goals whose type is an application of a tagged type are classified as invariants rather than verification conditions. The hard-coded check forStd.Do.Invariantis kept as a fallback until a stage0 update allows applying the attribute directly. -
#12767 makes sure that identifiers with
MetaorSimprocin their name do not show up in library search results. -
#12866 adds
optTypesupport to thedoPatDeclparser, allowinglet ⟨width, height⟩ : Nat × Nat ← actionin do-notation. Previously, only the less ergonomiclet ⟨width, height⟩ : Nat × Nat := ← actionworkaround was available. The type annotation is propagated to the monadic action as an expected type, matchingdoIdDecl's existing behavior. -
#12698 adds a
result? : Option TraceResultfield toTraceDataand populates it inwithTraceNodeandwithTraceNodeBefore, so that metaprograms walking trace trees can determine success/failure structurally instead of string-matching on emoji. -
#12233 replaces the default
instantiateMVarsimplementation with a two-pass variant that fuses fvar substitution into the traversal, avoiding separatereplace_fvarscalls for delayed-assigned MVars and preserving sharing. The old single-pass implementation is removed entirely. -
#12560 changes the way the linting for
linter.unusedSimpArgsgets the value from the environment. This is achieved by using the appropriate helper functions defined inLean.Linter.Basic. -
#11427 modifies
#eval eto elaborateewith section variables in scope. While evaluating expressions with free variables is not possible, this lets#evalgive a better error message than "unknown identifier." -
#12841 changes the elaboration of the
structure/classcommands so that default values have later fields in context as well. This allows field defaults to depend on fields that come both before and after them. While this was already the case for inherited fields to some degree, it now applies uniformly to all fields. Additionally, when elaborating the default value for a field, all fields that depend on it are cleared from the context to avoid situations where the default value depends on itself. -
#12749 changes "structure-like" terminology to "non-recursive structure" across internal documentation, error messages, the metaprogramming API, and the kernel, to clarify Lean's type theory. A structure is a one-constructor inductive type with no indices — these can be created by either the
structureorinductivecommands — and are supported by the primitiveExpr.projprojections. Only non-recursive structures have an eta conversion rule. The PR description contains the APIs that were renamed. -
#12662 adjusts the module parser to set the leading whitespace of the first token to the whitespace up to that token. If there are no actual tokens in the file, the leading whitespace is set on the final (empty) EOI token. This ensures that we do not lose the initial whitespace (e.g. comments) of a file in
Syntax. -
#12325 adds a warning to any
defof class type that does not also declare an appropriate reducibility. -
#12817 moves the universe-level-count check from
unfold_definition_coreintois_delta, establishing the invariant that ifis_deltasucceeds thenunfold_definitionalso succeeds. This prevents a crash (SIGSEGV or garbled error) that occurred when call sites inlazy_delta_reduction_stepunconditionally dereferenced the result ofunfold_definitioneven on a level-parameter-count mismatch. -
#12802 re-applies https://github.com/leanprover/lean4/pull/12757 (reverted in https://github.com/leanprover/lean4/pull/12801) with the
release-cilabel to test whether it causes the async extension PANIC seen in the v4.29.0-rc5 tag CI. -
#12789 skips the noncomputable pre-check in
processDefDerivingwhen the instance type isProp. Since proofs are erased by the compiler, computability is irrelevant forProp-valued instances. -
#12776 fixes
@[implicit_reducible]on well-founded recursive definitions. -
#12778 fixes an inconsistency in
getStuckMVar?where the instance argument to class projection functions and auxiliary parent projections was not whnf-normalized before checking for stuck metavariables. Every other case ingetStuckMVar?(recursors, quotient recursors,.projnodes) normalizes the major argument viawhnfbefore recursing — class projection functions and aux parent projections were the exception. -
#12756 adds
deriving noncomputable instance Foo for Barsyntax so that delta-derived instances can be marked noncomputable. Previously, when the underlying instance was noncomputable,deriving instancewould fail with an opaque async compilation error. -
#12699 gives the
generatefunction's "apply @Foo to Goal" trace nodes their own trace sub-classMeta.synthInstance.applyinstead of sharing the parentMeta.synthInstanceclass. -
#12701 fixes a gap in how
@[implicit_reducible]is assigned to parent projections during structure elaboration. -
#12719 marks
levelZeroandLevel.ofNatas@[implicit_reducible]so thatLevel.ofNat 0 =?= Level.zerosucceeds when the definitional equality checker respects transparency annotations. Without this, coercions between structures with implicitLevelparameters fail, as reported by @FLDutchmann on Zulip. -
#12695 fixes a bug in
Meta.zetaReducewherehaveexpressions were not being zeta reduced. It also adds a feature where applications of local functions are beta reduced, and another where zeta-delta reduction can be disabled. These are all controllable by flags:-
zetaDelta(default: true) enables unfolding local definitions -
zetaHave(default: true) enables zeta reducinghaveexpressions -
beta(default: true) enables beta reducing applications of local definitions
-
-
#12696 fixes a test case reported by Alexander Bentkamp that runs into a heartbeat limit due to daring use of
withDefaultrflinmvcgen. -
#12680 fixes an issue where
mutual public structurewould have a private constructor. The fix copies the fix from #11940. -
#12602 restricts and in particular simplifies the semantics of
evalConstwith(checkMeta := true)(which is the default): it now fails iff the passed constant name is notmeta(and we are undermodule). -
#12603 adds a feature where
inductiveconstructors can override the binder kinds of the type's parameters, like in #9480 forstructure. For example, it's possible to makexexplicit in the constructorEq.refl, rather than implicit:inductive Eq {α : Type u} (x : α) : α → Prop where | refl (x) : Eq x x -
#12647 adds the missing
popScopescall towithNamespace, which previously only dropped scopes from the elaborator'sCommand.Statebut did not pop the environment'sScopedEnvExtensionstate stacks. This caused scoped syntax declarations to leak keywords outside their namespace whenwithNamespacehad been called. -
#12673 allows for a leightweight version of dependent
matchin the newdoelaborator: discriminant types get abstracted over previous discriminants. The match result type and the local context still are not considered for abstraction. For example, if bothi : Natandh : i < lenare discrminants, then if an alternative matchesiwith0, we also haveh : 0 < len:example {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (as : Array α) (b : β) (f : (a : α) → a ∈ as → β → m (ForInStep β)) : m β := let rec loop (i : Nat) (h : i ≤ as.size) (b : β) : m β := do match i, h with | 0, _ => pure b | i+1, h => have h' : i < as.size := Nat.lt_of_lt_of_le (Nat.lt_succ_self i) h have : as.size - 1 < as.size := Nat.sub_lt (Nat.zero_lt_of_lt h') (by decide) have : as.size - 1 - i < as.size := Nat.lt_of_le_of_lt (Nat.sub_le (as.size - 1) i) this match (← f as[as.size - 1 - i] (Array.getElem_mem this) b) with | ForInStep.done b => pure b | ForInStep.yield b => loop i (Nat.le_of_lt h') b loop as.size (Nat.le_refl _) b -
#12608 continues #9674, cleaning up binder annotations inside the bodies of
let recandwheredefinitions. -
#12666 fixes spurious unused variable warnings for variables used in non-atomic match discriminants in
donotation. For example, inmatch Json.parse s >>= fromJson? with, the variableswould be reported as unused. -
#12661 fixes false-positive "unused variable" warnings for mutable variables reassigned inside
try/catchblocks with the new do elaborator.
Library
-
#13175 fixes the wrong behavior of a stream in http_body.
-
#12144 introduces the
Bodytype class, theChunkStreamandFulltypes that are used to represent streaming bodies of Requests and Responses. -
#13129 implements verification infrastructure for backwards patterns that is analogous to the existing infrastructure for forward patterns. Based on this it adds verification for the
skipSuffix?,endsWithanddropSuffix?functions on strings. -
#12912 adds trivial lemmas about
ExceptCpsT.runKto match the existing lemmas about.run. -
#13109 adds lemmas about the
Stringoperationsdrop,dropEnd,take,takeEnd. -
#13106 verifies
String.Pos.nextnby providing the low-level APInextn_zero/nextn_add_oneas well as aSplitslemma. -
#13105 proves
theorem front?_eq {s : String} : s.front? = s.toList.head?and related results. -
#13098 generalizes some theorems about
Nat.ofDigitCharswhich were needlessly restricted to base 10. -
#13096 show the trivial result that given
c : l.Cursor, we have thatc.pos ≤ l.length. -
#13092 fixes an issue where
Std.Iter.joinStringhad an extra universe parameter because of anIteratorLoopinstance which was actually unnecessary. -
#13091 adds the function
String.Slice.joinand adds lemmas aboutString.joinandString.Slice.join. -
#13090 adds the single lemma
Char.toNat_mk. -
#13061 adds lemmas about
BEqonList String.Slice. -
#13058 adds
EquivBEqandLawfulHashableinstances toString.Slice. -
#13057 adds some variants of existing lemmas about
String.toNat?and friends. -
#13056 adds the functions
Std.Iter.joinStringandStd.Iter.intercalateString. -
#13054 adds the simproc String.reduceToSingleton
, which is disabled by default and turns"c"intoString.singleton 'c'`. -
#13003 reorganizes the instances
ToString IntandRepr Intso that they both point at a common definitionInt.repr(the same setup is used forNat). It then verifies the functionsInt.repr,String.isIntandString.toInt. -
#12999 verifies the
String.dropPrefix?function for our various patterns. -
#12469 adds the
Inhabitedinstance forThunk. -
#12128 introduces the
URIdata type. -
#12990 verifies the
String.startsWithandString.skipPrefix?functions for our various pattern types. -
#12988 introduces the functions
String.Slice.skipPrefix?,String.Slice.Pos.skip?,String.Slice.skipPrefixWhile,String.Slice.Pos.skipWhileand redefinesString.Slice.takeWhileandString.Slice.dropWhileto use these new functions. -
#12984 renames the function
ForwardPattern.dropPrefix?toForwardPattern.skipPrefix? -
#12828 redefines the
String.isNatfunction to use less state and perform short-circuiting. It then verifies theString.isNatandString.toNat?functions. -
#12980 adds theorems about
Char,NatandList. -
#12977 removes most of the
simpannotations added in #12945, to mitigate the performance impact. The lemmas remain. -
#12966 adds simp lemmas that simplify
n.digitChar = '0'ton = 0and a simproc that simplifiesn.digitChar = '!'toFalse. -
#12924 fixes a regression introduced in Lean 4.29.0-rc2 where
simpno longer simplifies inside type class instance arguments due to thebackward.isDefEq.respectTransparencychange. This breaks proofs where a term like(a :: l).lengthappears both in the main expression and inside implicit instance arguments (e.g., determining aBitVecwidth). -
#12950 adds simp lemmas equating kernel-friendly function names with their operator notation equivalents:
Nat.land_eq,Nat.lor_eq,Nat.xor_eq,Nat.shiftLeft_eq',Nat.shiftRight_eq', andBool.rec_eq. These are useful when proofs involve reflection and need to simplify kernel-reduced terms back to operator notation. -
#12955 fixes the windows build with signal handlers.
-
#12945 adds a few
foralllemmas to thesimpset. -
#12900 fixes some process signals that were incorrectly numbered.
-
#12127 introduces the
Headersdata type, that provides a good and convenient abstraction for parsing, querying, and encoding HTTP/1.1 headers. -
#12936 fixes
Id.run_seqLeftandId.run_seqRightto apply when the two monad results are different. -
#12909 fixes the typo in
Int.sq_nonnneg. -
#12919 fixes the
HSub PlainTime Durationinstance, which had its operands reversed: it computedduration - timeinstead oftime - duration. For example, subtracting 2 minutes fromtime("13:02:01")would givetime("10:57:59")rather than the expectedtime("13:00:01"). We also noticed thatHSub PlainDateTime Millisecond.Offsetis similarly affected. -
#12885 shifts some material in
Initto make sure that theToStringinstances of basic types don't rely onString.Internal.append. -
#12857 removes the use of
native_decidein the HTTP library and adds proofs to remove thepanic!. -
#12852 implements an iterator for
PersistentHashMap. -
#12844 provides the iterator combinator
appendthat permits the concatenation of two iterators. -
#12481 provides lemmas about
toArrayandkeysArrayon tree maps and tree sets that are analogous to the existingtoListandkeyslemmas. -
#12385 implements a merge sort algorithm on arrays. It has been measured to be about twice as fast as
List.mergeSortfor large arrays with random elements, but for small or almost sorted ones, the list implementation is faster. Compared toArray.qsort, it is stable and has O(n log n) worst-case cost. Note: There is still a lot of potential for optimization. The current implementation allocates O(n log n) arrays, one per recursive call. -
#12821 removes the
@[grind →]attribute fromList.getElem_of_getElem?andVector.getElem_of_getElem?. These were identified as problematic in Mathlib by https://github.com/leanprover/lean4/issues/12805. -
#12807 makes the lemmas about
String.find?andString.containsthat were added recently into public declarations. -
#12757 marks
Id.runas[implicit_reducible]to ensure thatId.instMonadLiftTOfPureandinstMonadLiftT Idare definitionally equal when using.implicitReducibletransparency setting. -
#12793 takes a more principled approach in deriving
Stringpattern lemmas by reducing to simpler cases similar to how the instances are defined. -
#12126 introduces the core HTTP data types:
Request,Response,Status,Version, andMethod. Currently, URIs are represented asStringand headers asHashMap String (Array String). These are placeholders, future PRs will replace them with strict implementations. -
#12783 adds user-facing API lemmas for
s.contains t, wheresandtare both a string or a slice. -
#12760 adds general projection lemmas for
ExceptCondsconjunction:-
ExceptConds.and_elim_left:(x ∧ₑ y) ⊢ₑ x -
ExceptConds.and_elim_right:(x ∧ₑ y) ⊢ₑ y
-
-
#12779 provides a
ForwardPatternModelfor string patterns and deduces theorems and lawfulness instances from the corresponding results for slice patterns. -
#12777 adds lemmas about
String.find?andString.contains. -
#12771 generalizes
String.Slice.Pos.cast, which turns ans.Posinto at.Pos, to no longer requires = t, but merelys.copy = t.copy. -
#12433 adds a bitblasting circuit for
BitVec.cpopwith a divide-and-conquer for a parallel-prefix-sum. -
#12435 provides injectivity lemmas for
List.getElem,List.getElem?,List.getElem!andList.getDas well as forOption. Note: This introduces a breaking change, changing the signature ofOption.getElem?_inj. -
#12725 shows that lawful searchers split the empty string to
[""]. -
#12723 relates
String.splittoList.splitOnandList.splitOnP, provided that we are splitting by a character or character predicate. -
#12710 deprecated the handful of names in core involving the component
cons₂in favor ofcons_cons. -
#12709 adds various
Stringlemmas that will be useful for deriving high-level theorems aboutString.split. -
#12708 changes the order of implicit parameters
αandpssuch thatαconsistently comes beforepsinPostCond.noThrow,PostCond.mayThrow,PostCond.entails,PostCond.and,PostCond.impand theorems. -
#12707 adds lemmas about
String.intercalateandString.Slice.intercalate. -
#12706 adds a dsimproc which evaluates
String.singleton ' 'to" ". -
#12697 adds two new unfolding theorems to Std.Do:
PostCond.entails.mkandTriple.of_entails_wp. -
#12702 upstreams
List.splitOnandList.splitOnPfrom Batteries/mathlib. -
#12405 adds several useful lemmas for
List,ArrayandVectorwhenever they were missing, improving API coverage and consistency among these types.-
size_singleton/sum_singleton/sum_push -
foldlM_toArray/foldlM_toList/foldl_toArray/foldl_toList/foldrM_toArray/foldrM_toList/foldr_toList -
toArray_toList -
foldl_eq_apply_foldr/foldr_eq_apply_foldl,foldr_eq_foldl: relatesfoldlandfoldrfor associative operations with identity -
sum_eq_foldl: relates sum tofoldlfor associative operations with identity -
Perm.pairwise_iff/Perm.pairwise: pairwise properties are preserved under permutations of arrays
-
-
#12430 provides
WellFounded.partialExtrinsicFix, which makes it possible to implement and verify partially terminating functions, safely building on top of the seemingly less generalextrinsicFix(which is now calledtotalExtrinsicFix). A proof of termination is only necessary in order to formally verify the behavior ofpartialExtrinsicFix. -
#12685 adds some missing material about transferring positions across the subslicing operations
slice,sliceFrom,sliceTo. -
#12678 marks
List.flatten,List.flatMap,List.intercalateas noncomputable to ensure that theircsimpvariants are used everywhere. -
#12668 adds lemmas about string positions and patterns that will be useful for providing high-level API lemmas for
String.splitand friends.
Tactics
-
#13177 adds
@[expose]toLean.Grind.abstractFnandLean.Grind.simpMatchDiscrsOnlyso that the kernel can unfold them when type-checkinggrind-produced proofs insidemoduleblocks. Other similar gadgets (nestedDecidable,PreMatchCond,alreadyNorm) were already exposed; these two were simply missed. -
#13166 replaces the
grindcanonicalizer with a new type-directed normalizer (Sym.canon) that goes inside binders and applies targeted reductions in type positions, eliminating the O(n^2)isDefEq-based approach. -
#13149 simplifies the
grindcanonicalizer by removing dead state and unnecessary complexity, and fixes two bugs discovered during the cleanup. -
#13080 adds
SymExtension, a typed extensible state mechanism forSymM, following the same pattern asGrind.SolverExtension. Extensions are registered at initialization time viaregisterSymExtensionand provide typedgetState/modifyStateaccessors. Extension state persists acrosssimpinvocations within asym =>block and is re-initialized on eachSymM.run. -
#13048 adds two new
sym_simprocDSL primitives and helper grind-mode tactics. -
#13046 prevents
Sym.simpfrom looping on permutation theorems like∀ x y, x + y = y + x. -
#13042 extends the
simptactic insym =>mode to support local hypotheses in the extra theorem list. -
#13041 extends
mkTheoremFromDeclandmkTheoremFromExprto handle theorems whose conclusion is not an equality, enablingSym.simpto use a broader class of lemmas as rewrite rules. -
#13040 adds validation to the
register_sym_simpcommand:-
Reject duplicate variant names
-
Validate
pre/postsyntax by elaborating them viaelabSymSimprocin a minimalGrindTacticMcontext, catching unknown theorem names and unknown theorem set references at registration time
-
-
#13039 adds the
simptactic to thesym =>interactive mode, completing theSym.simpinteractive infrastructure. -
#13034 adds the
register_sym_simpcommand for declaring namedSym.simpvariants withpre/postsimproc chains and optional config overrides. -
#13033 adds
r == eguards to thenorm_eq_varandnorm_eq_var_constbranches ofInt.Linear.simpEq?. Without these guards,simpEq?returns a non-trivial proof for already-normalized equations likex = -1, causingexists_prop_congrto fire repeatedly and build an infinitely growing term. -
#13032 fixes #12842 where
grindexhausts memory on goals involving high-degree polynomials such as(x + y)^2 = x^128 + y^2overFin 2. -
#13031 adds the built-in elaborators for the
sym_simprocandsym_dischargerDSL syntax categories introduced in #13026. -
#13027 fixes a nondeterministic crash in
grindcaused by aBEq/Hashableinvariant violation in the congruence table.congrHashuses each expression's ownfunCCflag to compute its hash (one-level decomposition forfunCC = true, full recursive decomposition forfunCC = false), butisCongruentonly checked the stored expression's flag. When two expressions with mismatchedfunCCflags accidentally hash-collided (via pointer-basedptrAddrUnsafehashing),isCongruentcould declare them congruent despite different argument counts, leading to an assertion failure inmkCongrProof. -
#13026 adds the infrastructure for simproc and discharger DSLs used to specify
pre/postsimproc chains and conditional rewrite dischargers inSym.simpvariants. -
#13024 fixes an issue where
grindcould prove each conjunct individually but failed on the conjunction. The root cause:solverAction's.propagatedpath callsprocessNewFactswhich drains thenewFactsqueue, but the resulting propagation cascade (congruence closure, or-propagation,propagateForallPropDown) can calladdNewRawFact, enqueuing to the separatenewRawFactsqueue. These raw facts were never drained. -
#13018 adds named theorem sets for
Sym.simpwith associated attributes, following the same pattern asMeta.simp'sregister_simp_attr. -
#12996 adds per-result
contextDependenttracking toSym.Simp.Resultand splits the simplifier cache into persistent (context-independent) and transient (context-dependent, cleared on binder entry). This replaces the coarsewellBehavedMethodsflag. -
#12970 adds a
sym =>tactic that enters an interactive symbolic simulation mode built ongrind. Unlikegrind =>, it does not eagerly introduce hypotheses or apply by-contradiction, giving users explicit control overintro,apply, andinternalizesteps. -
#12944 changes the interaction between
@[cbv_opaque]and@[cbv_eval]attributes in thecbvtactic. Previously,@[cbv_opaque]completely blocked all reduction including@[cbv_eval]rewrite rules. Now,@[cbv_eval]rules can fire on@[cbv_opaque]constants, allowing users to provide custom rewrite rules without exposing the full definition. Equation theorems, unfold theorems, and kernel reduction remain suppressed for opaque constants. -
#12923 fixes a bug where
max u vandmax v ufail to match in SymM's pattern matching. BothprocessLevel(Phase 1) andisLevelDefEqS(Phase 2) treatedmaxpositionally, somax u v ≠ max v ustructurally even though they are semantically equal. -
#12920 adds eta reduction to the sym discrimination tree lookup functions (
getMatch,getMatchWithExtra,getMatchLoop). Without this, expressions likeStateM Natthat unfold to eta-expanded forms(fun α => StateT Nat Id α)fail to match discrimination tree entries for the eta-reduced form(StateT Nat Id). -
#12887 optimizes the
String.reduceEq,String.reduceNe, andSym.Simpstring equality simprocs to produce kernel-efficient proofs. Previously, these usedString.decEqwhich forced the kernel to run UTF-8 encoding/decoding and byte array comparison, causing 86+ kernel unfoldings on short strings. -
#12908 makes
@[cbv_opaque]unconditionally block all evaluation of a constant bycbv, including@[cbv_eval]rewrite rules. Previously,@[cbv_eval]could bypass@[cbv_opaque], and for bare constants (not applications),isOpaqueConstcould fall through tohandleConstwhich would unfold the definition body. -
#12888 adds
String-specific simprocs tocbvtactic. -
#12882 adds an
@[mvcgen_witness_type]tag attribute, analogous to@[mvcgen_invariant_type], that allows users to mark types as witness types. Goals whose type is an application of a tagged type are classified as witnesses rather than verification conditions, and appear in a newwitnessessection in themvcgentactic syntax (beforeinvariants). -
#12875 adds
cbvsimprocs for getting elements out of arrays. -
#12597 adds a
cbv_simprocsystem for thecbvtactic, mirroring simp'ssimprocinfrastructure but tailored to cbv's three-phase pipeline (↓pre,cbv_evaleval,↑post). User-defined simplification procedures are indexed by discrimination tree patterns and dispatched during cbv normalization. -
#12851 add support for erasing
@[cbv_eval]annotations usingattribute [-cbv_eval], mirroring the existing@[-simp]mechanism for simp lemmas. -
#12805 adds a
set_option grind.unusedLemmaThresholdthat, when set to N > 0 andgrindsucceeds, reports E-matching lemmas that were activated at least N times but do not appear in the final proof term. This helps identify@[grind]annotations that fire frequently without contributing to proofs. -
#12563 makes the
omit,unusedSectionVarsandloopingSimpArgslinters respect thelinter.alloption: whenlinter.allis set to false (and the respective linter option is unset), the linter should not report errors. -
#12816 solves three distinct issues with the handling of
ite/dite,decide. -
#12788 adds a
set_option cbv.maxSteps Noption that controls the maximum number of simplification steps thecbvtactic performs. Previously the limit was hardcoded to theSym.Simp.Configdefault of 100,000 with no way for users to override it. The option is threaded throughcbvCore,cbvEntry,cbvGoal, andcbvDecideGoal. -
#12782 adds high priority to instances for
OfSemiring.Qin the grind ring envelope. When Mathlib is imported, instance synthesis for types likeOfSemiring.Q Natbecomes very expensive because the solver explores many irrelevant paths before finding the correct instances. By marking these instances as high priority and adding shortcut instances for basic operations (Add,Sub,Mul,Neg,OfNat,NatCast,IntCast,HPow), instance synthesis resolves quickly. -
#12773 adds
atlocation syntax to thecbvtactic, matching the interface ofsimp at. Previouslycbvcould only reduce the goal target; now it supportscbv at h,cbv at h |-, andcbv at *. -
#12766 adds a dedicated cbv simproc for
Decidable.decidethat directly matches onisTrue/isFalseinstances, producing simpler proof terms and avoiding unnecessary unfolding throughDecidable.rec. -
#12677 changes the approach in
simpIteCbvandsimpDIteCbv, by replacing call toDecidable.decidewith reducing and direct pattern matching on theDecidableinstance forisTrue/isFalse. This produces simpler proof terms. -
#12763 adds pre-pass simprocs
simpOrandsimpAndto thecbvtactic that evaluate only the left argument ofOr/Andfirst, short-circuiting when the result is determined without evaluating the right side. Previously,cbvprocessedOr/Andvia congruence, which always evaluated both arguments. For expressions likedecide (m < n ∨ expensive), whenm < nis true, the expensive right side is now skipped entirely. -
#12607 fixes an issue where
withLocationwasn't saving the info context, which meant that tactics that useat *location syntax and do term elaboration would save infotrees but revert the metacontext, leading to Infoview messages like "Error updating: Error fetching goals: Rpc error: InternalError: unknown metavariable" if the tactic failed at some locations but succeeded at others.
Compiler
-
#13152 informs the RC optimizer that tagged values can also be considered as "borrowed" in the sense that we do not need to consider them as owned values for the borrow analysis (they do of course not have an allocation they actually borrow from).
-
#13136 introduces coalescing of RC operations to the RC optimizer. Whenever we perform multiple
incs for a single value within one basic block it is legal to instead perform all of theseincs at once at the firstincside. This is the case because the value will stay alive until at least the lastincand was thus never observable withRC=1. Hence, this change ofinclocation never destroys reuse opportunities. -
#13147 fixes theoretical leaks in the handling of
Array.get!Internalin the code generator. Currently, the code generator assumes that the value returned byget!Internalis derived from theArrayargument. However, this does not generally hold up as we might also return theInhabitedvalue in case of an out of bounds access (recall that we continue execution after panics by default). This means that we sometimes convert anArray.get!InternaltoArray.get!InternalBorrowedwhen we are not allowed to do so because in the panic case theInhabitedinstance can be returned and if it is an owned value it is going to leak. -
#13138 introduces the
weak_specializeattribute. Unlike thenospecializeattribute it does not block specialization for parameters marked with this type completely. Instead,weak_specializeparameters are only specialized for if another parameter provokes specialization. If no such parameter exists, they are treated likenospecialize. -
#13118 fixes an incompatibility of
--load-dynlibwith the module system. -
#13116 ensures that reads from constants count as borrows in the eyes of the borrow inference analysis. This reduces RC pressure in the presence of constant reads.
-
#13094 marks the
Inhabitedarguments of all functions in core marked asexternas borrowed (panicking array accessors andpanic!itself). This in turn causes a transitive effect throughout the codebase and promotes most, if not all,Inhabitedarguments to functions to borrowed. -
#13097 makes the compiler traces contain more information about the kind of
inc/decthat are being conducted (persistent,checkedetc.) -
#13066 changes the behavior of forward and backward projection propagation in the context of user defined borrows. The reason to have them be "forced" override (i.e. override user annotations as well) was that a user annotated borrowed value can potentially flow into a reset-reuse transitively through a projection and must thus have accurate reference count. The reasons that this is no longer necessary are:
-
Forward never had to be forced anyways, it can only affect the
zinlet z := oproj x iwhich can't be annotated by a user -
Backward is no longer necessary as the forward propagator for user annotations prevents the reset-reuse insertion from working with values that have user defined borrow annotations entirely.
-
-
#13064 informs the borrow inference that if an
Arrayis borrowed and we index into it, the value we obtain is effectively a borrowed value as well. This helps improve the ABI of operations that recurse on linked structures containing arrays such as tries or persistent hash maps. -
#12942 marks the context argument of
ReaderTas borrowed, causing a wide spread of useful borrow annotations throughout the entire meta stack which reduces RC pressure. This introduces a crucial new behavior: When modifyingReaderTcontext, e.g. throughwithReaderthis will almost always cause an allocation. Given that theReaderTcontext is frequently used in a non-linear fashion anyways we think this is an acceptable behavior. -
#13052 fixes a bug in the borrow inference in connection with
exportannotations. -
#13017 ensures that when a declaration is marked with
@[export], the compiler throws an error if any of its arguments are marked as borrowed. -
#12971 increases Lean's default stack size, including for the main thread of Lean executables, to 1GB.
-
#12830 enables support for respecting user provided borrow annotations. This allows user to mark arguments of their definitions or local functions with
(x : @&Ty)and have the borrow inference try its best to preserve this annotation, thus potentially reducing RC pressure. Note that in some cases this might not be possible. For example, the compiler prioritizes preserving tail calls over preserving borrow annotations. A precise reasoning of why the compiler chose to make its inference decisions can be obtained withtrace.Compiler.inferBorrow. -
#12952 ensures that when a function is marked
exportits borrow annotations (if present) are always ignored. -
#12930 places
set_option compiler.ignoreBorrowAnnotation true inon to allexport/externpairs. This is necessary becauseexportforces all arguments to be passed as owned whileexternrespects borrow annotations. The current approach to theexport/externtrick was always broken but never surfaced. However, with upcoming changes manyexport/externpairs are going to be affected by borrow annotations and would've broken without this. -
#12886 adds support for ignoring user defined borrow annotations. This can be useful when defining
extern/exportpairs as theexternmight be infected by borrow annotations while inexportthey are already ignored. -
#12781 ports the C emission pass from IR to LCNF, marking the last step of the IR/LCNF conversion and thus enabling end-to-end code generation through the new compilation infrastructure.
-
#12850 optimizes the handling of
match_same_ctor.hetto make it emit nice match trees as opposed to unoptimized CPS style code. -
#12539 replaces three independent name demangling implementations (Lean, C++, Python) with a single source of truth in
Lean.Compiler.NameDemangling. The new module handles the full pipeline: prefix parsing (l_,lp_,_init_,initialize_,lean_apply_N,_lean_main), postprocessing (suffix flags, private name stripping, hygienic suffix stripping, specialization contexts), backtrace line parsing, and C exports via@[export]. -
#12810 adds tracing to the borrow inference to explain to the user why it got to its conclusions.
-
#12796 fixes a deadlock when
uv_tcp_acceptis under contention from multiple threads. -
#12795 fixes a memory leak that gets triggered on the error path of
lean_uv_dns_get_name -
#12790 makes the compiler removes arguments to join points that are void, avoiding a bunch of dead stores in the bytecode and the initial C (though LLVM was surely able to optimize these away further down the line already).
-
#12759 replaces the
isImplicitReduciblecheck withMeta.isInstancein theshouldInlinefunction withininlineCandidate?. -
#12724 implements support for extracting simple ground array literals into statically initialized data.
-
#12727 implements simple ground literal extraction for boxed scalar values.
-
#12715 ensures the compiler extracts
Array/ByteArray/FloatArrayliterals as one big closed term to avoid quadratic overhead at closed term initialization time. -
#12705 ports the simple ground expression extraction pass from IR to LCNF.
-
#12665 ports the expand reset/reuse pass from IR to LCNF. In addition it prevents exponential code generation unlike the old one. This results in a ~15% decrease in binary size and slight speedups across the board.
-
#12687 implements the LCNF instructions required for the expand reset reuse pass.
-
#12663 avoids false-positive error messages on specialization restrictions under the module system when the declaration is explicitly marked as not specializable. It could also provide some minor public size and rebuild savings.
Pretty Printing
-
#10384 makes notations such as
∨,∧,≤, and≥pretty print using ASCII versions whenpp.unicodeis false. -
#12745 fixes
pp.fvars.anonymousto display loose free variables as_fvar._instead of_when the option is set tofalse. This was the intended behavior in https://github.com/leanprover/lean4/pull/12688 but the fix was committed locally and not pushed before that PR was merged. -
#12688 adds a
pp.fvars.anonymousoption (defaulttrue) that controls the display of loose free variables (fvars not in the local context). -
#12654 fixes two aspects of pretty printing of private names.
-
Name unresolution. Now private names are not special cased: the private prefix is stripped off and the
_root_prefix is added, then it tries resolving all suffixes of the result. This is sufficient to handle imported private names in the new module system. (Additionally, unresolution takes macro scopes into account now.) -
Delaboration. Inaccessible private names use a deterministic algorithm to convert private prefixes into macro scopes. The effect is that the same private name appearing in multiple times in the same delaborated expression will now have the same
✝suffix each time. It used to use fresh macro scopes per occurrence.
-
-
#12606 adds the pretty printer option
pp.mdata, which causes the pretty printer to annotate terms with any metadata that is present. For example,set_option pp.mdata true /-- info: [mdata noindex:true] 2 : Nat -/ #guard_msgs in #check no_index 2
Documentation
-
#13115 updates the
inferInstanceAsdocstring to reflect current behavior: it requires an expected type from context and should not be used as a simpleinferInstancesynonym. The old example (#check inferInstanceAs (Inhabited Nat)) no longer works, so it's replaced with one demonstrating the intended transport use case. -
#13065 rewrites the docstring on
Lean.ReducibilityHintsto accurately describe the kernel's lazy delta reduction strategy: which side gets unfolded when comparing two definitions, how definitional height is computed, and how hints relate to the@[reducible]/@[irreducible]elaborator attributes. -
#12959 fixes a series of errors in docstrings.
Server
-
#12948 moves
RequestCancellationTokenfromIO.ReftoIO.CancelToken. -
#12905 adjusts the JSON encoding of RPC references from
{"p": "n"}to{"__rpcref": "n"}. Existing clients will continue to work unchanged, but should eventually move to the new format by advertising therpcWireFormatclient capability.
Lake
-
#13164 changes
lake cache getto fetch artifact cloud storage URLs from Reservoir in a single bulk POST request rather than relying on per-artifact HTTP redirects. When downloading many artifacts, the redirect-based approach sends one request per artifact to the Reservoir web host (Netlify), which can be slow and risks hitting rate limits. The bulk endpoint returns all URLs at once, so curl only talks to the CDN after that. -
#13151 changes
Lake.procto always log process output asinfoif the process exits with a nonzero return code. This way it behaves the same ascaptureProcon errors. -
#13144 adds three new
lake cachesubcommands for staged cache uploads:stage,unstage, andput-staged. These are designed to function as parallels for the commands of the same name in Mathlib'slake exe cache. -
#13141 changes Lake's materialization process to run remove untracked files in tracked directories (via
git clean -xf) when updating dependency repositories. This ensures stale leftovers in the source tree are removed. -
#13110 fixes a race condition in
Cache.saveArtifactthat caused intermittent "permission denied" errors when two library facets (e.g.,staticandstatic.export) produce artifacts with the same content hash and attempt to cache them concurrently. -
#13028 adds a check that rejects Lake configurations where multiple executables share the same root module name. Previously, Lake would silently compile the root module once and link it into all executables, producing identical binaries regardless of differing
srcDirsettings. -
#13014 makes errors in
lake cache get/lake cache putartifact transfers more verbose, which helps with debugging. It also fixes an issue with error reporting when downloading artifacts on demand. -
#12993 fixes a bug with Lake where caching an
ltarproduced vialake build -owould fail ifrestoreAllArtifactswas alsotrue. -
#12974 changes
lake cache getandlake cache putto transfer artifacts in parallel (usingcurl --parallel) when uploading or eagerly downloading artifacts. Transfers are still recorded one-by-one in the output -- no progress meter yet. -
#12957 fixes a build failure on macOS introduced by #12540. macOS BSD
ardoes not support the@fileresponse file syntax that #12540 enabled unconditionally. On macOS, when building core (i.e.,bootsrap := true),recBuildStaticnow useslibtool -static -filelist, which handles long argument lists natively. -
#12954 changes the Lake
CacheMapdata structure to track the platform-dependence of outputs. Platform-independent packages will no longer include platform-dependent mappings in the output files produced bylake build -o. -
#12540 extends Lake's use of response files (
@file) from Windows-only to all platforms, avoidingARG_MAXlimits when invokingclang/arwith many object files. -
#12935 adds the
fixedToolchainLake package configuration option. Setting this totrueinforms Lake that the package is only expected to function on a single toolchain (like Mathlib). This causes Lake's toolchain update procedure to prioritize its toolchain and avoids the need to separate input-to-output mappings for the package by toolchain version in the Lake cache. -
#12914 adds packing and unpacking of module artifacts into
.ltararchives usingleantar. -
#12927 changes
lake cache getto download artifacts by default. Artifacts can be downloaded on demand with the new--mappings-onlyoption (--download-artsis now obsolete). -
#12837 changes the default behavior of the
restoreAllArtifactspackage configuration to mirror that of the workspace. If the workspace also has it unset, the default remains the same (false). -
#12835 changes Lake to only emit
.nobuildtraces (introduced in #12076) if the normal trace file already exists. This fixes an issue where alake build --no-buildwould create the build directory and thereby prevent a cloud release fetch in a future build. -
#12799 changes Lake to use the modification times of traces (where available) for artifact modification times.
-
#12634 enables Lake to download artifacts from a remote cache service on demand as part of a
lake build. It also refactors much of the cache API to be more type safe.
Other
-
#12865 fixes a crash in release_checklist.py when a repository uses the
leanprover/lean4-nightly:toolchain prefix (e.g. leansqlite). Theis_version_gtefunction only checked forleanprover/lean4:nightly-but notleanprover/lean4-nightly:, causing aValueError: invalid literal for int() with base 10: 'nightly'when trying to parse the version. -
#12963 fixes a panic in
lake shakewhen applied to a header-only file without trailing newline -
#12836 adds a
lake-cilabel that enables the full Lake test suite in CI, avoiding the need to temporarily commit and revert changes totests/CMakeLists.txt. Thelake-cilabel impliesrelease-ci(check level 3), so all release platforms are also tested. -
#12822 downloads a prebuilt release of
leantarand bundles it with Lean as part of the core build. -
#12700 fixes a CMake scoping bug that made
-DLEAN_VERSION_*overrides ineffective. -
#12638 switches four lightweight workflows from
pull_requesttopull_request_targetto stop GitHub from requiring manual approval when themathlib-lean-pr-testing[bot]app triggers label events (e.g. addingbuilds-mathlib). Since the bot never lands commits on master, it is perpetually treated as a "first-time contributor" and everypull_requestevent it triggers requires approval.pull_request_targetevents always run without approval because they execute trusted code from the base branch. -
#12682 extends
lake shakewith a flag for minimizing only a specific module -
#12648 adds the experimental
idbg e, a new do-element (and term) syntax for live debugging between the language server and a running compiled Lean program.