Skip to content

Commit 3c69f95

Browse files
authored
Merge pull request #56 from SwayamInSync/fix/lean-pin
verify: Pin Lean 4.35.0, move projects 0.1 pinned, and certify with the pinned toolchain
2 parents d7bb472 + b695c49 commit 3c69f95

13 files changed

Lines changed: 144 additions & 28 deletions

File tree

‎.github/workflows/cppverify-ci.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ concurrency:
2626

2727
env:
2828
CVC5_VERSION: "1.1.2"
29-
LEAN_TOOLCHAIN: "leanprover/lean4:v4.32.2"
29+
LEAN_TOOLCHAIN: "leanprover/lean4:v4.35.0"
3030

3131
jobs:
3232
linux:

‎README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ Binaries: `build/bin/cpp-verify`, `build/bin/clang++`, `build/bin/clangd`, and `
4343
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`:
4444

4545
- 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.
46-
- Lean 4, for `--lean-certify` and `--lean-fallback`: install [elan](https://github.com/leanprover/elan); generated projects pin `leanprover/lean4:v4.32.2`.
46+
- Lean 4, for `--lean-certify` and `--lean-fallback`: install [elan](https://github.com/leanprover/elan); generated projects pin `leanprover/lean4:v4.35.0`.
4747

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

‎clang/lib/Verify/Driver/CppVerifyVersion.h‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,12 @@ namespace clang {
99
namespace verify {
1010

1111
/// The Lean toolchain every generated Lean project pins.
12-
inline constexpr llvm::StringLiteral LeanToolchain = "leanprover/lean4:v4.32.2";
12+
inline constexpr llvm::StringLiteral LeanToolchain = "leanprover/lean4:v4.35.0";
13+
14+
/// The toolchains earlier releases pinned. A project that pins one was
15+
/// generated by such a release, and is moved to the current pin.
16+
inline constexpr llvm::StringLiteral EarlierLeanToolchains[] = {
17+
"leanprover/lean4:v4.32.2"};
1318

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

‎clang/lib/Verify/Driver/Verifier.cpp‎

Lines changed: 56 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1430,13 +1430,26 @@ class Verifier {
14301430
if (!Existing)
14311431
return llvm::createStringError(Existing.getError(),
14321432
"cannot read Lean toolchain pin");
1433-
if (Existing.get()->getBuffer().trim() !=
1434-
llvm::StringRef(Toolchain).trim())
1435-
return llvm::createStringError(
1436-
llvm::inconvertibleErrorCode(),
1437-
"Lean project uses toolchain '%s'; expected '%s'",
1438-
Existing.get()->getBuffer().trim().str().c_str(),
1439-
llvm::StringRef(Toolchain).trim().str().c_str());
1433+
const std::string Pin = Existing.get()->getBuffer().trim().str();
1434+
if (Pin != LeanToolchain) {
1435+
// A pin an earlier release wrote is moved with the release; any
1436+
// other is the user's, and the generated semantics need ours.
1437+
if (!llvm::is_contained(EarlierLeanToolchains, Pin))
1438+
return llvm::createStringError(
1439+
llvm::inconvertibleErrorCode(),
1440+
"Lean project uses toolchain '%s'; expected '%s'", Pin.c_str(),
1441+
LeanToolchain.str().c_str());
1442+
if (llvm::Error Error = writeTextFile(ToolchainPath, Toolchain))
1443+
return std::move(Error);
1444+
Diags.push_back(
1445+
{VerifyDiagnostic::Warning,
1446+
"the Lean project " + Root + " pinned " + Pin +
1447+
", which an earlier release of cpp-verify wrote; it now "
1448+
"pins " +
1449+
LeanToolchain.str() +
1450+
", so a proof that no longer builds needs updating for "
1451+
"that Lean"});
1452+
}
14401453
} else if (llvm::Error Error = writeTextFile(ToolchainPath, Toolchain)) {
14411454
return std::move(Error);
14421455
}
@@ -1500,14 +1513,49 @@ class Verifier {
15001513
const std::string &Root = leanProjectRoot();
15011514
const std::string LogBase =
15021515
leanProjectPath(Root, {".cppverify-certification"});
1516+
// Every step runs the Lean the project pins. elan picks a toolchain by
1517+
// the working directory, not by --dir, so its proxy is given the pin;
1518+
// and whatever runs, lake must report the pinned Lean.
1519+
std::vector<std::string> Launcher = {"lake"};
1520+
auto Elan = llvm::sys::findProgramByName("elan");
1521+
bool ViaElan = false;
1522+
if (!Elan || llvm::sys::fs::equivalent(*Lake, *Elan, ViaElan))
1523+
ViaElan = false;
1524+
if (ViaElan)
1525+
Launcher.push_back(("+" + LeanToolchain).str());
15031526
auto runLake = [&](std::vector<std::string> Arguments,
15041527
llvm::StringRef Stage) -> llvm::Expected<std::string> {
1505-
std::vector<std::string> Command = {"lake", "--dir=" + Root};
1528+
std::vector<std::string> Command = Launcher;
1529+
Command.push_back("--dir=" + Root);
15061530
Command.insert(Command.end(), std::make_move_iterator(Arguments.begin()),
15071531
std::make_move_iterator(Arguments.end()));
15081532
return executeWithLogs(*Lake, Command, LogBase + "-" + Stage.str());
15091533
};
15101534

1535+
// lake takes --version alone.
1536+
std::vector<std::string> Query = Launcher;
1537+
Query.push_back("--version");
1538+
auto Reported = executeWithLogs(*Lake, Query, LogBase + "-version");
1539+
if (!Reported)
1540+
return Reported.takeError();
1541+
const llvm::StringRef Pinned =
1542+
LeanToolchain.substr(LeanToolchain.find(":v") + 2);
1543+
llvm::StringRef Version = *Reported;
1544+
if (size_t At = Version.find("Lean version "); At != llvm::StringRef::npos)
1545+
Version =
1546+
Version.substr(At + std::strlen("Lean version "))
1547+
.take_while([](char C) { return llvm::isDigit(C) || C == '.'; });
1548+
else
1549+
Version = "";
1550+
if (Version != Pinned)
1551+
return llvm::createStringError(
1552+
llvm::inconvertibleErrorCode(),
1553+
"the project pins %s, but %s runs Lean %s; install elan "
1554+
"(https://github.com/leanprover/elan), which runs the pinned "
1555+
"toolchain",
1556+
LeanToolchain.str().c_str(), Lake->c_str(),
1557+
Version.empty() ? "of an unknown version" : Version.str().c_str());
1558+
15111559
if (auto Build = runLake({"build", "CppVerify.Generated", "CppVerify.User"},
15121560
"build");
15131561
!Build)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
void trivial()
2+
cppverify::post(true)
3+
{}

‎clang/test/Verify/suite/lean_project.cpp‎

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,21 @@
66
// RUN: test -f %t.project/CppVerify/User.lean
77
// RUN: test -f %t.project/CppVerify/Check.lean
88
// RUN: test -f %t.project/CppVerify/Proofs/Goal_583ed320b83a75f8.lean
9-
// RUN: grep -Fxq 'leanprover/lean4:v4.32.2' %t.project/lean-toolchain
9+
// RUN: grep -Fxq 'leanprover/lean4:v4.35.0' %t.project/lean-toolchain
1010
// RUN: FileCheck %s --input-file=%t.project/CppVerify/Generated.lean --check-prefix=GENERATED
1111
// RUN: grep -Fq 'import CppVerify.Proofs.Goal_583ed320b83a75f8' %t.project/CppVerify/Check.lean
1212
// RUN: echo '/- USER_KEEP -/' >> %t.project/CppVerify/User.lean
1313
// RUN: echo '/- PROOF_KEEP -/' >> %t.project/CppVerify/Proofs/Goal_583ed320b83a75f8.lean
1414
// RUN: %cpp-verify --backend=lean --lean-project=%t.project %s -- 2>&1 | FileCheck %s --check-prefix=EXPORT
1515
// RUN: grep -Fq 'USER_KEEP' %t.project/CppVerify/User.lean
1616
// RUN: grep -Fq 'PROOF_KEEP' %t.project/CppVerify/Proofs/Goal_583ed320b83a75f8.lean
17+
// A project an earlier release generated moves to the current pin, and keeps
18+
// its proofs; a pin no release wrote is the user's, and an error.
19+
// RUN: echo 'leanprover/lean4:v4.32.2' > %t.project/lean-toolchain
20+
// RUN: %cpp-verify --backend=lean --lean-project=%t.project %s -- 2>&1 | FileCheck %s --check-prefix=MOVED
21+
// RUN: grep -Fxq 'leanprover/lean4:v4.35.0' %t.project/lean-toolchain
22+
// RUN: grep -Fq 'PROOF_KEEP' %t.project/CppVerify/Proofs/Goal_583ed320b83a75f8.lean
23+
// RUN: %cpp-verify --backend=lean --lean-project=%t.project %s -- 2>&1 | FileCheck %s --check-prefix=EXPORT --implicit-check-not=warning
1724
// RUN: echo 'leanprover/lean4:v4.31.0' > %t.project/lean-toolchain
1825
// RUN: not %cpp-verify --backend=lean --lean-project=%t.project %s -- 2>&1 | FileCheck %s --check-prefix=PIN
1926
// RUN: not %cpp-verify --backend=lean --lean-project=%t.empty --lean-certify %S/Inputs/lean_no_obligations.cpp -- 2>&1 | FileCheck %s --check-prefix=EMPTY
@@ -27,5 +34,7 @@ void trivial()
2734
// GENERATED-NOT: unchecked Lean scratch-pad
2835
// GENERATED: function: trivial
2936
// GENERATED: source: {{.*}}lean_project.cpp:{{[0-9]+:[0-9]+}}
30-
// PIN: Lean project uses toolchain 'leanprover/lean4:v4.31.0'; expected 'leanprover/lean4:v4.32.2'
37+
// MOVED: warning: the Lean project {{.*}}.project pinned leanprover/lean4:v4.32.2, which an earlier release of cpp-verify wrote; it now pins leanprover/lean4:v4.35.0, so a proof that no longer builds needs updating for that Lean
38+
// MOVED-NEXT: Exported: lean obligation: trivial
39+
// PIN: Lean project uses toolchain 'leanprover/lean4:v4.31.0'; expected 'leanprover/lean4:v4.35.0'
3140
// EMPTY: Unresolved: Lean certification requires at least one proof obligation
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Certification runs the Lean the project pins. Through elan, which picks a
2+
# toolchain by the working directory, every step names the pin; a lake
3+
# outside elan must report the pinned Lean.
4+
#
5+
# UNSUPPORTED: system-windows
6+
# RUN: rm -rf %t %t.project && mkdir -p %t/elan %t/plain
7+
# RUN: %cpp-verify --backend=lean --lean-project=%t.project %S/Inputs/lean_toolchain.cpp --
8+
#
9+
# A fake elan, with lake as its proxy, records what it is asked.
10+
# RUN: printf '#!/bin/sh\necho "$*" >> %t/elan.log\ncase "$*" in *--version*) echo "Lake version 5.0.0 (Lean version 4.35.0)";; *) exit 1;; esac\n' > %t/elan/elan
11+
# RUN: chmod +x %t/elan/elan && ln -s elan %t/elan/lake
12+
# RUN: not env PATH=%t/elan %cpp-verify --backend=lean --lean-project=%t.project --lean-certify %S/Inputs/lean_toolchain.cpp -- 2>&1 | FileCheck %s --check-prefix=ELAN
13+
# RUN: FileCheck %s --input-file=%t/elan.log --check-prefix=ELAN-LOG
14+
#
15+
# RUN: printf '#!/bin/sh\necho "Lake version 5.0.0 (Lean version 4.1.0)"\n' > %t/plain/lake
16+
# RUN: chmod +x %t/plain/lake
17+
# RUN: not env PATH=%t/plain %cpp-verify --backend=lean --lean-project=%t.project --lean-certify %S/Inputs/lean_toolchain.cpp -- 2>&1 | FileCheck %s --check-prefix=PLAIN -DDIR=%t/plain
18+
19+
# ELAN: Unresolved: lean obligation: trivial (Lean certification failed: command failed with exit code 1
20+
21+
# ELAN-LOG: {{^}}+leanprover/lean4:v4.35.0 --version{{$}}
22+
# ELAN-LOG-NEXT: {{^}}+leanprover/lean4:v4.35.0 --dir={{.*}}.project build CppVerify.Generated CppVerify.User{{$}}
23+
24+
# PLAIN: Unresolved: lean obligation: trivial (Lean certification failed: the project pins leanprover/lean4:v4.35.0, but [[DIR]]/lake runs Lean 4.1.0; install elan (https://github.com/leanprover/elan), which runs the pinned toolchain)

‎clang/test/Verify/version.test‎

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@
88
# RUN: printf '#!/bin/sh\necho "Lake version 5.0.0 (Lean version 4.1.0)"\n' > %t/plain/lake
99
# RUN: chmod +x %t/plain/cvc5 %t/plain/lake
1010
# RUN: env PATH=%t/plain %cpp-verify --version | FileCheck %s --check-prefixes=HEAD,PLAIN -DDIR=%t/plain
11-
# RUN: mkdir -p %t/match && printf '#!/bin/sh\necho "Lake version 5.0.0 (Lean version 4.32.2)"\n' > %t/match/lake
11+
# RUN: mkdir -p %t/match && printf '#!/bin/sh\necho "Lake version 5.0.0 (Lean version 4.35.0)"\n' > %t/match/lake
1212
# RUN: chmod +x %t/match/lake
1313
# RUN: env PATH=%t/match %cpp-verify --version | FileCheck %s --check-prefixes=HEAD,MATCH -DDIR=%t/match
14-
# RUN: printf '#!/bin/sh\necho "leanprover/lean4:v4.32.2 (default)"\n' > %t/elan/elan
14+
# RUN: printf '#!/bin/sh\necho "leanprover/lean4:v4.35.0 (default)"\n' > %t/elan/elan
1515
# RUN: chmod +x %t/elan/elan && ln -s elan %t/elan/lake
1616
# RUN: env PATH=%t/elan %cpp-verify --version | FileCheck %s --check-prefixes=HEAD,ELAN -DDIR=%t/elan
1717
# RUN: printf '#!/bin/sh\necho "leanprover/lean4:v4.30.0 (default)"\n' > %t/elan/elan
@@ -25,18 +25,18 @@
2525
# HEAD-NEXT: {{^}} z3 {{[0-9]+\.[0-9]+\.[0-9]+}} built in ({{vendored|system}})
2626

2727
# NONE-NEXT: {{^}} cvc5 missing not on PATH; --backend=cvc5, portfolio, and race run it (tested with 1.1.2)
28-
# NONE-NEXT: {{^}} lean missing lake is not on PATH; --lean-certify runs it (projects pin leanprover/lean4:v4.32.2)
28+
# NONE-NEXT: {{^}} lean missing lake is not on PATH; --lean-certify runs it (projects pin leanprover/lean4:v4.35.0)
2929

3030
# PLAIN-NEXT: {{^}} cvc5 9.8.7 [[DIR]]/cvc5 (tested with 1.1.2)
31-
# PLAIN-NEXT: {{^}} lean 4.1.0 [[DIR]]/lake, but projects pin leanprover/lean4:v4.32.2
31+
# PLAIN-NEXT: {{^}} lean 4.1.0 [[DIR]]/lake, but projects pin leanprover/lean4:v4.35.0
3232

3333
# MATCH-NEXT: {{^}} cvc5 missing
34-
# MATCH-NEXT: {{^}} lean 4.32.2 [[DIR]]/lake (projects pin leanprover/lean4:v4.32.2)
34+
# MATCH-NEXT: {{^}} lean 4.35.0 [[DIR]]/lake (projects pin leanprover/lean4:v4.35.0)
3535

3636
# ELAN-NEXT: {{^}} cvc5 missing
37-
# ELAN-NEXT: {{^}} lean 4.32.2 [[DIR]]/lake (elan; projects pin leanprover/lean4:v4.32.2, installed)
37+
# ELAN-NEXT: {{^}} lean 4.35.0 [[DIR]]/lake (elan; projects pin leanprover/lean4:v4.35.0, installed)
3838

3939
# ELAN-MISSING-NEXT: {{^}} cvc5 missing
40-
# ELAN-MISSING-NEXT: {{^}} lean 4.32.2 [[DIR]]/lake (elan; projects pin leanprover/lean4:v4.32.2, which elan installs on first use)
40+
# ELAN-MISSING-NEXT: {{^}} lean 4.35.0 [[DIR]]/lake (elan; projects pin leanprover/lean4:v4.35.0, which elan installs on first use)
4141

4242
# HEAD-NEXT: {{^}} bmc uses z3; portfolio and race use z3 and cvc5

‎docs/ARCHITECTURE.md‎

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1662,6 +1662,16 @@ The driver selects a backend via `VerifyOptions` (`Verifier.h` / `cpp-verify --b
16621662
| **BMC** | `LoopUnroll` on VCR, then shared obligation/Z3 path | Source verification grows bounds from zero through `--unroll=N`, stopping on a counterexample, complete unwinding, unresolved query, or the maximum frontier. Safety with failed unwinding is `BoundedSafe(N)`; only proved unwinding is `Verified`. A spec's checks have no loops: the driver solves them with a Z3 backend (`SpecBackend`), as on the default path, and keeps the bound on their archived modules for replay. |
16631663
| **Lean** | `exportLeanScratchPad` / project certification | Standalone mode emits unchecked theorem stubs. Project mode emits direct source goals, total functional heaps, typed bit-vector/integer operations, and compact finite-fuel spec bodies into generated files while preserving user proofs. Export is `Exported`; only the pinned admission-free kernel/axiom check is `Certified`. Collections are `logic.unsupported` (planned for a future release). |
16641664

1665+
A Lean project pins `LeanToolchain` (`Driver/CppVerifyVersion.h`) in its
1666+
`lean-toolchain` file. A pin in `EarlierLeanToolchains`, which an earlier
1667+
release wrote, is rewritten to the current one with a warning; any other pin is
1668+
an error, since the generated semantics are written for one Lean. elan picks a
1669+
toolchain by the working directory, not by `--dir`, so through elan's proxy
1670+
every certification step names the pin (`lake +leanprover/lean4:v4.35.0
1671+
--dir=DIR ...`). Certification first asks `lake --version`, alone, since lake
1672+
takes it alone, and refuses a `lake` that reports another Lean. Before 0.2,
1673+
certification ran elan's default toolchain.
1674+
16651675
Each backend declares supported `LogicFeature`s. Central dispatch rejects a
16661676
module requiring an unavailable feature before backend execution. Spec
16671677
inlining runs before passivization: `SpecInliner::prepareFunctionAxiomatic`
@@ -2034,7 +2044,7 @@ stops before VCR conversion.
20342044
`CPPVERIFY_PARITY_ARCHIVES` it also compares encodings per archived obligation.
20352045
- Runner: `scripts/run-verify-tests.sh`.
20362046
- Backend fidelity gate: `clang/test/Verify/suite/backend_fidelity_gate.test`
2037-
requires cvc5 and pinned Lean 4.32.2 and runs the permanent differential
2047+
requires cvc5 and pinned Lean 4.35.0 and runs the permanent differential
20382048
matrix under every machine-integer encoding.
20392049
- Instrumented coverage: `scripts/coverage-verify.sh` (region target on `clang/lib/Verify`).
20402050

‎docs/DESIGN.md‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1877,14 +1877,16 @@ type, a Boolean is a proposition, and machine division by zero is SMT-LIB's, as
18771877
the certifier (ARCHITECTURE.md, "Lean Encoding"). A unit test checks that
18781878
Lean's evaluation agrees with the certifier on random ground terms and on
18791879
every operator at its extreme operands. Standalone output is an unchecked scratch-pad.
1880-
Editable project mode pins Lean 4.32.2, overwrites only generated semantics and
1880+
Editable project mode pins Lean 4.35.0 (`LeanToolchain`), overwrites only generated semantics and
18811881
the active check module, and preserves reusable user lemmas and one proof file
18821882
per stable obligation goal. Initial generation is `Exported`. Certification
18831883
compiles the user module and every active proof with `sorry` as an error, then
18841884
checks aggregate theorem types and axiom dependencies. Only foundational Lean
18851885
axioms are permitted; user `axiom`/`opaque` proof shortcuts, stale proof types,
18861886
missing proofs, malformed output, and toolchain failures remain `Unresolved`.
1887-
Only complete success is `Certified`.
1887+
Only complete success is `Certified`. Certification runs the pinned toolchain
1888+
whatever elan's default is, and a project pinned by an earlier release moves to
1889+
the current pin with a warning; any other pin is an error.
18881890

18891891
`--check-ub` extent instrumentation is applied before all Z3, cvc5, portfolio,
18901892
BMC, and Lean obligation construction. Direct Lean export and canonical archive

0 commit comments

Comments
 (0)