Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
ec110b0
verify: Declare polling, stopping, and the environment of a child pro…
SwayamInSync Oct 22, 2026
177a966
verify: Poll, stop, and give an environment to a child process.
SwayamInSync Oct 22, 2026
f2e596b
verify: Build the child process helpers.
SwayamInSync Oct 22, 2026
b346bb9
verify: Poll and stop cvc5 through the shared child process helpers.
SwayamInSync Oct 22, 2026
fa7d244
verify: Add cppverify_auto, the ladder of steps that proves Lean goals.
SwayamInSync Oct 22, 2026
9029189
verify: Check every Lean goal in one environment, and each alone only…
SwayamInSync Oct 22, 2026
704a14c
verify: Add the options of Lean automation and of its processes.
SwayamInSync Oct 22, 2026
714db33
verify: Try cppverify_auto on each new Lean goal, and build Lean proo…
SwayamInSync Oct 22, 2026
153f8b9
verify: Add --no-lean-auto and --lean-auto-timeout, and let --jobs se…
SwayamInSync Oct 22, 2026
d78e3e8
test: Add functions whose goals cppverify_auto proves, leaves, and st…
SwayamInSync Oct 22, 2026
3060482
test: Add an unfinished grind lemma for the Lean automation test.
SwayamInSync Oct 22, 2026
82d05f9
test: Check what cppverify_auto writes, what it tries again, and what…
SwayamInSync Oct 22, 2026
84b8285
test: Match the stub's line by pattern now that stubs record automation.
SwayamInSync Oct 22, 2026
e5cd296
test: Certify proofs written over stubs without automation.
SwayamInSync Oct 22, 2026
4f94143
test: Keep the dependency test's stubs unproved without automation.
SwayamInSync Oct 22, 2026
d053218
test: Expect the complete fallback export certified, its return check…
SwayamInSync Oct 22, 2026
e1897db
test: Check the readable stubs without automation.
SwayamInSync Oct 22, 2026
2917d54
test: Expect certification to build the automation module.
SwayamInSync Oct 22, 2026
12fc555
test: Expect only the proof cache to be refused by the Lean backend.
SwayamInSync Oct 22, 2026
8754416
docs: Let a documented example run in a Lean project of its own.
SwayamInSync Oct 22, 2026
20efa19
docs: Document Lean automation, its options, and the Lean processes o…
SwayamInSync Oct 22, 2026
b35649f
docs: Show the proofs cppverify_auto finds and update the Lean walkth…
SwayamInSync Oct 22, 2026
0955a84
docs: Note the proofs cppverify_auto finds and faster certification.
SwayamInSync Oct 22, 2026
28aa36a
docs: Describe Lean automation and the checker's two paths.
SwayamInSync Oct 22, 2026
f43ec0c
docs: State the rules of Lean automation.
SwayamInSync Oct 22, 2026
20cb2fc
test: Check that the trial of cppverify_auto runs the pinned Lean too…
SwayamInSync Oct 23, 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
101 changes: 8 additions & 93 deletions clang/lib/Verify/Backend/CVC5Backend.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//===--- CVC5Backend.cpp --------------------------------------------------===//
#include "CVC5Backend.h"
#include "Certify.h"
#include "ChildProcess.h"
#include "ObligationSerialization.h"
#include "ObligationSimplify.h"
#include "TaskGroup.h"
Expand All @@ -25,14 +26,6 @@
#include <set>
#include <thread>

#if defined(_WIN32)
#include "llvm/Support/Windows/WindowsSupport.h"
#else
#include <cerrno>
#include <csignal>
#include <sys/wait.h>
#endif

using namespace clang;
using namespace verify;

Expand Down Expand Up @@ -1542,84 +1535,6 @@ static std::string diagnosticText(llvm::StringRef Text) {
return Text.str();
}

enum class ProcessPollState { Running, Exited, Failed };

struct ProcessPollResult {
ProcessPollState State = ProcessPollState::Running;
int ExitCode = -1;
std::string Error;
};

static ProcessPollResult pollProcess(const llvm::sys::ProcessInfo &Process) {
#if defined(_WIN32)
std::string Error;
llvm::sys::ProcessInfo Waited =
llvm::sys::Wait(Process, 0, &Error, nullptr, true);
if (Waited.Pid == llvm::sys::ProcessInfo::InvalidPid)
return {ProcessPollState::Running, -1, {}};
if (Waited.Pid != Process.Pid)
return {ProcessPollState::Failed, -1,
Error.empty() ? "cannot poll cvc5 process" : std::move(Error)};
return {ProcessPollState::Exited, Waited.ReturnCode, std::move(Error)};
#else
int Status = 0;
pid_t Waited;
do {
Waited = ::waitpid(Process.Pid, &Status, WNOHANG);
} while (Waited == -1 && errno == EINTR);
if (Waited == 0)
return {};
if (Waited == -1)
return {ProcessPollState::Failed, -1,
"cannot poll cvc5 process: " +
std::error_code(errno, std::generic_category()).message()};
if (WIFEXITED(Status))
return {ProcessPollState::Exited, WEXITSTATUS(Status), {}};
if (WIFSIGNALED(Status))
return {ProcessPollState::Exited, -2,
"cvc5 terminated by signal " + std::to_string(WTERMSIG(Status))};
return {ProcessPollState::Failed, -1,
"cvc5 changed to an unsupported process state"};
#endif
}

static bool terminateAndReap(const llvm::sys::ProcessInfo &Process,
std::string &Error) {
#if defined(_WIN32)
if (!::TerminateProcess(static_cast<HANDLE>(Process.Process), 1) &&
::GetLastError() != ERROR_ACCESS_DENIED) {
Error = "cannot terminate cvc5 process";
return false;
}
std::string WaitError;
llvm::sys::ProcessInfo Waited =
llvm::sys::Wait(Process, std::nullopt, &WaitError);
if (Waited.Pid != Process.Pid) {
Error =
WaitError.empty() ? "cannot reap cvc5 process" : std::move(WaitError);
return false;
}
return true;
#else
if (::kill(Process.Pid, SIGKILL) == -1 && errno != ESRCH) {
Error = "cannot terminate cvc5 process: " +
std::error_code(errno, std::generic_category()).message();
return false;
}
int Status = 0;
pid_t Waited;
do {
Waited = ::waitpid(Process.Pid, &Status, 0);
} while (Waited == -1 && errno == EINTR);
if (Waited == -1 && errno != ECHILD) {
Error = "cannot reap cvc5 process: " +
std::error_code(errno, std::generic_category()).message();
return false;
}
return true;
#endif
}

static bool startsWithSat(llvm::StringRef OutputPath) {
auto Buffer = llvm::MemoryBuffer::getFileSlice(OutputPath, 4, 0, false);
return Buffer && (*Buffer)->getBuffer() == "sat\n";
Expand Down Expand Up @@ -2548,16 +2463,16 @@ static SolverRun runCVC5(const std::string &SolverPath, llvm::StringRef Script,
bool Stopped = false;
bool OutputLimitExceeded = false;
while (true) {
ProcessPollResult Poll = pollProcess(Process);
if (Poll.State == ProcessPollState::Exited) {
ChildPoll Poll = pollChild(Process, "cvc5");
if (Poll.State == ChildState::Exited) {
ExitCode = Poll.ExitCode;
InvocationError = std::move(Poll.Error);
break;
}
if (Poll.State == ProcessPollState::Failed) {
if (Poll.State == ChildState::Failed) {
InvocationError = std::move(Poll.Error);
std::string TerminationError;
if (!terminateAndReap(Process, TerminationError) &&
if (!terminateChild(Process, "cvc5", TerminationError) &&
!TerminationError.empty())
InvocationError += ": " + TerminationError;
break;
Expand All @@ -2568,22 +2483,22 @@ static SolverRun runCVC5(const std::string &SolverPath, llvm::StringRef Script,
OutputLimitExceeded)) {
InvocationError = std::move(OutputError);
std::string TerminationError;
if (!terminateAndReap(Process, TerminationError) &&
if (!terminateChild(Process, "cvc5", TerminationError) &&
!TerminationError.empty())
InvocationError += ": " + TerminationError;
break;
}
if ((Cancelled && *Cancelled) || interruptRequested()) {
Stopped = true;
std::string TerminationError;
if (!terminateAndReap(Process, TerminationError))
if (!terminateChild(Process, "cvc5", TerminationError))
InvocationError = std::move(TerminationError);
break;
}
if (TimeoutMs != 0 && std::chrono::steady_clock::now() >= Deadline) {
TimedOut = true;
std::string TerminationError;
if (!terminateAndReap(Process, TerminationError))
if (!terminateChild(Process, "cvc5", TerminationError))
InvocationError = std::move(TerminationError);
break;
}
Expand Down
120 changes: 120 additions & 0 deletions clang/lib/Verify/Backend/ChildProcess.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
#include "ChildProcess.h"
#include "llvm/Support/ConvertUTF.h"
#include <system_error>

#if defined(_WIN32)
#include "llvm/Support/Windows/WindowsSupport.h"
#else
#include <cerrno>
#include <csignal>
#include <sys/wait.h>
#if defined(__APPLE__)
#include <crt_externs.h>
#define environ (*_NSGetEnviron())
#else
extern char **environ;
#endif
#endif

namespace clang {
namespace verify {

ChildPoll pollChild(const llvm::sys::ProcessInfo &Process,
llvm::StringRef Name) {
#if defined(_WIN32)
std::string Error;
llvm::sys::ProcessInfo Waited =
llvm::sys::Wait(Process, 0, &Error, nullptr, true);
if (Waited.Pid == llvm::sys::ProcessInfo::InvalidPid)
return {ChildState::Running, -1, {}};
if (Waited.Pid != Process.Pid)
return {ChildState::Failed, -1,
Error.empty() ? "cannot poll " + Name.str() + " process"
: std::move(Error)};
return {ChildState::Exited, Waited.ReturnCode, std::move(Error)};
#else
int Status = 0;
pid_t Waited;
do {
Waited = ::waitpid(Process.Pid, &Status, WNOHANG);
} while (Waited == -1 && errno == EINTR);
if (Waited == 0)
return {};
if (Waited == -1)
return {ChildState::Failed, -1,
"cannot poll " + Name.str() + " process: " +
std::error_code(errno, std::generic_category()).message()};
if (WIFEXITED(Status))
return {ChildState::Exited, WEXITSTATUS(Status), {}};
if (WIFSIGNALED(Status))
return {ChildState::Exited, -2,
Name.str() + " terminated by signal " +
std::to_string(WTERMSIG(Status))};
return {ChildState::Failed, -1,
Name.str() + " changed to an unsupported process state"};
#endif
}

bool terminateChild(const llvm::sys::ProcessInfo &Process, llvm::StringRef Name,
std::string &Error) {
#if defined(_WIN32)
if (!::TerminateProcess(static_cast<HANDLE>(Process.Process), 1) &&
::GetLastError() != ERROR_ACCESS_DENIED) {
Error = "cannot terminate " + Name.str() + " process";
return false;
}
std::string WaitError;
llvm::sys::ProcessInfo Waited =
llvm::sys::Wait(Process, std::nullopt, &WaitError);
if (Waited.Pid != Process.Pid) {
Error = WaitError.empty() ? "cannot reap " + Name.str() + " process"
: std::move(WaitError);
return false;
}
return true;
#else
if (::kill(Process.Pid, SIGKILL) == -1 && errno != ESRCH) {
Error = "cannot terminate " + Name.str() + " process: " +
std::error_code(errno, std::generic_category()).message();
return false;
}
int Status = 0;
pid_t Waited;
do {
Waited = ::waitpid(Process.Pid, &Status, 0);
} while (Waited == -1 && errno == EINTR);
if (Waited == -1 && errno != ECHILD) {
Error = "cannot reap " + Name.str() + " process: " +
std::error_code(errno, std::generic_category()).message();
return false;
}
return true;
#endif
}

std::vector<std::string> childEnvironment(llvm::StringRef Variable,
llvm::StringRef Value) {
std::vector<std::string> Environment;
auto add = [&](llvm::StringRef Entry) {
if (!Entry.starts_with((Variable + "=").str()))
Environment.push_back(Entry.str());
};
#if defined(_WIN32)
if (wchar_t *Block = ::GetEnvironmentStringsW()) {
for (const wchar_t *Entry = Block; *Entry; Entry += wcslen(Entry) + 1) {
std::string Narrow;
if (llvm::convertWideToUTF8(Entry, Narrow))
add(Narrow);
}
::FreeEnvironmentStringsW(Block);
}
#else
for (char **Entry = environ; Entry && *Entry; ++Entry)
add(*Entry);
#endif
Environment.push_back((Variable + "=" + Value).str());
return Environment;
}

} // namespace verify
} // namespace clang
38 changes: 38 additions & 0 deletions clang/lib/Verify/Backend/ChildProcess.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//===--- ChildProcess.h - Polling and stopping a process ----------------===//
#ifndef LLVM_CLANG_VERIFY_BACKEND_CHILDPROCESS_H
#define LLVM_CLANG_VERIFY_BACKEND_CHILDPROCESS_H

#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Program.h"
#include <string>
#include <vector>

namespace clang {
namespace verify {

enum class ChildState { Running, Exited, Failed };

struct ChildPoll {
ChildState State = ChildState::Running;
int ExitCode = -1;
std::string Error;
};

/// Whether the process has ended, without waiting for it. Name is what the
/// messages call it.
ChildPoll pollChild(const llvm::sys::ProcessInfo &Process,
llvm::StringRef Name);

/// Kills the process and reaps it.
bool terminateChild(const llvm::sys::ProcessInfo &Process, llvm::StringRef Name,
std::string &Error);

/// This process's environment with Variable set to Value, for a child: a
/// child of several started at once cannot be given it by changing ours.
std::vector<std::string> childEnvironment(llvm::StringRef Variable,
llvm::StringRef Value);

} // namespace verify
} // namespace clang

#endif
1 change: 1 addition & 0 deletions clang/lib/Verify/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ add_clang_library(clangVerify
Backend/ObligationSimplify.cpp
Backend/ObligationSerialization.cpp
Backend/ProofCache.cpp
Backend/ChildProcess.cpp
Backend/CVC5Backend.cpp
Backend/Z3Encode.cpp
Backend/SpecAxioms.cpp
Expand Down
Loading
Loading