Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
79 commits
Select commit Hold shift + click to select a range
c2a2a57
verify: Pin Lean 4.35.0, and name the pins earlier releases wrote.
SwayamInSync Oct 22, 2026
c62d004
verify: Move a project an earlier release pinned to the current Lean,…
SwayamInSync Oct 22, 2026
73fb7cf
verify: Install Lean 4.35.0 in CI.
SwayamInSync Oct 22, 2026
3219cf3
test: Expect the Lean 4.35.0 pin in the version report.
SwayamInSync Oct 22, 2026
514b740
test: Check that a project an earlier release pinned moves to the cur…
SwayamInSync Oct 22, 2026
00dc8f2
test: Add a function to certify with a given lake.
SwayamInSync Oct 22, 2026
87fa76a
test: Check that certification runs the pinned Lean.
SwayamInSync Oct 22, 2026
8ef88f4
docs: Note the Lean 4.35.0 pin in the README.
SwayamInSync Oct 22, 2026
80b0bd0
docs: Describe the Lean pin and its migration in the architecture.
SwayamInSync Oct 22, 2026
feedc46
docs: Note the Lean pin and its migration in the design.
SwayamInSync Oct 22, 2026
9312d84
docs: Note the Lean 4.35.0 pin in chapter 17.
SwayamInSync Oct 22, 2026
fb8c11e
docs: Describe the Lean pin, how certification runs it, and how proje…
SwayamInSync Oct 22, 2026
b695c49
docs: Note the Lean 4.35.0 pin in the release notes.
SwayamInSync Oct 22, 2026
978e151
verify: Declare the hash of an obligation's statement, which leaves o…
SwayamInSync Oct 22, 2026
aa0db6e
verify: Hash an obligation's statement without its function's identity.
SwayamInSync Oct 22, 2026
6679b53
verify: Describe each goal of a Lean project with what identifies it …
SwayamInSync Oct 22, 2026
7f57a54
verify: Name Lean project goals and spec bodies by function and state…
SwayamInSync Oct 22, 2026
b28e265
verify: Pass the Lean backend the goal records of a project.
SwayamInSync Oct 22, 2026
b6461b6
verify: Create the Lean backend with the goal records of a project.
SwayamInSync Oct 22, 2026
51080c3
verify: Add the option that deletes the proofs a Lean project keeps.
SwayamInSync Oct 22, 2026
c858715
verify: Keep each Lean proof with the goal of its statement across re…
SwayamInSync Oct 22, 2026
ac5fd41
verify: Add --lean-prune-stale.
SwayamInSync Oct 22, 2026
7a1f239
test: Prove the statement-named goal in lean_dependencies_impossible.…
SwayamInSync Oct 22, 2026
fa9357e
test: Prove the statement-named goal in lean_dependencies_looping.lean.
SwayamInSync Oct 22, 2026
994b492
test: Prove the statement-named goal in lean_dependencies_mixed.lean.
SwayamInSync Oct 22, 2026
e006a33
test: Prove the statement-named goal in lean_dependencies_positive.lean.
SwayamInSync Oct 22, 2026
87eb170
test: Prove the statement-named goal in lean_dependencies_same.lean.
SwayamInSync Oct 22, 2026
94247f0
test: Prove the statement-named goal in lean_dependencies_seven.lean.
SwayamInSync Oct 22, 2026
a118c22
test: Prove the statement-named goal in lean_dependencies_termination…
SwayamInSync Oct 22, 2026
17da669
test: Prove the statement-named goal in lean_dependencies_trusting.lean.
SwayamInSync Oct 22, 2026
fdde12a
test: Prove the statement-named goal in lean_fallback_goal_1.lean.
SwayamInSync Oct 22, 2026
bda7564
test: Prove the statement-named goal in lean_fallback_goal_2.lean.
SwayamInSync Oct 22, 2026
255b9cb
test: Prove the statement-named goal in lean_fallback_goal_3.lean.
SwayamInSync Oct 22, 2026
e9e41ee
test: Prove the statement-named goal in lean_fallback_goal_4.lean.
SwayamInSync Oct 22, 2026
2fd244c
test: Prove the statement-named goal in lean_fallback_partial_goal_1.…
SwayamInSync Oct 22, 2026
e3cb581
test: Prove the statement-named goal in lean_fallback_partial_goal_2.…
SwayamInSync Oct 22, 2026
6828c19
test: Prove the statement-named goal in lean_semantics_call.lean.
SwayamInSync Oct 22, 2026
a94beff
test: Prove the statement-named goal in lean_semantics_mix.lean.
SwayamInSync Oct 22, 2026
5abd96b
test: Prove the statement-named goal in lean_semantics_numerals.lean.
SwayamInSync Oct 22, 2026
6984be4
test: Check Lean project goals and proof files against the export by …
SwayamInSync Oct 22, 2026
9e561a3
test: Expect statement-named goals and per-function proof files in le…
SwayamInSync Oct 22, 2026
9c97a5b
test: Expect statement-named goals and per-function proof files in le…
SwayamInSync Oct 22, 2026
ad38b04
test: Expect statement-named goals and per-function proof files in le…
SwayamInSync Oct 22, 2026
c8413af
test: Expect statement-named goals and per-function proof files in le…
SwayamInSync Oct 22, 2026
736379f
test: Expect statement-named goals and per-function proof files in le…
SwayamInSync Oct 22, 2026
8fbb54f
test: Expect statement-named goals and per-function proof files in le…
SwayamInSync Oct 22, 2026
e4506d3
test: Expect statement-named goals and per-function proof files in le…
SwayamInSync Oct 22, 2026
43e6347
test: Add the base source of the Lean proof identity test.
SwayamInSync Oct 22, 2026
5ee7f7b
test: Add the inserted source of the Lean proof identity test.
SwayamInSync Oct 22, 2026
8129f71
test: Add the renamed source of the Lean proof identity test.
SwayamInSync Oct 22, 2026
3988adb
test: Add the changed source of the Lean proof identity test.
SwayamInSync Oct 22, 2026
d3252a1
test: Add the removed source of the Lean proof identity test.
SwayamInSync Oct 22, 2026
4fe9541
test: Add the proof of cppverify_bump_ce87d1_missing_return_ba8abb90 …
SwayamInSync Oct 22, 2026
afe389a
test: Add the proof of cppverify_one_99bcee_missing_return_54ae7c80 f…
SwayamInSync Oct 22, 2026
f5590e4
test: Add the proof of cppverify_one_99bcee_postcondition_cd6bdfcd fo…
SwayamInSync Oct 22, 2026
8129b4b
test: Add the proof of cppverify_one_99bcee_postcondition_cd6bdfcd_2 …
SwayamInSync Oct 22, 2026
10b4da9
test: Add the proof of cppverify_one_99bcee_postcondition_ebbe6a6a fo…
SwayamInSync Oct 22, 2026
e445a82
test: Add the proof of cppverify_six_6a2cf3_missing_return_54ae7c80 f…
SwayamInSync Oct 22, 2026
9808fe9
test: Add the proof of cppverify_six_6a2cf3_postcondition_198bc076 fo…
SwayamInSync Oct 22, 2026
7073977
test: Add the proof of cppverify_tri_decreases_f55d4c_termination_3c6…
SwayamInSync Oct 22, 2026
5214899
test: Add CppVerify/Proofs/Goal_00000000deadbeef.lean of the 0.1 proj…
SwayamInSync Oct 22, 2026
75b0652
test: Add CppVerify/Proofs/Goal_00000000feedface.lean of the 0.1 proj…
SwayamInSync Oct 22, 2026
bd5634b
test: Add CppVerify/Proofs/Goal_1272763fe029af5c.lean of the 0.1 proj…
SwayamInSync Oct 22, 2026
9110117
test: Add CppVerify/Proofs/Goal_14843fe605ca0dfc.lean of the 0.1 proj…
SwayamInSync Oct 22, 2026
acbcfa1
test: Add CppVerify/Proofs/Goal_959f92a07e95a169.lean of the 0.1 proj…
SwayamInSync Oct 22, 2026
76883a7
test: Add CppVerify/Proofs/Goal_9b7aec03175825c9.lean of the 0.1 proj…
SwayamInSync Oct 22, 2026
18a236c
test: Add CppVerify/Proofs/Goal_b054b023e3576f6e.lean of the 0.1 proj…
SwayamInSync Oct 22, 2026
c3ca848
test: Add CppVerify/Proofs/Goal_b95e8b7f774dddd4.lean of the 0.1 proj…
SwayamInSync Oct 22, 2026
efe7356
test: Add CppVerify/Proofs/Goal_f799910065d5f258.lean of the 0.1 proj…
SwayamInSync Oct 22, 2026
782bef8
test: Add CppVerify/User.lean of the 0.1 project the Lean proof ident…
SwayamInSync Oct 22, 2026
5bd3df4
test: Add lakefile.toml of the 0.1 project the Lean proof identity te…
SwayamInSync Oct 22, 2026
07db008
test: Add lean-toolchain of the 0.1 project the Lean proof identity t…
SwayamInSync Oct 22, 2026
e3bde60
test: Check that Lean proofs stay with their goals as the source chan…
SwayamInSync Oct 22, 2026
ac1e7f0
docs: Describe how Lean project goals are named and keep their proofs…
SwayamInSync Oct 22, 2026
77e6cc5
docs: State how Lean project goals are named and regenerated in the d…
SwayamInSync Oct 22, 2026
1ae8bae
docs: Give the Lean proof file path by function in chapter 16.
SwayamInSync Oct 22, 2026
1787839
docs: Show Lean proofs following their goals in chapter 17.
SwayamInSync Oct 22, 2026
c84dce2
docs: Document Lean goal names, kept proofs, and --lean-prune-stale i…
SwayamInSync Oct 22, 2026
78b7845
docs: Note that Lean proofs stay with their goals.
SwayamInSync Oct 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/cppverify-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ concurrency:

env:
CVC5_VERSION: "1.1.2"
LEAN_TOOLCHAIN: "leanprover/lean4:v4.32.2"
LEAN_TOOLCHAIN: "leanprover/lean4:v4.35.0"

jobs:
linux:
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ Binaries: `build/bin/cpp-verify`, `build/bin/clang++`, `build/bin/clangd`, and `
Z3 4.13.4 is built in: from the `third_party/z3` submodule, or, without it, cloned during the first build. See `third_party/README.md`. Two backends are optional and found on `PATH`:

- cvc5, for `--backend=cvc5`, `portfolio`, and `race` (tested with 1.1.2): `apt install cvc5` on Ubuntu 24.04, or a [cvc5 release](https://github.com/cvc5/cvc5/releases/tag/cvc5-1.1.2) for Linux, macOS, or Windows; `--cvc5-path` names another executable.
- Lean 4, for `--lean-certify` and `--lean-fallback`: install [elan](https://github.com/leanprover/elan); generated projects pin `leanprover/lean4:v4.32.2`.
- Lean 4, for `--lean-certify` and `--lean-fallback`: install [elan](https://github.com/leanprover/elan); generated projects pin `leanprover/lean4:v4.35.0`.

`cpp-verify --version` shows the cpp-verify release, the LLVM release it is built on, and the Z3, cvc5, and Lean versions it uses.

Expand Down
88 changes: 80 additions & 8 deletions clang/lib/Verify/Backend/LeanBackend.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
//===--- LeanBackend.cpp --------------------------------------------------===//
#include "LeanBackend.h"
#include "ObligationSerialization.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/MD5.h"
#include "llvm/Support/raw_ostream.h"
#include <cctype>
#include <map>
Expand Down Expand Up @@ -59,6 +62,32 @@ static std::string encodeLeanName(llvm::StringRef Name) {
return Encoded;
}

// The characters a Lean identifier takes; the others become `_`.
static std::string leanIdentifierPart(llvm::StringRef Text) {
std::string Part;
for (char C : Text)
Part += std::isalnum(static_cast<unsigned char>(C)) ? C : '_';
return Part;
}

static std::string leanShortHash(llvm::StringRef Text, unsigned Digits) {
llvm::MD5 Hasher;
llvm::MD5::MD5Result Hash;
Hasher.update(Text);
Hasher.final(Hash);
llvm::SmallString<32> Hex;
llvm::MD5::stringifyResult(Hash, Hex);
return llvm::StringRef(Hex).take_front(Digits).str();
}

std::string verify::leanProjectGroup(const ObligationModule &Module) {
return "cppverify_" +
leanIdentifierPart(Module.FunctionName.empty()
? llvm::StringRef("goal")
: llvm::StringRef(Module.FunctionName)) +
"_" + leanShortHash(Module.FunctionIdentity, 6);
}

static void printLeanName(llvm::StringRef Name, llvm::raw_ostream &OS) {
OS << encodeLeanName(Name);
}
Expand Down Expand Up @@ -899,7 +928,7 @@ VerifyResult verify::exportLeanScratchPad(
const ObligationModule &Module, llvm::raw_ostream &OS, bool EmitPreamble,
std::set<std::string> &EmittedFunctions,
std::set<std::string> &EmittedTheorems, unsigned ModuleIndex,
std::vector<std::string> *ProjectGoals,
std::vector<LeanProjectGoal> *ProjectGoals,
const std::set<std::string> *Selection) {
VerifyResult Result;
if (!Module.CorrectnessGoal || !Module.CounterexampleQuery) {
Expand All @@ -911,7 +940,23 @@ VerifyResult verify::exportLeanScratchPad(

LeanEmissionFailed = false;
LeanLogicFunctions = &Module.LogicFunctions;
LeanBodyPrefix = "cppSpecBody_m" + std::to_string(ModuleIndex) + "_";
// A project names everything by identity, never by position, so that a
// proof keeps the names it uses when the source around it changes.
const std::string LegacyBodyPrefix =
"cppSpecBody_m" + std::to_string(ModuleIndex) + "_";
LeanBodyPrefix = LegacyBodyPrefix;
std::string Group;
if (ProjectGoals) {
Group = leanProjectGroup(Module);
LeanBodyPrefix =
"cppSpecBody_" + leanShortHash(Module.FunctionIdentity, 8) + "_";
// Modules of one identity (an archive's) are told apart in order.
for (unsigned Copy = 2; !EmittedTheorems.insert(LeanBodyPrefix).second;
++Copy)
LeanBodyPrefix = "cppSpecBody_" +
leanShortHash(Module.FunctionIdentity, 8) + "_" +
std::to_string(Copy) + "_";
}

if (EmitPreamble) {
if (ProjectGoals) {
Expand Down Expand Up @@ -988,8 +1033,38 @@ VerifyResult verify::exportLeanScratchPad(
unsigned Index = 0;
for (const Obligation &Item : Module.Obligations) {
++Index;
if (Selection && !Selection->count(Item.Id))
const bool Selected = !Selection || Selection->count(Item.Id);
if (!Selected && !ProjectGoals)
continue;
std::string ObligationName =
TheoremName + "_obligation_" + std::to_string(Index);
LeanProjectGoal Goal;
if (ProjectGoals) {
Goal.Hash = obligationStatementHash(Module, Item);
Goal.Name = Group + "_" +
leanIdentifierPart(obligationKindName(Item.Kind)) + "_" +
Goal.Hash.substr(0, 8);
// Identical obligations of one function, in source order, whichever
// of them are exported.
const std::string Base = Goal.Name;
for (unsigned Copy = 2; !EmittedTheorems.insert(Goal.Name).second; ++Copy)
Goal.Name = Base + "_" + std::to_string(Copy);
Goal.Group = Group;
Goal.StableId = Item.StableId.empty() ? Item.Id : Item.StableId;
if (Item.Source.isValid()) {
llvm::raw_string_ostream SourceOS(Goal.Source);
printLeanCommentText(Item.Source.File, SourceOS);
SourceOS << ":" << Item.Source.Line << ":" << Item.Source.Column;
}
Goal.BodyPrefix = LeanBodyPrefix;
Goal.LegacyName = encodeLeanName(ObligationName + "_goal");
Goal.LegacyBodyPrefix = LegacyBodyPrefix;
Goal.Exported = Selected;
if (!Selected) {
ProjectGoals->push_back(std::move(Goal));
continue;
}
}
OS << "/- obligation: ";
printLeanCommentText(Item.StableId.empty() ? Item.Id : Item.StableId, OS);
OS << "\n";
Expand All @@ -1004,12 +1079,9 @@ VerifyResult verify::exportLeanScratchPad(
OS << "unknown";
}
OS << " -/\n";
std::string ObligationName =
TheoremName + "_obligation_" + std::to_string(Index);
if (ProjectGoals) {
ObligationName += "_goal";
emitLeanGoalDefinition(ObligationName, Item.Goal.get(), OS);
ProjectGoals->push_back(encodeLeanName(ObligationName));
emitLeanGoalDefinition(Goal.Name, Item.Goal.get(), OS);
ProjectGoals->push_back(std::move(Goal));
} else {
emitLeanTheorem(ObligationName, Item.Goal.get(), OS);
}
Expand Down
37 changes: 32 additions & 5 deletions clang/lib/Verify/Backend/LeanBackend.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,41 @@
namespace clang {
namespace verify {

/// Selection names the internal IDs to export; an exported goal keeps the
/// positional name it has when every obligation is exported.
/// A goal of an editable Lean project, with what identifies it across
/// regenerations: the semantic hash of its obligation. Its proof file lives in
/// the directory of its function's group.
struct LeanProjectGoal {
std::string Name;
std::string Group;
std::string StableId;
std::string Hash;
/// Where its obligation is, as a Lean comment may hold it.
std::string Source;
/// The prefix of the spec bodies its goal may unfold.
std::string BodyPrefix;
/// The names 0.1 gave the goal and its spec bodies, which a project 0.1
/// generated still uses.
std::string LegacyName;
std::string LegacyBodyPrefix;
/// False for an obligation of an exported function that a selection left
/// to the solver: its goal is known, but has no definition or proof file.
bool Exported = true;
};

/// The group of a module's goals in a project: its function's directory.
std::string leanProjectGroup(const ObligationModule &Module);

/// Selection names the internal IDs to export; a project goal is named by
/// its function and semantic hash, so it keeps its name whatever else is
/// exported, and a scratch-pad goal keeps the positional name it has when
/// every obligation is exported. A project records every goal of the module,
/// the ones left out as not exported.
VerifyResult
exportLeanScratchPad(const ObligationModule &Module, llvm::raw_ostream &OS,
bool EmitPreamble, std::set<std::string> &EmittedFunctions,
std::set<std::string> &EmittedTheorems,
unsigned ModuleIndex,
std::vector<std::string> *ProjectGoals,
std::vector<LeanProjectGoal> *ProjectGoals,
const std::set<std::string> *Selection = nullptr);

class LeanVerifyBackend : public VerifyBackend {
Expand All @@ -27,12 +54,12 @@ class LeanVerifyBackend : public VerifyBackend {
std::set<std::string> EmittedFunctions;
std::set<std::string> EmittedTheorems;
unsigned ModuleIndex = 0;
std::vector<std::string> *ProjectGoals;
std::vector<LeanProjectGoal> *ProjectGoals;
std::optional<std::set<std::string>> Selection;

public:
LeanVerifyBackend(llvm::raw_ostream *OS,
std::vector<std::string> *ProjectGoals)
std::vector<LeanProjectGoal> *ProjectGoals)
: Out(OS), ProjectGoals(ProjectGoals) {}
llvm::StringRef getName() const override { return "lean"; }
BackendCapabilities getCapabilities() const override {
Expand Down
15 changes: 13 additions & 2 deletions clang/lib/Verify/Backend/ObligationSerialization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -606,10 +606,12 @@ class ArchiveWriter {
const ObligationModule &Module, const Obligation &Item,
bool IncludeBMCTransform = true,
llvm::StringRef Domain = "cppverify-obligation-semantic",
uint32_t Version = ObligationSemanticHashVersion) {
uint32_t Version = ObligationSemanticHashVersion,
bool IncludeIdentity = true) {
writeString(Domain);
writeU32(Version);
writeString(Module.FunctionIdentity);
if (IncludeIdentity)
writeString(Module.FunctionIdentity);
writeString(Module.ResultVarName);
writeString(Module.HeapPrefix);
if (IncludeBMCTransform && Module.BMCTransform) {
Expand Down Expand Up @@ -1017,6 +1019,15 @@ std::string verify::obligationSemanticHash(const ObligationModule &Module,
return sha256(Writer.take());
}

std::string verify::obligationStatementHash(const ObligationModule &Module,
const Obligation &Item) {
ArchiveWriter Writer(false);
Writer.writeObligationSemantics(Module, Item, /*IncludeBMCTransform=*/true,
"cppverify-obligation-statement", 1,
/*IncludeIdentity=*/false);
return sha256(Writer.take());
}

std::string verify::obligationQuerySemanticHash(const ObligationModule &Module,
const Obligation &Item) {
ArchiveWriter Writer(false);
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/Verify/Backend/ObligationSerialization.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ std::string obligationSemanticHash(const ObligationModule &Module);
std::string obligationSemanticHash(const ObligationModule &Module,
const Obligation &Item);

/// SHA-256 for what a proof of an individual goal proves: its semantics
/// without the identity of the function it comes from, so that a Lean proof
/// follows its goal when the function is renamed.
std::string obligationStatementHash(const ObligationModule &Module,
const Obligation &Item);

/// SHA-256 for exact dependency-scoped query semantics. Unlike the persistent
/// proof-cache identity, transformed query contents rather than the enclosing
/// BMC frontier distinguish bounds, permitting in-process prefix-proof reuse.
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Verify/Backend/VerifyBackend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -749,7 +749,7 @@ std::unique_ptr<VerifyBackend>
verify::createVerifyBackend(BackendKind K, llvm::raw_ostream *LeanOut,
unsigned BMCUnroll,
const BackendExecutionOptions &Execution,
std::vector<std::string> *LeanProjectGoals) {
std::vector<LeanProjectGoal> *LeanProjectGoals) {
switch (K) {
case BackendKind::Z3:
return std::make_unique<Z3VerifyBackend>(Execution, "z3");
Expand Down
4 changes: 3 additions & 1 deletion clang/lib/Verify/Backend/VerifyBackend.h
Original file line number Diff line number Diff line change
Expand Up @@ -303,11 +303,13 @@ inline unsigned moduleTimeoutMs(const ObligationModule &Module,
: SolverTimeoutMs;
}

struct LeanProjectGoal;

std::unique_ptr<VerifyBackend>
createVerifyBackend(BackendKind K, llvm::raw_ostream *LeanOut = nullptr,
unsigned BMCUnroll = 10,
const BackendExecutionOptions &Execution = {},
std::vector<std::string> *LeanProjectGoals = nullptr);
std::vector<LeanProjectGoal> *LeanProjectGoals = nullptr);

VerifyResult
lowerObligationModule(const ObligationModule &Module,
Expand Down
7 changes: 6 additions & 1 deletion clang/lib/Verify/Driver/CppVerifyVersion.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@ namespace clang {
namespace verify {

/// The Lean toolchain every generated Lean project pins.
inline constexpr llvm::StringLiteral LeanToolchain = "leanprover/lean4:v4.32.2";
inline constexpr llvm::StringLiteral LeanToolchain = "leanprover/lean4:v4.35.0";

/// The toolchains earlier releases pinned. A project that pins one was
/// generated by such a release, and is moved to the current pin.
inline constexpr llvm::StringLiteral EarlierLeanToolchains[] = {
"leanprover/lean4:v4.32.2"};

/// The cvc5 release the test suite runs with.
inline constexpr llvm::StringLiteral CVC5TestedVersion = "1.1.2";
Expand Down
Loading
Loading