Repository navigation
verify: Replay a Lean project into one kernel environment, importing the toolchain once - #63
Merged
Merged
Conversation
…the toolchain once.
…eft out, also when it comes first.
…ugh a rejected module.
… module that redeclares a name.
… in the architecture.
SwayamInSync
force-pushed
the
fix/lean-certifier-memory
branch
from
October 24, 2026 00:07
63720b4 to
c55de5b
Compare
This was referenced Oct 24, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The Lean certifier (
LeanCertifier.h, from #60) uses memory that grows with every goal it checks on its own: onmain, one certification inlean_certifier.testtakes 13.4 GB.The checker first checks every goal together: all proof modules imported at once, every module of the project replayed into the environment of the toolchain modules they import. When that fails, because one module is rejected (a proof of
Trueadded without the kernel) or a proof file declares a goal's name, it checks each goal by itself: it imports that goal's proof module again, and for every module it replays it imports that module's imports again, never freeing what it imported. A proof that importsLean(every automatic proof does, and a user's tactic lemmas may) brings in Lean's whole environment each time. Onmain, theimportedcase oflean_certifier.test(one proof through a module the kernel rejects, four goals) makes onelean --run Certify.leanprocess take 13.4 GB. The newlean_certifier_memory.testruns that case underInputs/peak_memory.py, which prints whether the largest process stayed below 6144 MB; withmain's binary:The Linux runners have 16 GB, and lit runs four tests at a time, in name order, so
lean_certifier.testruns beside other Lean tests. Since #61 each of those runscppverify_auto's trial with the pinned Lean, and a Lean test takes about 4.5 GB onmain(measured peak PSS, 4 cores).This is not what stops the Linux release jobs (
The runner has received a shutdown signal, exit 143, 18 to 28 minutes intocheck-clang:mainafter #61, #62 twice), as this description first said: this branch's own release job was stopped the same way (run 39993647759). The ULEB128 case study takes 14.9 GB by itself onmain, and in a run of the Verify suite in lit's order on four workers the whole run peaked at 16.4 GB with it besidelean_semantics.cpp's certifier (1.7 GB on this branch). #64 fixes that (the case study then verifies within 0.9 GB); this change removes the certifier's own share.Design
One import, one kernel environment, as
leancheckerreplays a library:.oleanoutside the toolchain): each module's imports and its own constants. It imports only the toolchain modules among those imports, once.Kernel.Environment.replay, Lean's own replay) one at a time, each after the modules it imports, into that environment. A module is left out, with why, when the kernel rejects it, when it imports a module that was left out (so the reason is the first module's, as before:the kernel rejects CppVerify.Lemmas: ...), or when it declares a constant that an earlier module or the toolchain declares otherwise. A constant declared alike twice (an equation lemma Lean derives in each module that unfolds a definition, or a theorem with the same statement, which the kernel's replay itself takes twice) is the same constant....cppverify_one_99bcee_postcondition_646d2761 declares cppverify_one_99bcee_postcondition_646d2761, which CppVerify.Generated declares. Before, the joint import failed on it and the goal was checked by itself, where the proof's type named its own definition (... is not the goal CppVerify.Generated defines); the verdict is the same, and the reason now says what the file did.G_proofis a theorem of type exactly.const G [],Gis a definition thatCppVerify.Generateddeclared (the module is recorded with each constant at its replay), and the axioms its kernel constants reach are allowed. These are the same checks as before; only where they read changed. A bad module now fails only the goals whose proofs import it, with no second path.safe declaration must not contain partial declaration), and checked so in Lean 4.35.0 with a partial constant of a false goal's type added without the kernel.Measured
Peak memory of each Lean test, run alone on 4 cores (
taskset), 8 tests at a time: the PSS of every process of the test summed every 0.2 s (PSS counts each shared page of Lean's.oleanfiles once).mainlean_certifier.testlean_semantics.cpplean_readable.cpplean_automation.testlean_fallback_partial.testlean_dependencies.testEvery Lean test whose proofs import
Lean(an automatic proof does) dropped by about 2 GB, since even the joint check imported Lean's environment twice (the proofs, then the replay's toolchain environment). The largest is nowlean_readable.cpp'slake buildof a project (44 processes), which the checker does not run. The largest of them,lean_readable.cpp, peaks at 5.6 GB, wherelean_certifier.testalone took 12.4. The largest process of theimportedcase, byru_maxrss: 13360 MB onmain, 3576 MB here. The time oflean_certifier.testis 88 s onmainand 39 s here.Tests
suite/lean_certifier_memory.test(new, Linux): theimportedcase underInputs/peak_memory.py, which prints whether the largest process the command ran stayed below 6144 MB (ru_maxrssof the descendants). It fails onmain(peak memory 12240 MB, not below 6144 MB) and passes here.suite/lean_certifier.test: every case keeps its verdict and reason, except the shadowed proof's reason, above; it fails onmainat that line.suite/lean_certifier.testalso checks the case where the proof that redeclares its goal is the first goal on the list and imports nothing of the project (Inputs/lean_certifier_first.cpp, functionearly): the next function,fine, is certified. Without replaying the generated module first,CppVerify.Generatedis the module left out andfinefails (CppVerify.Generated declares cppverify_early_..., which CppVerify.Proofs... declares).Mutants, each built and run on
lean_certifier.test,lean_certifier_memory.test, andlean_automation.test: 6 of 6 killed on the final code. "The generated module replayed last" survived at first, since a proof that imports it puts it first anyway; the first-goal case above is the one where it matters.lean_certifier(the first-goal case)lean_certifierlean_certifier,lean_certifier_memorylean_certifier,lean_certifier_memorylean_certifier_memoryaloneA first version also kept, for each module whose compiled files could not be read, why, so that only the goals importing it would fail; its mutant survived. Only proofs that built reach the checker, and lake builds what they import, so no input reaches it; it is gone, and an unreadable
.oleanstops the checker, failing every goal with the exception, as onmain.Docs
Lean; names declared twice; the shadowed proof's message.Checked
Inputs/),docs_coverage.test, anddocs_examples.testpass;check-doc-examples.pyon ch17 and the release notes: 7 examples, 0 failed.safe declaration must not contain partial declaration 'cheat'.