Skip to content

Commit 2a132ab

Browse files
author
Leo Alt
committed
Add option to choose solver
1 parent c570b63 commit 2a132ab

14 files changed

+157
-71
lines changed

libsmtutil/SMTPortfolio.cpp

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ SMTPortfolio::SMTPortfolio(
4040
):
4141
SolverInterface(_queryTimeout)
4242
{
43-
m_solvers.emplace_back(make_unique<SMTLib2Interface>(move(_smtlib2Responses), move(_smtCallback), m_queryTimeout));
43+
if (_enabledSolvers.smtlib2)
44+
m_solvers.emplace_back(make_unique<SMTLib2Interface>(move(_smtlib2Responses), move(_smtCallback), m_queryTimeout));
4445
#ifdef HAVE_Z3
4546
if (_enabledSolvers.z3 && Z3Interface::available())
4647
m_solvers.emplace_back(make_unique<Z3Interface>(m_queryTimeout));
@@ -143,10 +144,11 @@ pair<CheckResult, vector<string>> SMTPortfolio::check(vector<Expression> const&
143144
vector<string> SMTPortfolio::unhandledQueries()
144145
{
145146
// This code assumes that the constructor guarantees that
146-
// SmtLib2Interface is in position 0.
147-
smtAssert(!m_solvers.empty(), "");
148-
smtAssert(dynamic_cast<SMTLib2Interface*>(m_solvers.front().get()), "");
149-
return m_solvers.front()->unhandledQueries();
147+
// SmtLib2Interface is in position 0, if enabled.
148+
if (!m_solvers.empty())
149+
if (auto smtlib2 = dynamic_cast<SMTLib2Interface*>(m_solvers.front().get()))
150+
return smtlib2->unhandledQueries();
151+
return {};
150152
}
151153

152154
bool SMTPortfolio::solverAnswered(CheckResult result)

libsmtutil/SolverInterface.h

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323

2424
#include <libsolutil/Common.h>
2525

26+
#include <range/v3/view.hpp>
27+
2628
#include <cstdio>
2729
#include <map>
2830
#include <memory>
@@ -36,16 +38,42 @@ namespace solidity::smtutil
3638
struct SMTSolverChoice
3739
{
3840
bool cvc4 = false;
41+
bool smtlib2 = false;
3942
bool z3 = false;
4043

41-
static constexpr SMTSolverChoice All() { return {true, true}; }
42-
static constexpr SMTSolverChoice CVC4() { return {true, false}; }
43-
static constexpr SMTSolverChoice Z3() { return {false, true}; }
44-
static constexpr SMTSolverChoice None() { return {false, false}; }
44+
static constexpr SMTSolverChoice All() { return {true, true, true}; }
45+
static constexpr SMTSolverChoice CVC4() { return {true, false, false}; }
46+
static constexpr SMTSolverChoice SMTLIB2() { return {false, true, false}; }
47+
static constexpr SMTSolverChoice Z3() { return {false, false, true}; }
48+
static constexpr SMTSolverChoice None() { return {false, false, false}; }
49+
50+
static std::optional<SMTSolverChoice> fromString(std::string const& _solvers)
51+
{
52+
SMTSolverChoice solvers;
53+
for (auto&& s: _solvers | ranges::views::split(',') | ranges::to<std::vector<std::string>>())
54+
if (!solvers.setFromString(s))
55+
return {};
56+
57+
return solvers;
58+
}
59+
60+
bool setFromString(std::string const& _solver)
61+
{
62+
static std::set<std::string> solvers{"cvc4", "smtlib2", "z3"};
63+
if (!solvers.count(_solver))
64+
return false;
65+
if (_solver == "cvc4")
66+
cvc4 = true;
67+
else if (_solver == "smtlib2")
68+
smtlib2 = true;
69+
else if (_solver == "z3")
70+
z3 = true;
71+
return true;
72+
}
4573

4674
bool none() { return !some(); }
47-
bool some() { return cvc4 || z3; }
48-
bool all() { return cvc4 && z3; }
75+
bool some() { return cvc4 || smtlib2 || z3; }
76+
bool all() { return cvc4 && smtlib2 && z3; }
4977
};
5078

5179
enum class CheckResult

libsolidity/formal/BMC.cpp

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,15 +38,14 @@ BMC::BMC(
3838
ErrorReporter& _errorReporter,
3939
map<h256, string> const& _smtlib2Responses,
4040
ReadCallback::Callback const& _smtCallback,
41-
smtutil::SMTSolverChoice _enabledSolvers,
4241
ModelCheckerSettings const& _settings
4342
):
4443
SMTEncoder(_context, _settings),
45-
m_interface(make_unique<smtutil::SMTPortfolio>(_smtlib2Responses, _smtCallback, _enabledSolvers, _settings.timeout)),
44+
m_interface(make_unique<smtutil::SMTPortfolio>(_smtlib2Responses, _smtCallback, _settings.solvers, _settings.timeout)),
4645
m_outerErrorReporter(_errorReporter)
4746
{
4847
#if defined (HAVE_Z3) || defined (HAVE_CVC4)
49-
if (_enabledSolvers.some())
48+
if (m_settings.solvers.cvc4 || m_settings.solvers.z3)
5049
if (!_smtlib2Responses.empty())
5150
m_errorReporter.warning(
5251
5622_error,
@@ -60,6 +59,20 @@ BMC::BMC(
6059

6160
void BMC::analyze(SourceUnit const& _source, map<ASTNode const*, set<VerificationTargetType>> _solvedTargets)
6261
{
62+
if (m_interface->solvers() == 0)
63+
{
64+
if (!m_noSolverWarning)
65+
{
66+
m_noSolverWarning = true;
67+
m_outerErrorReporter.warning(
68+
0000_error,
69+
SourceLocation(),
70+
"BMC analysis was not possible since no SMT solver was found and enabled."
71+
);
72+
}
73+
return;
74+
}
75+
6376
if (SMTEncoder::analyze(_source))
6477
{
6578
m_solvedTargets = move(_solvedTargets);
@@ -72,19 +85,22 @@ void BMC::analyze(SourceUnit const& _source, map<ASTNode const*, set<Verificatio
7285
_source.accept(*this);
7386
}
7487

75-
solAssert(m_interface->solvers() > 0, "");
7688
// If this check is true, Z3 and CVC4 are not available
7789
// and the query answers were not provided, since SMTPortfolio
78-
// guarantees that SmtLib2Interface is the first solver.
79-
if (!m_interface->unhandledQueries().empty() && m_interface->solvers() == 1)
90+
// guarantees that SmtLib2Interface is the first solver, if enabled.
91+
if (
92+
!m_interface->unhandledQueries().empty() &&
93+
m_interface->solvers() == 1 &&
94+
m_settings.solvers.smtlib2
95+
)
8096
{
8197
if (!m_noSolverWarning)
8298
{
8399
m_noSolverWarning = true;
84100
m_outerErrorReporter.warning(
85101
8084_error,
86102
SourceLocation(),
87-
"BMC analysis was not possible since no SMT solver (Z3 or CVC4) was found."
103+
"BMC analysis was not possible since no SMT solver (Z3 or CVC4) was found and enabled."
88104
#ifdef HAVE_Z3_DLOPEN
89105
" Install libz3.so." + to_string(Z3_MAJOR_VERSION) + "." + to_string(Z3_MINOR_VERSION) + " to enable Z3."
90106
#endif

libsolidity/formal/BMC.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,6 @@ class BMC: public SMTEncoder
6262
langutil::ErrorReporter& _errorReporter,
6363
std::map<h256, std::string> const& _smtlib2Responses,
6464
ReadCallback::Callback const& _smtCallback,
65-
smtutil::SMTSolverChoice _enabledSolvers,
6665
ModelCheckerSettings const& _settings
6766
);
6867

libsolidity/formal/CHC.cpp

Lines changed: 35 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -56,25 +56,37 @@ CHC::CHC(
5656
ErrorReporter& _errorReporter,
5757
[[maybe_unused]] map<util::h256, string> const& _smtlib2Responses,
5858
[[maybe_unused]] ReadCallback::Callback const& _smtCallback,
59-
SMTSolverChoice _enabledSolvers,
6059
ModelCheckerSettings const& _settings
6160
):
6261
SMTEncoder(_context, _settings),
63-
m_outerErrorReporter(_errorReporter),
64-
m_enabledSolvers(_enabledSolvers)
62+
m_outerErrorReporter(_errorReporter)
6563
{
66-
bool usesZ3 = _enabledSolvers.z3;
64+
bool usesZ3 = m_settings.solvers.z3;
6765
#ifdef HAVE_Z3
6866
usesZ3 = usesZ3 && Z3Interface::available();
6967
#else
7068
usesZ3 = false;
7169
#endif
72-
if (!usesZ3)
70+
if (!usesZ3 && m_settings.solvers.smtlib2)
7371
m_interface = make_unique<CHCSmtLib2Interface>(_smtlib2Responses, _smtCallback, m_settings.timeout);
7472
}
7573

7674
void CHC::analyze(SourceUnit const& _source)
7775
{
76+
if (!m_settings.solvers.z3 && !m_settings.solvers.smtlib2)
77+
{
78+
if (!m_noSolverWarning)
79+
{
80+
m_noSolverWarning = true;
81+
m_outerErrorReporter.warning(
82+
0000_error,
83+
SourceLocation(),
84+
"CHC analysis was not possible since no Horn solver was enabled."
85+
);
86+
}
87+
return;
88+
}
89+
7890
if (SMTEncoder::analyze(_source))
7991
{
8092
resetSourceAnalysis();
@@ -91,6 +103,8 @@ void CHC::analyze(SourceUnit const& _source)
91103
}
92104

93105
bool ranSolver = true;
106+
// If ranSolver is true here it's because an SMT solver callback was
107+
// actually given and the queries were solved.
94108
if (auto const* smtLibInterface = dynamic_cast<CHCSmtLib2Interface const*>(m_interface.get()))
95109
ranSolver = smtLibInterface->unhandledQueries().empty();
96110
if (!ranSolver && !m_noSolverWarning)
@@ -102,7 +116,7 @@ void CHC::analyze(SourceUnit const& _source)
102116
#ifdef HAVE_Z3_DLOPEN
103117
"CHC analysis was not possible since libz3.so." + to_string(Z3_MAJOR_VERSION) + "." + to_string(Z3_MINOR_VERSION) + " was not found."
104118
#else
105-
"CHC analysis was not possible since no integrated z3 SMT solver was found."
119+
"CHC analysis was not possible since no Horn solver was found and enabled."
106120
#endif
107121
);
108122
}
@@ -933,7 +947,7 @@ void CHC::resetSourceAnalysis()
933947

934948
bool usesZ3 = false;
935949
#ifdef HAVE_Z3
936-
usesZ3 = m_enabledSolvers.z3 && Z3Interface::available();
950+
usesZ3 = m_settings.solvers.z3 && Z3Interface::available();
937951
if (usesZ3)
938952
{
939953
/// z3::fixedpoint does not have a reset mechanism, so we need to create another.
@@ -1427,20 +1441,23 @@ pair<CheckResult, CHCSolverInterface::CexGraph> CHC::query(smtutil::Expression c
14271441
case CheckResult::SATISFIABLE:
14281442
{
14291443
#ifdef HAVE_Z3
1430-
// Even though the problem is SAT, Spacer's pre processing makes counterexamples incomplete.
1431-
// We now disable those optimizations and check whether we can still solve the problem.
1432-
auto* spacer = dynamic_cast<Z3CHCInterface*>(m_interface.get());
1433-
solAssert(spacer, "");
1434-
spacer->setSpacerOptions(false);
1444+
if (m_settings.solvers.z3)
1445+
{
1446+
// Even though the problem is SAT, Spacer's pre processing makes counterexamples incomplete.
1447+
// We now disable those optimizations and check whether we can still solve the problem.
1448+
auto* spacer = dynamic_cast<Z3CHCInterface*>(m_interface.get());
1449+
solAssert(spacer, "");
1450+
spacer->setSpacerOptions(false);
14351451

1436-
CheckResult resultNoOpt;
1437-
CHCSolverInterface::CexGraph cexNoOpt;
1438-
tie(resultNoOpt, cexNoOpt) = m_interface->query(_query);
1452+
CheckResult resultNoOpt;
1453+
CHCSolverInterface::CexGraph cexNoOpt;
1454+
tie(resultNoOpt, cexNoOpt) = m_interface->query(_query);
14391455

1440-
if (resultNoOpt == CheckResult::SATISFIABLE)
1441-
cex = move(cexNoOpt);
1456+
if (resultNoOpt == CheckResult::SATISFIABLE)
1457+
cex = move(cexNoOpt);
14421458

1443-
spacer->setSpacerOptions(true);
1459+
spacer->setSpacerOptions(true);
1460+
}
14441461
#endif
14451462
break;
14461463
}

libsolidity/formal/CHC.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ class CHC: public SMTEncoder
5656
langutil::ErrorReporter& _errorReporter,
5757
std::map<util::h256, std::string> const& _smtlib2Responses,
5858
ReadCallback::Callback const& _smtCallback,
59-
smtutil::SMTSolverChoice _enabledSolvers,
6059
ModelCheckerSettings const& _settings
6160
);
6261

@@ -392,9 +391,6 @@ class CHC: public SMTEncoder
392391

393392
/// ErrorReporter that comes from CompilerStack.
394393
langutil::ErrorReporter& m_outerErrorReporter;
395-
396-
/// SMT solvers that are chosen at runtime.
397-
smtutil::SMTSolverChoice m_enabledSolvers;
398394
};
399395

400396
}

libsolidity/formal/ModelChecker.cpp

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,13 @@ ModelChecker::ModelChecker(
3434
ErrorReporter& _errorReporter,
3535
map<h256, string> const& _smtlib2Responses,
3636
ModelCheckerSettings _settings,
37-
ReadCallback::Callback const& _smtCallback,
38-
smtutil::SMTSolverChoice _enabledSolvers
37+
ReadCallback::Callback const& _smtCallback
3938
):
4039
m_errorReporter(_errorReporter),
41-
m_settings(_settings),
40+
m_settings(move(_settings)),
4241
m_context(),
43-
m_bmc(m_context, _errorReporter, _smtlib2Responses, _smtCallback, _enabledSolvers, m_settings),
44-
m_chc(m_context, _errorReporter, _smtlib2Responses, _smtCallback, _enabledSolvers, m_settings)
42+
m_bmc(m_context, _errorReporter, _smtlib2Responses, _smtCallback, m_settings),
43+
m_chc(m_context, _errorReporter, _smtlib2Responses, _smtCallback, m_settings)
4544
{
4645
}
4746

@@ -134,7 +133,7 @@ vector<string> ModelChecker::unhandledQueries()
134133

135134
solidity::smtutil::SMTSolverChoice ModelChecker::availableSolvers()
136135
{
137-
smtutil::SMTSolverChoice available = smtutil::SMTSolverChoice::None();
136+
smtutil::SMTSolverChoice available = smtutil::SMTSolverChoice::SMTLIB2();
138137
#ifdef HAVE_Z3
139138
available.z3 = solidity::smtutil::Z3Interface::available();
140139
#endif

libsolidity/formal/ModelChecker.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,7 @@ class ModelChecker
5151
langutil::ErrorReporter& _errorReporter,
5252
std::map<solidity::util::h256, std::string> const& _smtlib2Responses,
5353
ModelCheckerSettings _settings = ModelCheckerSettings{},
54-
ReadCallback::Callback const& _smtCallback = ReadCallback::Callback(),
55-
smtutil::SMTSolverChoice _enabledSolvers = smtutil::SMTSolverChoice::All()
54+
ReadCallback::Callback const& _smtCallback = ReadCallback::Callback()
5655
);
5756

5857
// TODO This should be removed for 0.9.0.

libsolidity/formal/ModelCheckerSettings.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ struct ModelCheckerSettings
104104
{
105105
ModelCheckerContracts contracts = ModelCheckerContracts::Default();
106106
ModelCheckerEngine engine = ModelCheckerEngine::None();
107+
smtutil::SMTSolverChoice solvers = smtutil::SMTSolverChoice::All();
107108
ModelCheckerTargets targets = ModelCheckerTargets::Default();
108109
std::optional<unsigned> timeout;
109110
};

libsolidity/interface/CompilerStack.cpp

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,6 @@ static int g_compilerStackCounts = 0;
9494

9595
CompilerStack::CompilerStack(ReadCallback::Callback _readFile):
9696
m_readFile{std::move(_readFile)},
97-
m_enabledSMTSolvers{smtutil::SMTSolverChoice::All()},
9897
m_errorReporter{m_errorList}
9998
{
10099
// Because TypeProvider is currently a singleton API, we must ensure that
@@ -227,13 +226,6 @@ void CompilerStack::setModelCheckerSettings(ModelCheckerSettings _settings)
227226
m_modelCheckerSettings = _settings;
228227
}
229228

230-
void CompilerStack::setSMTSolverChoice(smtutil::SMTSolverChoice _enabledSMTSolvers)
231-
{
232-
if (m_stackState >= ParsedAndImported)
233-
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must set enabled SMT solvers before parsing."));
234-
m_enabledSMTSolvers = _enabledSMTSolvers;
235-
}
236-
237229
void CompilerStack::setLibraries(std::map<std::string, util::h160> const& _libraries)
238230
{
239231
if (m_stackState >= ParsedAndImported)
@@ -298,7 +290,6 @@ void CompilerStack::reset(bool _keepSettings)
298290
m_viaIR = false;
299291
m_evmVersion = langutil::EVMVersion();
300292
m_modelCheckerSettings = ModelCheckerSettings{};
301-
m_enabledSMTSolvers = smtutil::SMTSolverChoice::All();
302293
m_generateIR = false;
303294
m_generateEwasm = false;
304295
m_revertStrings = RevertStrings::Default;
@@ -546,7 +537,7 @@ bool CompilerStack::analyze()
546537

547538
if (noErrors)
548539
{
549-
ModelChecker modelChecker(m_errorReporter, m_smtlib2Responses, m_modelCheckerSettings, m_readFile, m_enabledSMTSolvers);
540+
ModelChecker modelChecker(m_errorReporter, m_smtlib2Responses, m_modelCheckerSettings, m_readFile);
550541
auto allSources = applyMap(m_sourceOrder, [](Source const* _source) { return _source->ast; });
551542
modelChecker.enableAllEnginesIfPragmaPresent(allSources);
552543
modelChecker.checkRequestedSourcesAndContracts(allSources);

libsolidity/interface/CompilerStack.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -174,8 +174,6 @@ class CompilerStack
174174

175175
/// Set model checker settings.
176176
void setModelCheckerSettings(ModelCheckerSettings _settings);
177-
/// Set which SMT solvers should be enabled.
178-
void setSMTSolverChoice(smtutil::SMTSolverChoice _enabledSolvers);
179177

180178
/// Sets the requested contract names by source.
181179
/// If empty, no filtering is performed and every contract
@@ -475,7 +473,6 @@ class CompilerStack
475473
bool m_viaIR = false;
476474
langutil::EVMVersion m_evmVersion;
477475
ModelCheckerSettings m_modelCheckerSettings;
478-
smtutil::SMTSolverChoice m_enabledSMTSolvers;
479476
std::map<std::string, std::set<std::string>> m_requestedContractNames;
480477
bool m_generateEvmBytecode = true;
481478
bool m_generateIR = false;

0 commit comments

Comments
 (0)