mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Create libsmtutil
This commit is contained in:
@@ -94,22 +94,12 @@ set(sources
|
||||
formal/BMC.h
|
||||
formal/CHC.cpp
|
||||
formal/CHC.h
|
||||
formal/CHCSmtLib2Interface.cpp
|
||||
formal/CHCSmtLib2Interface.h
|
||||
formal/CHCSolverInterface.h
|
||||
formal/EncodingContext.cpp
|
||||
formal/EncodingContext.h
|
||||
formal/ModelChecker.cpp
|
||||
formal/ModelChecker.h
|
||||
formal/SMTEncoder.cpp
|
||||
formal/SMTEncoder.h
|
||||
formal/SMTLib2Interface.cpp
|
||||
formal/SMTLib2Interface.h
|
||||
formal/SMTPortfolio.cpp
|
||||
formal/SMTPortfolio.h
|
||||
formal/SolverInterface.h
|
||||
formal/Sorts.cpp
|
||||
formal/Sorts.h
|
||||
formal/SSAVariable.cpp
|
||||
formal/SSAVariable.h
|
||||
formal/SymbolicState.cpp
|
||||
@@ -144,36 +134,6 @@ set(sources
|
||||
parsing/Token.h
|
||||
)
|
||||
|
||||
find_package(Z3 4.6.0)
|
||||
if (${Z3_FOUND})
|
||||
add_definitions(-DHAVE_Z3)
|
||||
message("Z3 SMT solver found. This enables optional SMT checking with Z3.")
|
||||
set(z3_SRCS formal/Z3Interface.cpp formal/Z3Interface.h formal/Z3CHCInterface.cpp formal/Z3CHCInterface.h)
|
||||
else()
|
||||
set(z3_SRCS)
|
||||
endif()
|
||||
add_library(solidity ${sources})
|
||||
target_link_libraries(solidity PUBLIC yul evmasm langutil smtutil solutil Boost::boost)
|
||||
|
||||
find_package(CVC4 QUIET)
|
||||
if (${CVC4_FOUND})
|
||||
add_definitions(-DHAVE_CVC4)
|
||||
message("CVC4 SMT solver found. This enables optional SMT checking with CVC4.")
|
||||
set(cvc4_SRCS formal/CVC4Interface.cpp formal/CVC4Interface.h)
|
||||
else()
|
||||
set(cvc4_SRCS)
|
||||
endif()
|
||||
|
||||
if (NOT (${Z3_FOUND} OR ${CVC4_FOUND}))
|
||||
message("No SMT solver found (or it has been forcefully disabled). Optional SMT checking will not be available.\
|
||||
\nPlease install Z3 or CVC4 or remove the option disabling them (USE_Z3, USE_CVC4).")
|
||||
endif()
|
||||
|
||||
add_library(solidity ${sources} ${z3_SRCS} ${cvc4_SRCS})
|
||||
target_link_libraries(solidity PUBLIC yul evmasm langutil solutil Boost::boost)
|
||||
|
||||
if (${Z3_FOUND})
|
||||
target_link_libraries(solidity PUBLIC z3::libz3)
|
||||
endif()
|
||||
|
||||
if (${CVC4_FOUND})
|
||||
target_link_libraries(solidity PUBLIC CVC4::CVC4)
|
||||
endif()
|
||||
|
||||
@@ -17,10 +17,11 @@
|
||||
|
||||
#include <libsolidity/formal/BMC.h>
|
||||
|
||||
#include <libsolidity/formal/SMTPortfolio.h>
|
||||
#include <libsolidity/formal/SymbolicState.h>
|
||||
#include <libsolidity/formal/SymbolicTypes.h>
|
||||
|
||||
#include <libsmtutil/SMTPortfolio.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity;
|
||||
using namespace solidity::util;
|
||||
|
||||
@@ -30,9 +30,10 @@
|
||||
|
||||
#include <libsolidity/formal/EncodingContext.h>
|
||||
#include <libsolidity/formal/SMTEncoder.h>
|
||||
#include <libsolidity/formal/SolverInterface.h>
|
||||
|
||||
#include <libsolidity/interface/ReadFile.h>
|
||||
|
||||
#include <libsmtutil/SolverInterface.h>
|
||||
#include <liblangutil/ErrorReporter.h>
|
||||
|
||||
#include <set>
|
||||
|
||||
@@ -17,16 +17,15 @@
|
||||
|
||||
#include <libsolidity/formal/CHC.h>
|
||||
|
||||
#include <libsolidity/formal/CHCSmtLib2Interface.h>
|
||||
|
||||
#ifdef HAVE_Z3
|
||||
#include <libsolidity/formal/Z3CHCInterface.h>
|
||||
#include <libsmtutil/Z3CHCInterface.h>
|
||||
#endif
|
||||
|
||||
#include <libsolidity/formal/SymbolicTypes.h>
|
||||
|
||||
#include <libsolidity/ast/TypeProvider.h>
|
||||
|
||||
#include <libsmtutil/CHCSmtLib2Interface.h>
|
||||
#include <libsolutil/Algorithms.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
@@ -32,10 +32,10 @@
|
||||
|
||||
#include <libsolidity/formal/SMTEncoder.h>
|
||||
|
||||
#include <libsolidity/formal/CHCSolverInterface.h>
|
||||
|
||||
#include <libsolidity/interface/ReadFile.h>
|
||||
|
||||
#include <libsmtutil/CHCSolverInterface.h>
|
||||
|
||||
#include <set>
|
||||
|
||||
namespace solidity::frontend
|
||||
|
||||
@@ -1,167 +0,0 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <libsolidity/formal/CHCSmtLib2Interface.h>
|
||||
|
||||
#include <libsolutil/Keccak256.h>
|
||||
|
||||
#include <boost/algorithm/string/join.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity;
|
||||
using namespace solidity::util;
|
||||
using namespace solidity::frontend;
|
||||
using namespace solidity::frontend::smt;
|
||||
|
||||
CHCSmtLib2Interface::CHCSmtLib2Interface(
|
||||
map<h256, string> const& _queryResponses,
|
||||
ReadCallback::Callback const& _smtCallback
|
||||
):
|
||||
m_smtlib2(make_unique<SMTLib2Interface>(_queryResponses, _smtCallback)),
|
||||
m_queryResponses(_queryResponses),
|
||||
m_smtCallback(_smtCallback)
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
void CHCSmtLib2Interface::reset()
|
||||
{
|
||||
m_accumulatedOutput.clear();
|
||||
m_variables.clear();
|
||||
}
|
||||
|
||||
void CHCSmtLib2Interface::registerRelation(smt::Expression const& _expr)
|
||||
{
|
||||
solAssert(_expr.sort, "");
|
||||
solAssert(_expr.sort->kind == smt::Kind::Function, "");
|
||||
if (!m_variables.count(_expr.name))
|
||||
{
|
||||
auto fSort = dynamic_pointer_cast<FunctionSort>(_expr.sort);
|
||||
string domain = m_smtlib2->toSmtLibSort(fSort->domain);
|
||||
// Relations are predicates which have implicit codomain Bool.
|
||||
m_variables.insert(_expr.name);
|
||||
write(
|
||||
"(declare-rel |" +
|
||||
_expr.name +
|
||||
"| " +
|
||||
domain +
|
||||
")"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void CHCSmtLib2Interface::addRule(smt::Expression const& _expr, std::string const& _name)
|
||||
{
|
||||
write(
|
||||
"(rule (! " +
|
||||
m_smtlib2->toSExpr(_expr) +
|
||||
" :named " +
|
||||
_name +
|
||||
"))"
|
||||
);
|
||||
}
|
||||
|
||||
pair<CheckResult, vector<string>> CHCSmtLib2Interface::query(smt::Expression const& _block)
|
||||
{
|
||||
string accumulated{};
|
||||
swap(m_accumulatedOutput, accumulated);
|
||||
for (auto const& var: m_smtlib2->variables())
|
||||
declareVariable(var.first, var.second);
|
||||
m_accumulatedOutput += accumulated;
|
||||
|
||||
string response = querySolver(
|
||||
m_accumulatedOutput +
|
||||
"\n(query " + _block.name + " :print-certificate true)"
|
||||
);
|
||||
|
||||
CheckResult result;
|
||||
// TODO proper parsing
|
||||
if (boost::starts_with(response, "sat\n"))
|
||||
result = CheckResult::SATISFIABLE;
|
||||
else if (boost::starts_with(response, "unsat\n"))
|
||||
result = CheckResult::UNSATISFIABLE;
|
||||
else if (boost::starts_with(response, "unknown\n"))
|
||||
result = CheckResult::UNKNOWN;
|
||||
else
|
||||
result = CheckResult::ERROR;
|
||||
|
||||
// TODO collect invariants or counterexamples.
|
||||
return make_pair(result, vector<string>{});
|
||||
}
|
||||
|
||||
void CHCSmtLib2Interface::declareVariable(string const& _name, SortPointer const& _sort)
|
||||
{
|
||||
solAssert(_sort, "");
|
||||
if (_sort->kind == Kind::Function)
|
||||
declareFunction(_name, _sort);
|
||||
else if (!m_variables.count(_name))
|
||||
{
|
||||
m_variables.insert(_name);
|
||||
write("(declare-var |" + _name + "| " + m_smtlib2->toSmtLibSort(*_sort) + ')');
|
||||
}
|
||||
}
|
||||
|
||||
void CHCSmtLib2Interface::declareFunction(string const& _name, SortPointer const& _sort)
|
||||
{
|
||||
solAssert(_sort, "");
|
||||
solAssert(_sort->kind == smt::Kind::Function, "");
|
||||
// TODO Use domain and codomain as key as well
|
||||
if (!m_variables.count(_name))
|
||||
{
|
||||
auto fSort = dynamic_pointer_cast<FunctionSort>(_sort);
|
||||
solAssert(fSort->codomain, "");
|
||||
string domain = m_smtlib2->toSmtLibSort(fSort->domain);
|
||||
string codomain = m_smtlib2->toSmtLibSort(*fSort->codomain);
|
||||
m_variables.insert(_name);
|
||||
write(
|
||||
"(declare-fun |" +
|
||||
_name +
|
||||
"| " +
|
||||
domain +
|
||||
" " +
|
||||
codomain +
|
||||
")"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void CHCSmtLib2Interface::write(string _data)
|
||||
{
|
||||
m_accumulatedOutput += move(_data) + "\n";
|
||||
}
|
||||
|
||||
string CHCSmtLib2Interface::querySolver(string const& _input)
|
||||
{
|
||||
util::h256 inputHash = util::keccak256(_input);
|
||||
if (m_queryResponses.count(inputHash))
|
||||
return m_queryResponses.at(inputHash);
|
||||
if (m_smtCallback)
|
||||
{
|
||||
auto result = m_smtCallback(ReadCallback::kindString(ReadCallback::Kind::SMTQuery), _input);
|
||||
if (result.success)
|
||||
return result.responseOrErrorMessage;
|
||||
}
|
||||
m_unhandledQueries.push_back(_input);
|
||||
return "unknown\n";
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Interface for solving Horn systems via smtlib2.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libsolidity/formal/CHCSolverInterface.h>
|
||||
|
||||
#include <libsolidity/formal/SMTLib2Interface.h>
|
||||
|
||||
namespace solidity::frontend::smt
|
||||
{
|
||||
|
||||
class CHCSmtLib2Interface: public CHCSolverInterface
|
||||
{
|
||||
public:
|
||||
explicit CHCSmtLib2Interface(
|
||||
std::map<util::h256, std::string> const& _queryResponses,
|
||||
ReadCallback::Callback const& _smtCallback
|
||||
);
|
||||
|
||||
void reset();
|
||||
|
||||
void registerRelation(Expression const& _expr) override;
|
||||
|
||||
void addRule(Expression const& _expr, std::string const& _name) override;
|
||||
|
||||
std::pair<CheckResult, std::vector<std::string>> query(Expression const& _expr) override;
|
||||
|
||||
void declareVariable(std::string const& _name, SortPointer const& _sort) override;
|
||||
|
||||
std::vector<std::string> unhandledQueries() const { return m_unhandledQueries; }
|
||||
|
||||
SMTLib2Interface* smtlib2Interface() const { return m_smtlib2.get(); }
|
||||
|
||||
private:
|
||||
void declareFunction(std::string const& _name, SortPointer const& _sort);
|
||||
|
||||
void write(std::string _data);
|
||||
|
||||
/// Communicates with the solver via the callback. Throws SMTSolverError on error.
|
||||
std::string querySolver(std::string const& _input);
|
||||
|
||||
/// Used to access toSmtLibSort, SExpr, and handle variables.
|
||||
std::unique_ptr<SMTLib2Interface> m_smtlib2;
|
||||
|
||||
std::string m_accumulatedOutput;
|
||||
std::set<std::string> m_variables;
|
||||
|
||||
std::map<util::h256, std::string> const& m_queryResponses;
|
||||
std::vector<std::string> m_unhandledQueries;
|
||||
|
||||
ReadCallback::Callback m_smtCallback;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Interface for constrained Horn solvers.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libsolidity/formal/SolverInterface.h>
|
||||
|
||||
namespace solidity::frontend::smt
|
||||
{
|
||||
|
||||
class CHCSolverInterface
|
||||
{
|
||||
public:
|
||||
virtual ~CHCSolverInterface() = default;
|
||||
|
||||
virtual void declareVariable(std::string const& _name, SortPointer const& _sort) = 0;
|
||||
|
||||
/// Takes a function declaration as a relation.
|
||||
virtual void registerRelation(Expression const& _expr) = 0;
|
||||
|
||||
/// Takes an implication and adds as rule.
|
||||
/// Needs to bound all vars as universally quantified.
|
||||
virtual void addRule(Expression const& _expr, std::string const& _name) = 0;
|
||||
|
||||
/// Takes a function application and checks
|
||||
/// for reachability.
|
||||
virtual std::pair<CheckResult, std::vector<std::string>> query(
|
||||
Expression const& _expr
|
||||
) = 0;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,270 +0,0 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <libsolidity/formal/CVC4Interface.h>
|
||||
|
||||
#include <liblangutil/Exceptions.h>
|
||||
#include <libsolutil/CommonIO.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity;
|
||||
using namespace solidity::util;
|
||||
using namespace solidity::frontend::smt;
|
||||
|
||||
CVC4Interface::CVC4Interface():
|
||||
m_solver(&m_context)
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
void CVC4Interface::reset()
|
||||
{
|
||||
m_variables.clear();
|
||||
m_solver.reset();
|
||||
m_solver.setOption("produce-models", true);
|
||||
m_solver.setResourceLimit(resourceLimit);
|
||||
}
|
||||
|
||||
void CVC4Interface::push()
|
||||
{
|
||||
m_solver.push();
|
||||
}
|
||||
|
||||
void CVC4Interface::pop()
|
||||
{
|
||||
m_solver.pop();
|
||||
}
|
||||
|
||||
void CVC4Interface::declareVariable(string const& _name, SortPointer const& _sort)
|
||||
{
|
||||
solAssert(_sort, "");
|
||||
m_variables[_name] = m_context.mkVar(_name.c_str(), cvc4Sort(*_sort));
|
||||
}
|
||||
|
||||
void CVC4Interface::addAssertion(Expression const& _expr)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_solver.assertFormula(toCVC4Expr(_expr));
|
||||
}
|
||||
catch (CVC4::TypeCheckingException const& _e)
|
||||
{
|
||||
solAssert(false, _e.what());
|
||||
}
|
||||
catch (CVC4::LogicException const& _e)
|
||||
{
|
||||
solAssert(false, _e.what());
|
||||
}
|
||||
catch (CVC4::UnsafeInterruptException const& _e)
|
||||
{
|
||||
solAssert(false, _e.what());
|
||||
}
|
||||
catch (CVC4::Exception const& _e)
|
||||
{
|
||||
solAssert(false, _e.what());
|
||||
}
|
||||
}
|
||||
|
||||
pair<CheckResult, vector<string>> CVC4Interface::check(vector<Expression> const& _expressionsToEvaluate)
|
||||
{
|
||||
CheckResult result;
|
||||
vector<string> values;
|
||||
try
|
||||
{
|
||||
switch (m_solver.checkSat().isSat())
|
||||
{
|
||||
case CVC4::Result::SAT:
|
||||
result = CheckResult::SATISFIABLE;
|
||||
break;
|
||||
case CVC4::Result::UNSAT:
|
||||
result = CheckResult::UNSATISFIABLE;
|
||||
break;
|
||||
case CVC4::Result::SAT_UNKNOWN:
|
||||
result = CheckResult::UNKNOWN;
|
||||
break;
|
||||
default:
|
||||
solAssert(false, "");
|
||||
}
|
||||
|
||||
if (result == CheckResult::SATISFIABLE && !_expressionsToEvaluate.empty())
|
||||
{
|
||||
for (Expression const& e: _expressionsToEvaluate)
|
||||
values.push_back(toString(m_solver.getValue(toCVC4Expr(e))));
|
||||
}
|
||||
}
|
||||
catch (CVC4::Exception const&)
|
||||
{
|
||||
result = CheckResult::ERROR;
|
||||
values.clear();
|
||||
}
|
||||
|
||||
return make_pair(result, values);
|
||||
}
|
||||
|
||||
CVC4::Expr CVC4Interface::toCVC4Expr(Expression const& _expr)
|
||||
{
|
||||
// Variable
|
||||
if (_expr.arguments.empty() && m_variables.count(_expr.name))
|
||||
return m_variables.at(_expr.name);
|
||||
|
||||
vector<CVC4::Expr> arguments;
|
||||
for (auto const& arg: _expr.arguments)
|
||||
arguments.push_back(toCVC4Expr(arg));
|
||||
|
||||
try
|
||||
{
|
||||
string const& n = _expr.name;
|
||||
// Function application
|
||||
if (!arguments.empty() && m_variables.count(_expr.name))
|
||||
return m_context.mkExpr(CVC4::kind::APPLY_UF, m_variables.at(n), arguments);
|
||||
// Literal
|
||||
else if (arguments.empty())
|
||||
{
|
||||
if (n == "true")
|
||||
return m_context.mkConst(true);
|
||||
else if (n == "false")
|
||||
return m_context.mkConst(false);
|
||||
else if (auto sortSort = dynamic_pointer_cast<SortSort>(_expr.sort))
|
||||
return m_context.mkVar(n, cvc4Sort(*sortSort->inner));
|
||||
else
|
||||
try
|
||||
{
|
||||
return m_context.mkConst(CVC4::Rational(n));
|
||||
}
|
||||
catch (CVC4::TypeCheckingException const& _e)
|
||||
{
|
||||
solAssert(false, _e.what());
|
||||
}
|
||||
catch (CVC4::Exception const& _e)
|
||||
{
|
||||
solAssert(false, _e.what());
|
||||
}
|
||||
}
|
||||
|
||||
solAssert(_expr.hasCorrectArity(), "");
|
||||
if (n == "ite")
|
||||
return arguments[0].iteExpr(arguments[1], arguments[2]);
|
||||
else if (n == "not")
|
||||
return arguments[0].notExpr();
|
||||
else if (n == "and")
|
||||
return arguments[0].andExpr(arguments[1]);
|
||||
else if (n == "or")
|
||||
return arguments[0].orExpr(arguments[1]);
|
||||
else if (n == "implies")
|
||||
return m_context.mkExpr(CVC4::kind::IMPLIES, arguments[0], arguments[1]);
|
||||
else if (n == "=")
|
||||
return m_context.mkExpr(CVC4::kind::EQUAL, arguments[0], arguments[1]);
|
||||
else if (n == "<")
|
||||
return m_context.mkExpr(CVC4::kind::LT, arguments[0], arguments[1]);
|
||||
else if (n == "<=")
|
||||
return m_context.mkExpr(CVC4::kind::LEQ, arguments[0], arguments[1]);
|
||||
else if (n == ">")
|
||||
return m_context.mkExpr(CVC4::kind::GT, arguments[0], arguments[1]);
|
||||
else if (n == ">=")
|
||||
return m_context.mkExpr(CVC4::kind::GEQ, arguments[0], arguments[1]);
|
||||
else if (n == "+")
|
||||
return m_context.mkExpr(CVC4::kind::PLUS, arguments[0], arguments[1]);
|
||||
else if (n == "-")
|
||||
return m_context.mkExpr(CVC4::kind::MINUS, arguments[0], arguments[1]);
|
||||
else if (n == "*")
|
||||
return m_context.mkExpr(CVC4::kind::MULT, arguments[0], arguments[1]);
|
||||
else if (n == "/")
|
||||
return m_context.mkExpr(CVC4::kind::INTS_DIVISION_TOTAL, arguments[0], arguments[1]);
|
||||
else if (n == "mod")
|
||||
return m_context.mkExpr(CVC4::kind::INTS_MODULUS, arguments[0], arguments[1]);
|
||||
else if (n == "select")
|
||||
return m_context.mkExpr(CVC4::kind::SELECT, arguments[0], arguments[1]);
|
||||
else if (n == "store")
|
||||
return m_context.mkExpr(CVC4::kind::STORE, arguments[0], arguments[1], arguments[2]);
|
||||
else if (n == "const_array")
|
||||
{
|
||||
shared_ptr<SortSort> sortSort = std::dynamic_pointer_cast<SortSort>(_expr.arguments[0].sort);
|
||||
solAssert(sortSort, "");
|
||||
return m_context.mkConst(CVC4::ArrayStoreAll(cvc4Sort(*sortSort->inner), arguments[1]));
|
||||
}
|
||||
else if (n == "tuple_get")
|
||||
{
|
||||
shared_ptr<TupleSort> tupleSort = std::dynamic_pointer_cast<TupleSort>(_expr.arguments[0].sort);
|
||||
solAssert(tupleSort, "");
|
||||
CVC4::DatatypeType tt = m_context.mkTupleType(cvc4Sort(tupleSort->components));
|
||||
CVC4::Datatype const& dt = tt.getDatatype();
|
||||
size_t index = std::stoi(_expr.arguments[1].name);
|
||||
CVC4::Expr s = dt[0][index].getSelector();
|
||||
return m_context.mkExpr(CVC4::kind::APPLY_SELECTOR, s, arguments[0]);
|
||||
}
|
||||
else if (n == "tuple_constructor")
|
||||
{
|
||||
shared_ptr<TupleSort> tupleSort = std::dynamic_pointer_cast<TupleSort>(_expr.sort);
|
||||
solAssert(tupleSort, "");
|
||||
CVC4::DatatypeType tt = m_context.mkTupleType(cvc4Sort(tupleSort->components));
|
||||
CVC4::Datatype const& dt = tt.getDatatype();
|
||||
CVC4::Expr c = dt[0].getConstructor();
|
||||
return m_context.mkExpr(CVC4::kind::APPLY_CONSTRUCTOR, c, arguments);
|
||||
}
|
||||
|
||||
solAssert(false, "");
|
||||
}
|
||||
catch (CVC4::TypeCheckingException const& _e)
|
||||
{
|
||||
solAssert(false, _e.what());
|
||||
}
|
||||
catch (CVC4::Exception const& _e)
|
||||
{
|
||||
solAssert(false, _e.what());
|
||||
}
|
||||
|
||||
solAssert(false, "");
|
||||
}
|
||||
|
||||
CVC4::Type CVC4Interface::cvc4Sort(Sort const& _sort)
|
||||
{
|
||||
switch (_sort.kind)
|
||||
{
|
||||
case Kind::Bool:
|
||||
return m_context.booleanType();
|
||||
case Kind::Int:
|
||||
return m_context.integerType();
|
||||
case Kind::Function:
|
||||
{
|
||||
FunctionSort const& fSort = dynamic_cast<FunctionSort const&>(_sort);
|
||||
return m_context.mkFunctionType(cvc4Sort(fSort.domain), cvc4Sort(*fSort.codomain));
|
||||
}
|
||||
case Kind::Array:
|
||||
{
|
||||
auto const& arraySort = dynamic_cast<ArraySort const&>(_sort);
|
||||
return m_context.mkArrayType(cvc4Sort(*arraySort.domain), cvc4Sort(*arraySort.range));
|
||||
}
|
||||
case Kind::Tuple:
|
||||
{
|
||||
auto const& tupleSort = dynamic_cast<TupleSort const&>(_sort);
|
||||
return m_context.mkTupleType(cvc4Sort(tupleSort.components));
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
solAssert(false, "");
|
||||
// Cannot be reached.
|
||||
return m_context.integerType();
|
||||
}
|
||||
|
||||
vector<CVC4::Type> CVC4Interface::cvc4Sort(vector<SortPointer> const& _sorts)
|
||||
{
|
||||
vector<CVC4::Type> cvc4Sorts;
|
||||
for (auto const& _sort: _sorts)
|
||||
cvc4Sorts.push_back(cvc4Sort(*_sort));
|
||||
return cvc4Sorts;
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libsolidity/formal/SolverInterface.h>
|
||||
#include <boost/noncopyable.hpp>
|
||||
|
||||
#if defined(__GLIBC__)
|
||||
// The CVC4 headers includes the deprecated system headers <ext/hash_map>
|
||||
// and <ext/hash_set>. These headers cause a warning that will break the
|
||||
// build, unless _GLIBCXX_PERMIT_BACKWARD_HASH is set.
|
||||
#define _GLIBCXX_PERMIT_BACKWARD_HASH
|
||||
#endif
|
||||
|
||||
#include <cvc4/cvc4.h>
|
||||
|
||||
#if defined(__GLIBC__)
|
||||
#undef _GLIBCXX_PERMIT_BACKWARD_HASH
|
||||
#endif
|
||||
|
||||
namespace solidity::frontend::smt
|
||||
{
|
||||
|
||||
class CVC4Interface: public SolverInterface, public boost::noncopyable
|
||||
{
|
||||
public:
|
||||
CVC4Interface();
|
||||
|
||||
void reset() override;
|
||||
|
||||
void push() override;
|
||||
void pop() override;
|
||||
|
||||
void declareVariable(std::string const&, SortPointer const&) override;
|
||||
|
||||
void addAssertion(Expression const& _expr) override;
|
||||
std::pair<CheckResult, std::vector<std::string>> check(std::vector<Expression> const& _expressionsToEvaluate) override;
|
||||
|
||||
private:
|
||||
CVC4::Expr toCVC4Expr(Expression const& _expr);
|
||||
CVC4::Type cvc4Sort(smt::Sort const& _sort);
|
||||
std::vector<CVC4::Type> cvc4Sort(std::vector<smt::SortPointer> const& _sorts);
|
||||
|
||||
CVC4::ExprManager m_context;
|
||||
CVC4::SmtEngine m_solver;
|
||||
std::map<std::string, CVC4::Expr> m_variables;
|
||||
|
||||
// CVC4 "basic resources" limit.
|
||||
// This is used to make the runs more deterministic and platform/machine independent.
|
||||
// The tests start failing for CVC4 with less than 6000,
|
||||
// so using double that.
|
||||
static int const resourceLimit = 12000;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -17,10 +17,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libsolidity/formal/SolverInterface.h>
|
||||
#include <libsolidity/formal/SymbolicState.h>
|
||||
#include <libsolidity/formal/SymbolicVariables.h>
|
||||
|
||||
#include <libsmtutil/SolverInterface.h>
|
||||
|
||||
#include <unordered_map>
|
||||
#include <set>
|
||||
|
||||
|
||||
@@ -25,9 +25,10 @@
|
||||
#include <libsolidity/formal/BMC.h>
|
||||
#include <libsolidity/formal/CHC.h>
|
||||
#include <libsolidity/formal/EncodingContext.h>
|
||||
#include <libsolidity/formal/SolverInterface.h>
|
||||
|
||||
#include <libsolidity/interface/ReadFile.h>
|
||||
|
||||
#include <libsmtutil/SolverInterface.h>
|
||||
#include <liblangutil/ErrorReporter.h>
|
||||
|
||||
namespace solidity::langutil
|
||||
|
||||
@@ -18,10 +18,11 @@
|
||||
#include <libsolidity/formal/SMTEncoder.h>
|
||||
|
||||
#include <libsolidity/ast/TypeProvider.h>
|
||||
#include <libsolidity/formal/SMTPortfolio.h>
|
||||
#include <libsolidity/formal/SymbolicState.h>
|
||||
#include <libsolidity/formal/SymbolicTypes.h>
|
||||
|
||||
#include <libsmtutil/SMTPortfolio.h>
|
||||
|
||||
#include <boost/range/adaptors.hpp>
|
||||
#include <boost/range/adaptor/reversed.hpp>
|
||||
|
||||
|
||||
@@ -1,281 +0,0 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <libsolidity/formal/SMTLib2Interface.h>
|
||||
|
||||
#include <libsolutil/Keccak256.h>
|
||||
|
||||
#include <boost/algorithm/string/join.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity;
|
||||
using namespace solidity::util;
|
||||
using namespace solidity::frontend;
|
||||
using namespace solidity::frontend::smt;
|
||||
|
||||
SMTLib2Interface::SMTLib2Interface(
|
||||
map<h256, string> const& _queryResponses,
|
||||
ReadCallback::Callback _smtCallback
|
||||
):
|
||||
m_queryResponses(_queryResponses),
|
||||
m_smtCallback(std::move(_smtCallback))
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
void SMTLib2Interface::reset()
|
||||
{
|
||||
m_accumulatedOutput.clear();
|
||||
m_accumulatedOutput.emplace_back();
|
||||
m_variables.clear();
|
||||
m_userSorts.clear();
|
||||
write("(set-option :produce-models true)");
|
||||
write("(set-logic ALL)");
|
||||
}
|
||||
|
||||
void SMTLib2Interface::push()
|
||||
{
|
||||
m_accumulatedOutput.emplace_back();
|
||||
}
|
||||
|
||||
void SMTLib2Interface::pop()
|
||||
{
|
||||
solAssert(!m_accumulatedOutput.empty(), "");
|
||||
m_accumulatedOutput.pop_back();
|
||||
}
|
||||
|
||||
void SMTLib2Interface::declareVariable(string const& _name, SortPointer const& _sort)
|
||||
{
|
||||
solAssert(_sort, "");
|
||||
if (_sort->kind == Kind::Function)
|
||||
declareFunction(_name, _sort);
|
||||
else if (!m_variables.count(_name))
|
||||
{
|
||||
m_variables.emplace(_name, _sort);
|
||||
write("(declare-fun |" + _name + "| () " + toSmtLibSort(*_sort) + ')');
|
||||
}
|
||||
}
|
||||
|
||||
void SMTLib2Interface::declareFunction(string const& _name, SortPointer const& _sort)
|
||||
{
|
||||
solAssert(_sort, "");
|
||||
solAssert(_sort->kind == smt::Kind::Function, "");
|
||||
// TODO Use domain and codomain as key as well
|
||||
if (!m_variables.count(_name))
|
||||
{
|
||||
auto const& fSort = dynamic_pointer_cast<FunctionSort>(_sort);
|
||||
string domain = toSmtLibSort(fSort->domain);
|
||||
string codomain = toSmtLibSort(*fSort->codomain);
|
||||
m_variables.emplace(_name, _sort);
|
||||
write(
|
||||
"(declare-fun |" +
|
||||
_name +
|
||||
"| " +
|
||||
domain +
|
||||
" " +
|
||||
codomain +
|
||||
")"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void SMTLib2Interface::addAssertion(smt::Expression const& _expr)
|
||||
{
|
||||
write("(assert " + toSExpr(_expr) + ")");
|
||||
}
|
||||
|
||||
pair<CheckResult, vector<string>> SMTLib2Interface::check(vector<smt::Expression> const& _expressionsToEvaluate)
|
||||
{
|
||||
string response = querySolver(
|
||||
boost::algorithm::join(m_accumulatedOutput, "\n") +
|
||||
checkSatAndGetValuesCommand(_expressionsToEvaluate)
|
||||
);
|
||||
|
||||
CheckResult result;
|
||||
// TODO proper parsing
|
||||
if (boost::starts_with(response, "sat\n"))
|
||||
result = CheckResult::SATISFIABLE;
|
||||
else if (boost::starts_with(response, "unsat\n"))
|
||||
result = CheckResult::UNSATISFIABLE;
|
||||
else if (boost::starts_with(response, "unknown\n"))
|
||||
result = CheckResult::UNKNOWN;
|
||||
else
|
||||
result = CheckResult::ERROR;
|
||||
|
||||
vector<string> values;
|
||||
if (result == CheckResult::SATISFIABLE && result != CheckResult::ERROR)
|
||||
values = parseValues(find(response.cbegin(), response.cend(), '\n'), response.cend());
|
||||
return make_pair(result, values);
|
||||
}
|
||||
|
||||
string SMTLib2Interface::toSExpr(smt::Expression const& _expr)
|
||||
{
|
||||
if (_expr.arguments.empty())
|
||||
return _expr.name;
|
||||
|
||||
std::string sexpr = "(";
|
||||
if (_expr.name == "const_array")
|
||||
{
|
||||
solAssert(_expr.arguments.size() == 2, "");
|
||||
auto sortSort = std::dynamic_pointer_cast<SortSort>(_expr.arguments.at(0).sort);
|
||||
solAssert(sortSort, "");
|
||||
auto arraySort = dynamic_pointer_cast<ArraySort>(sortSort->inner);
|
||||
solAssert(arraySort, "");
|
||||
sexpr += "(as const " + toSmtLibSort(*arraySort) + ") ";
|
||||
sexpr += toSExpr(_expr.arguments.at(1));
|
||||
}
|
||||
else if (_expr.name == "tuple_get")
|
||||
{
|
||||
solAssert(_expr.arguments.size() == 2, "");
|
||||
auto tupleSort = dynamic_pointer_cast<TupleSort>(_expr.arguments.at(0).sort);
|
||||
unsigned index = std::stoi(_expr.arguments.at(1).name);
|
||||
solAssert(index < tupleSort->members.size(), "");
|
||||
sexpr += "|" + tupleSort->members.at(index) + "| " + toSExpr(_expr.arguments.at(0));
|
||||
}
|
||||
else if (_expr.name == "tuple_constructor")
|
||||
{
|
||||
auto tupleSort = dynamic_pointer_cast<TupleSort>(_expr.sort);
|
||||
solAssert(tupleSort, "");
|
||||
sexpr += "|" + tupleSort->name + "|";
|
||||
for (auto const& arg: _expr.arguments)
|
||||
sexpr += " " + toSExpr(arg);
|
||||
}
|
||||
else
|
||||
{
|
||||
sexpr += _expr.name;
|
||||
for (auto const& arg: _expr.arguments)
|
||||
sexpr += " " + toSExpr(arg);
|
||||
}
|
||||
sexpr += ")";
|
||||
return sexpr;
|
||||
}
|
||||
|
||||
string SMTLib2Interface::toSmtLibSort(Sort const& _sort)
|
||||
{
|
||||
switch (_sort.kind)
|
||||
{
|
||||
case Kind::Int:
|
||||
return "Int";
|
||||
case Kind::Bool:
|
||||
return "Bool";
|
||||
case Kind::Array:
|
||||
{
|
||||
auto const& arraySort = dynamic_cast<ArraySort const&>(_sort);
|
||||
solAssert(arraySort.domain && arraySort.range, "");
|
||||
return "(Array " + toSmtLibSort(*arraySort.domain) + ' ' + toSmtLibSort(*arraySort.range) + ')';
|
||||
}
|
||||
case Kind::Tuple:
|
||||
{
|
||||
auto const& tupleSort = dynamic_cast<TupleSort const&>(_sort);
|
||||
string tupleName = "|" + tupleSort.name + "|";
|
||||
if (!m_userSorts.count(tupleName))
|
||||
{
|
||||
m_userSorts.insert(tupleName);
|
||||
string decl("(declare-datatypes ((" + tupleName + " 0)) (((" + tupleName);
|
||||
solAssert(tupleSort.members.size() == tupleSort.components.size(), "");
|
||||
for (unsigned i = 0; i < tupleSort.members.size(); ++i)
|
||||
decl += " (|" + tupleSort.members.at(i) + "| " + toSmtLibSort(*tupleSort.components.at(i)) + ")";
|
||||
decl += "))))";
|
||||
write(decl);
|
||||
}
|
||||
|
||||
return tupleName;
|
||||
}
|
||||
default:
|
||||
solAssert(false, "Invalid SMT sort");
|
||||
}
|
||||
}
|
||||
|
||||
string SMTLib2Interface::toSmtLibSort(vector<SortPointer> const& _sorts)
|
||||
{
|
||||
string ssort("(");
|
||||
for (auto const& sort: _sorts)
|
||||
ssort += toSmtLibSort(*sort) + " ";
|
||||
ssort += ")";
|
||||
return ssort;
|
||||
}
|
||||
|
||||
void SMTLib2Interface::write(string _data)
|
||||
{
|
||||
solAssert(!m_accumulatedOutput.empty(), "");
|
||||
m_accumulatedOutput.back() += move(_data) + "\n";
|
||||
}
|
||||
|
||||
string SMTLib2Interface::checkSatAndGetValuesCommand(vector<smt::Expression> const& _expressionsToEvaluate)
|
||||
{
|
||||
string command;
|
||||
if (_expressionsToEvaluate.empty())
|
||||
command = "(check-sat)\n";
|
||||
else
|
||||
{
|
||||
// TODO make sure these are unique
|
||||
for (size_t i = 0; i < _expressionsToEvaluate.size(); i++)
|
||||
{
|
||||
auto const& e = _expressionsToEvaluate.at(i);
|
||||
solAssert(e.sort->kind == Kind::Int || e.sort->kind == Kind::Bool, "Invalid sort for expression to evaluate.");
|
||||
command += "(declare-const |EVALEXPR_" + to_string(i) + "| " + (e.sort->kind == Kind::Int ? "Int" : "Bool") + ")\n";
|
||||
command += "(assert (= |EVALEXPR_" + to_string(i) + "| " + toSExpr(e) + "))\n";
|
||||
}
|
||||
command += "(check-sat)\n";
|
||||
command += "(get-value (";
|
||||
for (size_t i = 0; i < _expressionsToEvaluate.size(); i++)
|
||||
command += "|EVALEXPR_" + to_string(i) + "| ";
|
||||
command += "))\n";
|
||||
}
|
||||
|
||||
return command;
|
||||
}
|
||||
|
||||
vector<string> SMTLib2Interface::parseValues(string::const_iterator _start, string::const_iterator _end)
|
||||
{
|
||||
vector<string> values;
|
||||
while (_start < _end)
|
||||
{
|
||||
auto valStart = find(_start, _end, ' ');
|
||||
if (valStart < _end)
|
||||
++valStart;
|
||||
auto valEnd = find(valStart, _end, ')');
|
||||
values.emplace_back(valStart, valEnd);
|
||||
_start = find(valEnd, _end, '(');
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
string SMTLib2Interface::querySolver(string const& _input)
|
||||
{
|
||||
h256 inputHash = keccak256(_input);
|
||||
if (m_queryResponses.count(inputHash))
|
||||
return m_queryResponses.at(inputHash);
|
||||
if (m_smtCallback)
|
||||
{
|
||||
auto result = m_smtCallback(ReadCallback::kindString(ReadCallback::Kind::SMTQuery), _input);
|
||||
if (result.success)
|
||||
return result.responseOrErrorMessage;
|
||||
}
|
||||
m_unhandledQueries.push_back(_input);
|
||||
return "unknown\n";
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libsolidity/formal/SolverInterface.h>
|
||||
|
||||
#include <libsolidity/interface/ReadFile.h>
|
||||
#include <liblangutil/Exceptions.h>
|
||||
#include <libsolutil/Common.h>
|
||||
#include <libsolutil/FixedHash.h>
|
||||
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <cstdio>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace solidity::frontend::smt
|
||||
{
|
||||
|
||||
class SMTLib2Interface: public SolverInterface, public boost::noncopyable
|
||||
{
|
||||
public:
|
||||
explicit SMTLib2Interface(
|
||||
std::map<util::h256, std::string> const& _queryResponses,
|
||||
ReadCallback::Callback _smtCallback
|
||||
);
|
||||
|
||||
void reset() override;
|
||||
|
||||
void push() override;
|
||||
void pop() override;
|
||||
|
||||
void declareVariable(std::string const&, SortPointer const&) override;
|
||||
|
||||
void addAssertion(smt::Expression const& _expr) override;
|
||||
std::pair<CheckResult, std::vector<std::string>> check(std::vector<smt::Expression> const& _expressionsToEvaluate) override;
|
||||
|
||||
std::vector<std::string> unhandledQueries() override { return m_unhandledQueries; }
|
||||
|
||||
// Used by CHCSmtLib2Interface
|
||||
std::string toSExpr(smt::Expression const& _expr);
|
||||
std::string toSmtLibSort(Sort const& _sort);
|
||||
std::string toSmtLibSort(std::vector<SortPointer> const& _sort);
|
||||
|
||||
std::map<std::string, SortPointer> variables() { return m_variables; }
|
||||
|
||||
private:
|
||||
void declareFunction(std::string const& _name, SortPointer const& _sort);
|
||||
|
||||
void write(std::string _data);
|
||||
|
||||
std::string checkSatAndGetValuesCommand(std::vector<smt::Expression> const& _expressionsToEvaluate);
|
||||
std::vector<std::string> parseValues(std::string::const_iterator _start, std::string::const_iterator _end);
|
||||
|
||||
/// Communicates with the solver via the callback. Throws SMTSolverError on error.
|
||||
std::string querySolver(std::string const& _input);
|
||||
|
||||
std::vector<std::string> m_accumulatedOutput;
|
||||
std::map<std::string, SortPointer> m_variables;
|
||||
std::set<std::string> m_userSorts;
|
||||
|
||||
std::map<util::h256, std::string> const& m_queryResponses;
|
||||
std::vector<std::string> m_unhandledQueries;
|
||||
|
||||
ReadCallback::Callback m_smtCallback;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,152 +0,0 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <libsolidity/formal/SMTPortfolio.h>
|
||||
|
||||
#ifdef HAVE_Z3
|
||||
#include <libsolidity/formal/Z3Interface.h>
|
||||
#endif
|
||||
#ifdef HAVE_CVC4
|
||||
#include <libsolidity/formal/CVC4Interface.h>
|
||||
#endif
|
||||
#include <libsolidity/formal/SMTLib2Interface.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity;
|
||||
using namespace solidity::util;
|
||||
using namespace solidity::frontend;
|
||||
using namespace solidity::frontend::smt;
|
||||
|
||||
SMTPortfolio::SMTPortfolio(
|
||||
map<h256, string> const& _smtlib2Responses,
|
||||
ReadCallback::Callback const& _smtCallback,
|
||||
[[maybe_unused]] SMTSolverChoice _enabledSolvers
|
||||
)
|
||||
{
|
||||
m_solvers.emplace_back(make_unique<smt::SMTLib2Interface>(_smtlib2Responses, _smtCallback));
|
||||
#ifdef HAVE_Z3
|
||||
if (_enabledSolvers.z3)
|
||||
m_solvers.emplace_back(make_unique<smt::Z3Interface>());
|
||||
#endif
|
||||
#ifdef HAVE_CVC4
|
||||
if (_enabledSolvers.cvc4)
|
||||
m_solvers.emplace_back(make_unique<smt::CVC4Interface>());
|
||||
#endif
|
||||
}
|
||||
|
||||
void SMTPortfolio::reset()
|
||||
{
|
||||
for (auto const& s: m_solvers)
|
||||
s->reset();
|
||||
}
|
||||
|
||||
void SMTPortfolio::push()
|
||||
{
|
||||
for (auto const& s: m_solvers)
|
||||
s->push();
|
||||
}
|
||||
|
||||
void SMTPortfolio::pop()
|
||||
{
|
||||
for (auto const& s: m_solvers)
|
||||
s->pop();
|
||||
}
|
||||
|
||||
void SMTPortfolio::declareVariable(string const& _name, SortPointer const& _sort)
|
||||
{
|
||||
solAssert(_sort, "");
|
||||
for (auto const& s: m_solvers)
|
||||
s->declareVariable(_name, _sort);
|
||||
}
|
||||
|
||||
void SMTPortfolio::addAssertion(smt::Expression const& _expr)
|
||||
{
|
||||
for (auto const& s: m_solvers)
|
||||
s->addAssertion(_expr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Broadcasts the SMT query to all solvers and returns a single result.
|
||||
* This comment explains how this result is decided.
|
||||
*
|
||||
* When a solver is queried, there are four possible answers:
|
||||
* SATISFIABLE (SAT), UNSATISFIABLE (UNSAT), UNKNOWN, CONFLICTING, ERROR
|
||||
* We say that a solver _answered_ the query if it returns either:
|
||||
* SAT or UNSAT
|
||||
* A solver did not answer the query if it returns either:
|
||||
* UNKNOWN (it tried but couldn't solve it) or ERROR (crash, internal error, API error, etc).
|
||||
*
|
||||
* Ideally all solvers answer the query and agree on what the answer is
|
||||
* (all say SAT or all say UNSAT).
|
||||
*
|
||||
* The actual logic as as follows:
|
||||
* 1) If at least one solver answers the query, all the non-answer results are ignored.
|
||||
* Here SAT/UNSAT is preferred over UNKNOWN since it's an actual answer, and over ERROR
|
||||
* because one buggy solver/integration shouldn't break the portfolio.
|
||||
*
|
||||
* 2) If at least one solver answers SAT and at least one answers UNSAT, at least one of them is buggy
|
||||
* and the result is CONFLICTING.
|
||||
* In the future if we have more than 2 solvers enabled we could go with the majority.
|
||||
*
|
||||
* 3) If NO solver answers the query:
|
||||
* If at least one solver returned UNKNOWN (where the rest returned ERROR), the result is UNKNOWN.
|
||||
* This is preferred over ERROR since the SMTChecker might decide to abstract the query
|
||||
* when it is told that this is a hard query to solve.
|
||||
*
|
||||
* If all solvers return ERROR, the result is ERROR.
|
||||
*/
|
||||
pair<CheckResult, vector<string>> SMTPortfolio::check(vector<smt::Expression> const& _expressionsToEvaluate)
|
||||
{
|
||||
CheckResult lastResult = CheckResult::ERROR;
|
||||
vector<string> finalValues;
|
||||
for (auto const& s: m_solvers)
|
||||
{
|
||||
CheckResult result;
|
||||
vector<string> values;
|
||||
tie(result, values) = s->check(_expressionsToEvaluate);
|
||||
if (solverAnswered(result))
|
||||
{
|
||||
if (!solverAnswered(lastResult))
|
||||
{
|
||||
lastResult = result;
|
||||
finalValues = std::move(values);
|
||||
}
|
||||
else if (lastResult != result)
|
||||
{
|
||||
lastResult = CheckResult::CONFLICTING;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (result == CheckResult::UNKNOWN && lastResult == CheckResult::ERROR)
|
||||
lastResult = result;
|
||||
}
|
||||
return make_pair(lastResult, finalValues);
|
||||
}
|
||||
|
||||
vector<string> SMTPortfolio::unhandledQueries()
|
||||
{
|
||||
// This code assumes that the constructor guarantees that
|
||||
// SmtLib2Interface is in position 0.
|
||||
solAssert(!m_solvers.empty(), "");
|
||||
solAssert(dynamic_cast<smt::SMTLib2Interface*>(m_solvers.front().get()), "");
|
||||
return m_solvers.front()->unhandledQueries();
|
||||
}
|
||||
|
||||
bool SMTPortfolio::solverAnswered(CheckResult result)
|
||||
{
|
||||
return result == CheckResult::SATISFIABLE || result == CheckResult::UNSATISFIABLE;
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
#include <libsolidity/formal/SolverInterface.h>
|
||||
#include <libsolidity/interface/ReadFile.h>
|
||||
#include <libsolutil/FixedHash.h>
|
||||
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
namespace solidity::frontend::smt
|
||||
{
|
||||
|
||||
/**
|
||||
* The SMTPortfolio wraps all available solvers within a single interface,
|
||||
* propagating the functionalities to all solvers.
|
||||
* It also checks whether different solvers give conflicting answers
|
||||
* to SMT queries.
|
||||
*/
|
||||
class SMTPortfolio: public SolverInterface, public boost::noncopyable
|
||||
{
|
||||
public:
|
||||
SMTPortfolio(
|
||||
std::map<util::h256, std::string> const& _smtlib2Responses,
|
||||
ReadCallback::Callback const& _smtCallback,
|
||||
SMTSolverChoice _enabledSolvers
|
||||
);
|
||||
|
||||
void reset() override;
|
||||
|
||||
void push() override;
|
||||
void pop() override;
|
||||
|
||||
void declareVariable(std::string const&, SortPointer const&) override;
|
||||
|
||||
void addAssertion(smt::Expression const& _expr) override;
|
||||
|
||||
std::pair<CheckResult, std::vector<std::string>> check(std::vector<smt::Expression> const& _expressionsToEvaluate) override;
|
||||
|
||||
std::vector<std::string> unhandledQueries() override;
|
||||
unsigned solvers() override { return m_solvers.size(); }
|
||||
private:
|
||||
static bool solverAnswered(CheckResult result);
|
||||
|
||||
std::vector<std::unique_ptr<smt::SolverInterface>> m_solvers;
|
||||
|
||||
std::vector<smt::Expression> m_assertions;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,325 +0,0 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libsolidity/formal/Sorts.h>
|
||||
|
||||
#include <libsolidity/ast/Types.h>
|
||||
#include <libsolidity/interface/ReadFile.h>
|
||||
#include <liblangutil/Exceptions.h>
|
||||
#include <libsolutil/Common.h>
|
||||
#include <libsolutil/Exceptions.h>
|
||||
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <cstdio>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace solidity::frontend::smt
|
||||
{
|
||||
|
||||
struct SMTSolverChoice
|
||||
{
|
||||
bool cvc4 = false;
|
||||
bool z3 = false;
|
||||
|
||||
static constexpr SMTSolverChoice All() { return {true, true}; }
|
||||
static constexpr SMTSolverChoice CVC4() { return {true, false}; }
|
||||
static constexpr SMTSolverChoice Z3() { return {false, true}; }
|
||||
static constexpr SMTSolverChoice None() { return {false, false}; }
|
||||
|
||||
bool none() { return !some(); }
|
||||
bool some() { return cvc4 || z3; }
|
||||
bool all() { return cvc4 && z3; }
|
||||
};
|
||||
|
||||
enum class CheckResult
|
||||
{
|
||||
SATISFIABLE, UNSATISFIABLE, UNKNOWN, CONFLICTING, ERROR
|
||||
};
|
||||
|
||||
// Forward declaration.
|
||||
SortPointer smtSort(Type const& _type);
|
||||
|
||||
/// C++ representation of an SMTLIB2 expression.
|
||||
class Expression
|
||||
{
|
||||
friend class SolverInterface;
|
||||
public:
|
||||
explicit Expression(bool _v): Expression(_v ? "true" : "false", Kind::Bool) {}
|
||||
explicit Expression(frontend::TypePointer _type): Expression(_type->toString(true), {}, std::make_shared<SortSort>(smtSort(*_type))) {}
|
||||
explicit Expression(std::shared_ptr<SortSort> _sort): Expression("", {}, _sort) {}
|
||||
Expression(size_t _number): Expression(std::to_string(_number), Kind::Int) {}
|
||||
Expression(u256 const& _number): Expression(_number.str(), Kind::Int) {}
|
||||
Expression(s256 const& _number): Expression(_number.str(), Kind::Int) {}
|
||||
Expression(bigint const& _number): Expression(_number.str(), Kind::Int) {}
|
||||
|
||||
Expression(Expression const&) = default;
|
||||
Expression(Expression&&) = default;
|
||||
Expression& operator=(Expression const&) = default;
|
||||
Expression& operator=(Expression&&) = default;
|
||||
|
||||
bool hasCorrectArity() const
|
||||
{
|
||||
if (name == "tuple_constructor")
|
||||
{
|
||||
auto tupleSort = std::dynamic_pointer_cast<TupleSort>(sort);
|
||||
solAssert(tupleSort, "");
|
||||
return arguments.size() == tupleSort->components.size();
|
||||
}
|
||||
|
||||
static std::map<std::string, unsigned> const operatorsArity{
|
||||
{"ite", 3},
|
||||
{"not", 1},
|
||||
{"and", 2},
|
||||
{"or", 2},
|
||||
{"implies", 2},
|
||||
{"=", 2},
|
||||
{"<", 2},
|
||||
{"<=", 2},
|
||||
{">", 2},
|
||||
{">=", 2},
|
||||
{"+", 2},
|
||||
{"-", 2},
|
||||
{"*", 2},
|
||||
{"/", 2},
|
||||
{"mod", 2},
|
||||
{"select", 2},
|
||||
{"store", 3},
|
||||
{"const_array", 2},
|
||||
{"tuple_get", 2}
|
||||
};
|
||||
return operatorsArity.count(name) && operatorsArity.at(name) == arguments.size();
|
||||
}
|
||||
|
||||
static Expression ite(Expression _condition, Expression _trueValue, Expression _falseValue)
|
||||
{
|
||||
solAssert(*_trueValue.sort == *_falseValue.sort, "");
|
||||
SortPointer sort = _trueValue.sort;
|
||||
return Expression("ite", std::vector<Expression>{
|
||||
std::move(_condition), std::move(_trueValue), std::move(_falseValue)
|
||||
}, std::move(sort));
|
||||
}
|
||||
|
||||
static Expression implies(Expression _a, Expression _b)
|
||||
{
|
||||
return Expression(
|
||||
"implies",
|
||||
std::move(_a),
|
||||
std::move(_b),
|
||||
Kind::Bool
|
||||
);
|
||||
}
|
||||
|
||||
/// select is the SMT representation of an array index access.
|
||||
static Expression select(Expression _array, Expression _index)
|
||||
{
|
||||
solAssert(_array.sort->kind == Kind::Array, "");
|
||||
std::shared_ptr<ArraySort> arraySort = std::dynamic_pointer_cast<ArraySort>(_array.sort);
|
||||
solAssert(arraySort, "");
|
||||
solAssert(_index.sort, "");
|
||||
solAssert(*arraySort->domain == *_index.sort, "");
|
||||
return Expression(
|
||||
"select",
|
||||
std::vector<Expression>{std::move(_array), std::move(_index)},
|
||||
arraySort->range
|
||||
);
|
||||
}
|
||||
|
||||
/// store is the SMT representation of an assignment to array index.
|
||||
/// The function is pure and returns the modified array.
|
||||
static Expression store(Expression _array, Expression _index, Expression _element)
|
||||
{
|
||||
auto arraySort = std::dynamic_pointer_cast<ArraySort>(_array.sort);
|
||||
solAssert(arraySort, "");
|
||||
solAssert(_index.sort, "");
|
||||
solAssert(_element.sort, "");
|
||||
solAssert(*arraySort->domain == *_index.sort, "");
|
||||
solAssert(*arraySort->range == *_element.sort, "");
|
||||
return Expression(
|
||||
"store",
|
||||
std::vector<Expression>{std::move(_array), std::move(_index), std::move(_element)},
|
||||
arraySort
|
||||
);
|
||||
}
|
||||
|
||||
static Expression const_array(Expression _sort, Expression _value)
|
||||
{
|
||||
solAssert(_sort.sort->kind == Kind::Sort, "");
|
||||
auto sortSort = std::dynamic_pointer_cast<SortSort>(_sort.sort);
|
||||
auto arraySort = std::dynamic_pointer_cast<ArraySort>(sortSort->inner);
|
||||
solAssert(sortSort && arraySort, "");
|
||||
solAssert(_value.sort, "");
|
||||
solAssert(*arraySort->range == *_value.sort, "");
|
||||
return Expression(
|
||||
"const_array",
|
||||
std::vector<Expression>{std::move(_sort), std::move(_value)},
|
||||
arraySort
|
||||
);
|
||||
}
|
||||
|
||||
static Expression tuple_get(Expression _tuple, size_t _index)
|
||||
{
|
||||
solAssert(_tuple.sort->kind == Kind::Tuple, "");
|
||||
std::shared_ptr<TupleSort> tupleSort = std::dynamic_pointer_cast<TupleSort>(_tuple.sort);
|
||||
solAssert(tupleSort, "");
|
||||
solAssert(_index < tupleSort->components.size(), "");
|
||||
return Expression(
|
||||
"tuple_get",
|
||||
std::vector<Expression>{std::move(_tuple), Expression(_index)},
|
||||
tupleSort->components.at(_index)
|
||||
);
|
||||
}
|
||||
|
||||
static Expression tuple_constructor(Expression _tuple, std::vector<Expression> _arguments)
|
||||
{
|
||||
solAssert(_tuple.sort->kind == Kind::Sort, "");
|
||||
auto sortSort = std::dynamic_pointer_cast<SortSort>(_tuple.sort);
|
||||
auto tupleSort = std::dynamic_pointer_cast<TupleSort>(sortSort->inner);
|
||||
solAssert(tupleSort, "");
|
||||
solAssert(_arguments.size() == tupleSort->components.size(), "");
|
||||
return Expression(
|
||||
"tuple_constructor",
|
||||
std::move(_arguments),
|
||||
tupleSort
|
||||
);
|
||||
}
|
||||
|
||||
friend Expression operator!(Expression _a)
|
||||
{
|
||||
return Expression("not", std::move(_a), Kind::Bool);
|
||||
}
|
||||
friend Expression operator&&(Expression _a, Expression _b)
|
||||
{
|
||||
return Expression("and", std::move(_a), std::move(_b), Kind::Bool);
|
||||
}
|
||||
friend Expression operator||(Expression _a, Expression _b)
|
||||
{
|
||||
return Expression("or", std::move(_a), std::move(_b), Kind::Bool);
|
||||
}
|
||||
friend Expression operator==(Expression _a, Expression _b)
|
||||
{
|
||||
return Expression("=", std::move(_a), std::move(_b), Kind::Bool);
|
||||
}
|
||||
friend Expression operator!=(Expression _a, Expression _b)
|
||||
{
|
||||
return !(std::move(_a) == std::move(_b));
|
||||
}
|
||||
friend Expression operator<(Expression _a, Expression _b)
|
||||
{
|
||||
return Expression("<", std::move(_a), std::move(_b), Kind::Bool);
|
||||
}
|
||||
friend Expression operator<=(Expression _a, Expression _b)
|
||||
{
|
||||
return Expression("<=", std::move(_a), std::move(_b), Kind::Bool);
|
||||
}
|
||||
friend Expression operator>(Expression _a, Expression _b)
|
||||
{
|
||||
return Expression(">", std::move(_a), std::move(_b), Kind::Bool);
|
||||
}
|
||||
friend Expression operator>=(Expression _a, Expression _b)
|
||||
{
|
||||
return Expression(">=", std::move(_a), std::move(_b), Kind::Bool);
|
||||
}
|
||||
friend Expression operator+(Expression _a, Expression _b)
|
||||
{
|
||||
return Expression("+", std::move(_a), std::move(_b), Kind::Int);
|
||||
}
|
||||
friend Expression operator-(Expression _a, Expression _b)
|
||||
{
|
||||
return Expression("-", std::move(_a), std::move(_b), Kind::Int);
|
||||
}
|
||||
friend Expression operator*(Expression _a, Expression _b)
|
||||
{
|
||||
return Expression("*", std::move(_a), std::move(_b), Kind::Int);
|
||||
}
|
||||
friend Expression operator/(Expression _a, Expression _b)
|
||||
{
|
||||
return Expression("/", std::move(_a), std::move(_b), Kind::Int);
|
||||
}
|
||||
friend Expression operator%(Expression _a, Expression _b)
|
||||
{
|
||||
return Expression("mod", std::move(_a), std::move(_b), Kind::Int);
|
||||
}
|
||||
Expression operator()(std::vector<Expression> _arguments) const
|
||||
{
|
||||
solAssert(
|
||||
sort->kind == Kind::Function,
|
||||
"Attempted function application to non-function."
|
||||
);
|
||||
auto fSort = dynamic_cast<FunctionSort const*>(sort.get());
|
||||
solAssert(fSort, "");
|
||||
return Expression(name, std::move(_arguments), fSort->codomain);
|
||||
}
|
||||
|
||||
std::string name;
|
||||
std::vector<Expression> arguments;
|
||||
SortPointer sort;
|
||||
|
||||
private:
|
||||
/// Manual constructors, should only be used by SolverInterface and this class itself.
|
||||
Expression(std::string _name, std::vector<Expression> _arguments, SortPointer _sort):
|
||||
name(std::move(_name)), arguments(std::move(_arguments)), sort(std::move(_sort)) {}
|
||||
Expression(std::string _name, std::vector<Expression> _arguments, Kind _kind):
|
||||
Expression(std::move(_name), std::move(_arguments), std::make_shared<Sort>(_kind)) {}
|
||||
|
||||
explicit Expression(std::string _name, Kind _kind):
|
||||
Expression(std::move(_name), std::vector<Expression>{}, _kind) {}
|
||||
Expression(std::string _name, Expression _arg, Kind _kind):
|
||||
Expression(std::move(_name), std::vector<Expression>{std::move(_arg)}, _kind) {}
|
||||
Expression(std::string _name, Expression _arg1, Expression _arg2, Kind _kind):
|
||||
Expression(std::move(_name), std::vector<Expression>{std::move(_arg1), std::move(_arg2)}, _kind) {}
|
||||
};
|
||||
|
||||
DEV_SIMPLE_EXCEPTION(SolverError);
|
||||
|
||||
class SolverInterface
|
||||
{
|
||||
public:
|
||||
virtual ~SolverInterface() = default;
|
||||
virtual void reset() = 0;
|
||||
|
||||
virtual void push() = 0;
|
||||
virtual void pop() = 0;
|
||||
|
||||
virtual void declareVariable(std::string const& _name, SortPointer const& _sort) = 0;
|
||||
Expression newVariable(std::string _name, SortPointer const& _sort)
|
||||
{
|
||||
// Subclasses should do something here
|
||||
solAssert(_sort, "");
|
||||
declareVariable(_name, _sort);
|
||||
return Expression(std::move(_name), {}, _sort);
|
||||
}
|
||||
|
||||
virtual void addAssertion(Expression const& _expr) = 0;
|
||||
|
||||
/// Checks for satisfiability, evaluates the expressions if a model
|
||||
/// is available. Throws SMTSolverError on error.
|
||||
virtual std::pair<CheckResult, std::vector<std::string>>
|
||||
check(std::vector<Expression> const& _expressionsToEvaluate) = 0;
|
||||
|
||||
/// @returns a list of queries that the system was not able to respond to.
|
||||
virtual std::vector<std::string> unhandledQueries() { return {}; }
|
||||
|
||||
/// @returns how many SMT solvers this interface has.
|
||||
virtual unsigned solvers() { return 1; }
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include <libsolidity/formal/Sorts.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace solidity::frontend::smt
|
||||
{
|
||||
|
||||
shared_ptr<Sort> const SortProvider::boolSort{make_shared<Sort>(Kind::Bool)};
|
||||
shared_ptr<Sort> const SortProvider::intSort{make_shared<Sort>(Kind::Int)};
|
||||
|
||||
}
|
||||
@@ -1,166 +0,0 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <liblangutil/Exceptions.h>
|
||||
#include <libsolutil/Common.h>
|
||||
#include <libsolutil/Exceptions.h>
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace solidity::frontend::smt
|
||||
{
|
||||
|
||||
enum class Kind
|
||||
{
|
||||
Int,
|
||||
Bool,
|
||||
Function,
|
||||
Array,
|
||||
Sort,
|
||||
Tuple
|
||||
};
|
||||
|
||||
struct Sort
|
||||
{
|
||||
Sort(Kind _kind):
|
||||
kind(_kind) {}
|
||||
virtual ~Sort() = default;
|
||||
virtual bool operator==(Sort const& _other) const { return kind == _other.kind; }
|
||||
|
||||
Kind const kind;
|
||||
};
|
||||
using SortPointer = std::shared_ptr<Sort>;
|
||||
|
||||
struct FunctionSort: public Sort
|
||||
{
|
||||
FunctionSort(std::vector<SortPointer> _domain, SortPointer _codomain):
|
||||
Sort(Kind::Function), domain(std::move(_domain)), codomain(std::move(_codomain)) {}
|
||||
bool operator==(Sort const& _other) const override
|
||||
{
|
||||
if (!Sort::operator==(_other))
|
||||
return false;
|
||||
auto _otherFunction = dynamic_cast<FunctionSort const*>(&_other);
|
||||
solAssert(_otherFunction, "");
|
||||
if (domain.size() != _otherFunction->domain.size())
|
||||
return false;
|
||||
if (!std::equal(
|
||||
domain.begin(),
|
||||
domain.end(),
|
||||
_otherFunction->domain.begin(),
|
||||
[&](SortPointer _a, SortPointer _b) { return *_a == *_b; }
|
||||
))
|
||||
return false;
|
||||
solAssert(codomain, "");
|
||||
solAssert(_otherFunction->codomain, "");
|
||||
return *codomain == *_otherFunction->codomain;
|
||||
}
|
||||
|
||||
std::vector<SortPointer> domain;
|
||||
SortPointer codomain;
|
||||
};
|
||||
|
||||
struct ArraySort: public Sort
|
||||
{
|
||||
/// _domain is the sort of the indices
|
||||
/// _range is the sort of the values
|
||||
ArraySort(SortPointer _domain, SortPointer _range):
|
||||
Sort(Kind::Array), domain(std::move(_domain)), range(std::move(_range)) {}
|
||||
bool operator==(Sort const& _other) const override
|
||||
{
|
||||
if (!Sort::operator==(_other))
|
||||
return false;
|
||||
auto _otherArray = dynamic_cast<ArraySort const*>(&_other);
|
||||
solAssert(_otherArray, "");
|
||||
solAssert(_otherArray->domain, "");
|
||||
solAssert(_otherArray->range, "");
|
||||
solAssert(domain, "");
|
||||
solAssert(range, "");
|
||||
return *domain == *_otherArray->domain && *range == *_otherArray->range;
|
||||
}
|
||||
|
||||
SortPointer domain;
|
||||
SortPointer range;
|
||||
};
|
||||
|
||||
struct SortSort: public Sort
|
||||
{
|
||||
SortSort(SortPointer _inner): Sort(Kind::Sort), inner(std::move(_inner)) {}
|
||||
bool operator==(Sort const& _other) const override
|
||||
{
|
||||
if (!Sort::operator==(_other))
|
||||
return false;
|
||||
auto _otherSort = dynamic_cast<SortSort const*>(&_other);
|
||||
solAssert(_otherSort, "");
|
||||
solAssert(_otherSort->inner, "");
|
||||
solAssert(inner, "");
|
||||
return *inner == *_otherSort->inner;
|
||||
}
|
||||
|
||||
SortPointer inner;
|
||||
};
|
||||
|
||||
struct TupleSort: public Sort
|
||||
{
|
||||
TupleSort(
|
||||
std::string _name,
|
||||
std::vector<std::string> _members,
|
||||
std::vector<SortPointer> _components
|
||||
):
|
||||
Sort(Kind::Tuple),
|
||||
name(std::move(_name)),
|
||||
members(std::move(_members)),
|
||||
components(std::move(_components))
|
||||
{}
|
||||
|
||||
bool operator==(Sort const& _other) const override
|
||||
{
|
||||
if (!Sort::operator==(_other))
|
||||
return false;
|
||||
auto _otherTuple = dynamic_cast<TupleSort const*>(&_other);
|
||||
solAssert(_otherTuple, "");
|
||||
if (name != _otherTuple->name)
|
||||
return false;
|
||||
if (members != _otherTuple->members)
|
||||
return false;
|
||||
if (components.size() != _otherTuple->components.size())
|
||||
return false;
|
||||
if (!std::equal(
|
||||
components.begin(),
|
||||
components.end(),
|
||||
_otherTuple->components.begin(),
|
||||
[&](SortPointer _a, SortPointer _b) { return *_a == *_b; }
|
||||
))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string const name;
|
||||
std::vector<std::string> const members;
|
||||
std::vector<SortPointer> const components;
|
||||
};
|
||||
|
||||
/** Frequently used sorts.*/
|
||||
struct SortProvider
|
||||
{
|
||||
static std::shared_ptr<Sort> const boolSort;
|
||||
static std::shared_ptr<Sort> const intSort;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -17,10 +17,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libsolidity/formal/Sorts.h>
|
||||
#include <libsolidity/formal/SolverInterface.h>
|
||||
#include <libsolidity/formal/SymbolicVariables.h>
|
||||
|
||||
#include <libsmtutil/Sorts.h>
|
||||
#include <libsmtutil/SolverInterface.h>
|
||||
|
||||
namespace solidity::frontend::smt
|
||||
{
|
||||
|
||||
|
||||
@@ -17,10 +17,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libsolidity/formal/SolverInterface.h>
|
||||
#include <libsolidity/formal/SSAVariable.h>
|
||||
#include <libsolidity/ast/Types.h>
|
||||
#include <libsolidity/ast/TypeProvider.h>
|
||||
|
||||
#include <libsmtutil/SolverInterface.h>
|
||||
#include <memory>
|
||||
|
||||
namespace solidity::frontend::smt
|
||||
|
||||
@@ -1,111 +0,0 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <libsolidity/formal/Z3CHCInterface.h>
|
||||
|
||||
#include <liblangutil/Exceptions.h>
|
||||
#include <libsolutil/CommonIO.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity;
|
||||
using namespace solidity::frontend::smt;
|
||||
|
||||
Z3CHCInterface::Z3CHCInterface():
|
||||
m_z3Interface(make_unique<Z3Interface>()),
|
||||
m_context(m_z3Interface->context()),
|
||||
m_solver(*m_context)
|
||||
{
|
||||
// These need to be set globally.
|
||||
z3::set_param("rewriter.pull_cheap_ite", true);
|
||||
z3::set_param("rlimit", Z3Interface::resourceLimit);
|
||||
|
||||
// Spacer options.
|
||||
// These needs to be set in the solver.
|
||||
// https://github.com/Z3Prover/z3/blob/master/src/muz/base/fp_params.pyg
|
||||
z3::params p(*m_context);
|
||||
// These are useful for solving problems with arrays and loops.
|
||||
// Use quantified lemma generalizer.
|
||||
p.set("fp.spacer.q3.use_qgen", true);
|
||||
p.set("fp.spacer.mbqi", false);
|
||||
// Ground pobs by using values from a model.
|
||||
p.set("fp.spacer.ground_pobs", false);
|
||||
m_solver.set(p);
|
||||
}
|
||||
|
||||
void Z3CHCInterface::declareVariable(string const& _name, SortPointer const& _sort)
|
||||
{
|
||||
solAssert(_sort, "");
|
||||
m_z3Interface->declareVariable(_name, _sort);
|
||||
}
|
||||
|
||||
void Z3CHCInterface::registerRelation(Expression const& _expr)
|
||||
{
|
||||
m_solver.register_relation(m_z3Interface->functions().at(_expr.name));
|
||||
}
|
||||
|
||||
void Z3CHCInterface::addRule(Expression const& _expr, string const& _name)
|
||||
{
|
||||
z3::expr rule = m_z3Interface->toZ3Expr(_expr);
|
||||
if (m_z3Interface->constants().empty())
|
||||
m_solver.add_rule(rule, m_context->str_symbol(_name.c_str()));
|
||||
else
|
||||
{
|
||||
z3::expr_vector variables(*m_context);
|
||||
for (auto const& var: m_z3Interface->constants())
|
||||
variables.push_back(var.second);
|
||||
z3::expr boundRule = z3::forall(variables, rule);
|
||||
m_solver.add_rule(boundRule, m_context->str_symbol(_name.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
pair<CheckResult, vector<string>> Z3CHCInterface::query(Expression const& _expr)
|
||||
{
|
||||
CheckResult result;
|
||||
vector<string> values;
|
||||
try
|
||||
{
|
||||
z3::expr z3Expr = m_z3Interface->toZ3Expr(_expr);
|
||||
switch (m_solver.query(z3Expr))
|
||||
{
|
||||
case z3::check_result::sat:
|
||||
{
|
||||
result = CheckResult::SATISFIABLE;
|
||||
// TODO retrieve model.
|
||||
break;
|
||||
}
|
||||
case z3::check_result::unsat:
|
||||
{
|
||||
result = CheckResult::UNSATISFIABLE;
|
||||
// TODO retrieve invariants.
|
||||
break;
|
||||
}
|
||||
case z3::check_result::unknown:
|
||||
{
|
||||
result = CheckResult::UNKNOWN;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// TODO retrieve model / invariants
|
||||
}
|
||||
catch (z3::exception const&)
|
||||
{
|
||||
result = CheckResult::ERROR;
|
||||
values.clear();
|
||||
}
|
||||
|
||||
return make_pair(result, values);
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Z3 specific Horn solver interface.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libsolidity/formal/CHCSolverInterface.h>
|
||||
#include <libsolidity/formal/Z3Interface.h>
|
||||
|
||||
namespace solidity::frontend::smt
|
||||
{
|
||||
|
||||
class Z3CHCInterface: public CHCSolverInterface
|
||||
{
|
||||
public:
|
||||
Z3CHCInterface();
|
||||
|
||||
/// Forwards variable declaration to Z3Interface.
|
||||
void declareVariable(std::string const& _name, SortPointer const& _sort) override;
|
||||
|
||||
void registerRelation(Expression const& _expr) override;
|
||||
|
||||
void addRule(Expression const& _expr, std::string const& _name) override;
|
||||
|
||||
std::pair<CheckResult, std::vector<std::string>> query(Expression const& _expr) override;
|
||||
|
||||
Z3Interface* z3Interface() const { return m_z3Interface.get(); }
|
||||
|
||||
private:
|
||||
// Used to handle variables.
|
||||
std::unique_ptr<Z3Interface> m_z3Interface;
|
||||
|
||||
z3::context* m_context;
|
||||
// Horn solver.
|
||||
z3::fixedpoint m_solver;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,270 +0,0 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <libsolidity/formal/Z3Interface.h>
|
||||
|
||||
#include <liblangutil/Exceptions.h>
|
||||
#include <libsolutil/CommonIO.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity::frontend::smt;
|
||||
|
||||
Z3Interface::Z3Interface():
|
||||
m_solver(m_context)
|
||||
{
|
||||
// These need to be set globally.
|
||||
z3::set_param("rewriter.pull_cheap_ite", true);
|
||||
z3::set_param("rlimit", resourceLimit);
|
||||
}
|
||||
|
||||
void Z3Interface::reset()
|
||||
{
|
||||
m_constants.clear();
|
||||
m_functions.clear();
|
||||
m_solver.reset();
|
||||
}
|
||||
|
||||
void Z3Interface::push()
|
||||
{
|
||||
m_solver.push();
|
||||
}
|
||||
|
||||
void Z3Interface::pop()
|
||||
{
|
||||
m_solver.pop();
|
||||
}
|
||||
|
||||
void Z3Interface::declareVariable(string const& _name, SortPointer const& _sort)
|
||||
{
|
||||
solAssert(_sort, "");
|
||||
if (_sort->kind == Kind::Function)
|
||||
declareFunction(_name, *_sort);
|
||||
else if (m_constants.count(_name))
|
||||
m_constants.at(_name) = m_context.constant(_name.c_str(), z3Sort(*_sort));
|
||||
else
|
||||
m_constants.emplace(_name, m_context.constant(_name.c_str(), z3Sort(*_sort)));
|
||||
}
|
||||
|
||||
void Z3Interface::declareFunction(string const& _name, Sort const& _sort)
|
||||
{
|
||||
solAssert(_sort.kind == smt::Kind::Function, "");
|
||||
FunctionSort fSort = dynamic_cast<FunctionSort const&>(_sort);
|
||||
if (m_functions.count(_name))
|
||||
m_functions.at(_name) = m_context.function(_name.c_str(), z3Sort(fSort.domain), z3Sort(*fSort.codomain));
|
||||
else
|
||||
m_functions.emplace(_name, m_context.function(_name.c_str(), z3Sort(fSort.domain), z3Sort(*fSort.codomain)));
|
||||
}
|
||||
|
||||
void Z3Interface::addAssertion(Expression const& _expr)
|
||||
{
|
||||
m_solver.add(toZ3Expr(_expr));
|
||||
}
|
||||
|
||||
pair<CheckResult, vector<string>> Z3Interface::check(vector<Expression> const& _expressionsToEvaluate)
|
||||
{
|
||||
CheckResult result;
|
||||
vector<string> values;
|
||||
try
|
||||
{
|
||||
switch (m_solver.check())
|
||||
{
|
||||
case z3::check_result::sat:
|
||||
result = CheckResult::SATISFIABLE;
|
||||
break;
|
||||
case z3::check_result::unsat:
|
||||
result = CheckResult::UNSATISFIABLE;
|
||||
break;
|
||||
case z3::check_result::unknown:
|
||||
result = CheckResult::UNKNOWN;
|
||||
break;
|
||||
}
|
||||
|
||||
if (result == CheckResult::SATISFIABLE && !_expressionsToEvaluate.empty())
|
||||
{
|
||||
z3::model m = m_solver.get_model();
|
||||
for (Expression const& e: _expressionsToEvaluate)
|
||||
values.push_back(util::toString(m.eval(toZ3Expr(e))));
|
||||
}
|
||||
}
|
||||
catch (z3::exception const&)
|
||||
{
|
||||
result = CheckResult::ERROR;
|
||||
values.clear();
|
||||
}
|
||||
|
||||
return make_pair(result, values);
|
||||
}
|
||||
|
||||
z3::expr Z3Interface::toZ3Expr(Expression const& _expr)
|
||||
{
|
||||
if (_expr.arguments.empty() && m_constants.count(_expr.name))
|
||||
return m_constants.at(_expr.name);
|
||||
z3::expr_vector arguments(m_context);
|
||||
for (auto const& arg: _expr.arguments)
|
||||
arguments.push_back(toZ3Expr(arg));
|
||||
|
||||
try
|
||||
{
|
||||
string const& n = _expr.name;
|
||||
if (m_functions.count(n))
|
||||
return m_functions.at(n)(arguments);
|
||||
else if (m_constants.count(n))
|
||||
{
|
||||
solAssert(arguments.empty(), "");
|
||||
return m_constants.at(n);
|
||||
}
|
||||
else if (arguments.empty())
|
||||
{
|
||||
if (n == "true")
|
||||
return m_context.bool_val(true);
|
||||
else if (n == "false")
|
||||
return m_context.bool_val(false);
|
||||
else if (_expr.sort->kind == Kind::Sort)
|
||||
{
|
||||
auto sortSort = dynamic_pointer_cast<SortSort>(_expr.sort);
|
||||
solAssert(sortSort, "");
|
||||
return m_context.constant(n.c_str(), z3Sort(*sortSort->inner));
|
||||
}
|
||||
else
|
||||
try
|
||||
{
|
||||
return m_context.int_val(n.c_str());
|
||||
}
|
||||
catch (z3::exception const& _e)
|
||||
{
|
||||
solAssert(false, _e.msg());
|
||||
}
|
||||
}
|
||||
|
||||
solAssert(_expr.hasCorrectArity(), "");
|
||||
if (n == "ite")
|
||||
return z3::ite(arguments[0], arguments[1], arguments[2]);
|
||||
else if (n == "not")
|
||||
return !arguments[0];
|
||||
else if (n == "and")
|
||||
return arguments[0] && arguments[1];
|
||||
else if (n == "or")
|
||||
return arguments[0] || arguments[1];
|
||||
else if (n == "implies")
|
||||
return z3::implies(arguments[0], arguments[1]);
|
||||
else if (n == "=")
|
||||
return arguments[0] == arguments[1];
|
||||
else if (n == "<")
|
||||
return arguments[0] < arguments[1];
|
||||
else if (n == "<=")
|
||||
return arguments[0] <= arguments[1];
|
||||
else if (n == ">")
|
||||
return arguments[0] > arguments[1];
|
||||
else if (n == ">=")
|
||||
return arguments[0] >= arguments[1];
|
||||
else if (n == "+")
|
||||
return arguments[0] + arguments[1];
|
||||
else if (n == "-")
|
||||
return arguments[0] - arguments[1];
|
||||
else if (n == "*")
|
||||
return arguments[0] * arguments[1];
|
||||
else if (n == "/")
|
||||
return arguments[0] / arguments[1];
|
||||
else if (n == "mod")
|
||||
return z3::mod(arguments[0], arguments[1]);
|
||||
else if (n == "select")
|
||||
return z3::select(arguments[0], arguments[1]);
|
||||
else if (n == "store")
|
||||
return z3::store(arguments[0], arguments[1], arguments[2]);
|
||||
else if (n == "const_array")
|
||||
{
|
||||
shared_ptr<SortSort> sortSort = std::dynamic_pointer_cast<SortSort>(_expr.arguments[0].sort);
|
||||
solAssert(sortSort, "");
|
||||
auto arraySort = dynamic_pointer_cast<ArraySort>(sortSort->inner);
|
||||
solAssert(arraySort && arraySort->domain, "");
|
||||
return z3::const_array(z3Sort(*arraySort->domain), arguments[1]);
|
||||
}
|
||||
else if (n == "tuple_get")
|
||||
{
|
||||
size_t index = std::stoi(_expr.arguments[1].name);
|
||||
return z3::func_decl(m_context, Z3_get_tuple_sort_field_decl(m_context, z3Sort(*_expr.arguments[0].sort), index))(arguments[0]);
|
||||
}
|
||||
else if (n == "tuple_constructor")
|
||||
{
|
||||
auto constructor = z3::func_decl(m_context, Z3_get_tuple_sort_mk_decl(m_context, z3Sort(*_expr.sort)));
|
||||
solAssert(constructor.arity() == arguments.size(), "");
|
||||
z3::expr_vector args(m_context);
|
||||
for (auto const& arg: arguments)
|
||||
args.push_back(arg);
|
||||
return constructor(args);
|
||||
}
|
||||
|
||||
solAssert(false, "");
|
||||
}
|
||||
catch (z3::exception const& _e)
|
||||
{
|
||||
solAssert(false, _e.msg());
|
||||
}
|
||||
|
||||
solAssert(false, "");
|
||||
}
|
||||
|
||||
z3::sort Z3Interface::z3Sort(Sort const& _sort)
|
||||
{
|
||||
switch (_sort.kind)
|
||||
{
|
||||
case Kind::Bool:
|
||||
return m_context.bool_sort();
|
||||
case Kind::Int:
|
||||
return m_context.int_sort();
|
||||
case Kind::Array:
|
||||
{
|
||||
auto const& arraySort = dynamic_cast<ArraySort const&>(_sort);
|
||||
return m_context.array_sort(z3Sort(*arraySort.domain), z3Sort(*arraySort.range));
|
||||
}
|
||||
case Kind::Tuple:
|
||||
{
|
||||
auto const& tupleSort = dynamic_cast<TupleSort const&>(_sort);
|
||||
vector<char const*> cMembers;
|
||||
for (auto const& member: tupleSort.members)
|
||||
cMembers.emplace_back(member.c_str());
|
||||
/// Using this instead of the function below because with that one
|
||||
/// we can't use `&sorts[0]` here.
|
||||
vector<z3::sort> sorts;
|
||||
for (auto const& sort: tupleSort.components)
|
||||
sorts.push_back(z3Sort(*sort));
|
||||
z3::func_decl_vector projs(m_context);
|
||||
z3::func_decl tupleConstructor = m_context.tuple_sort(
|
||||
tupleSort.name.c_str(),
|
||||
tupleSort.members.size(),
|
||||
cMembers.data(),
|
||||
sorts.data(),
|
||||
projs
|
||||
);
|
||||
return tupleConstructor.range();
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
solAssert(false, "");
|
||||
// Cannot be reached.
|
||||
return m_context.int_sort();
|
||||
}
|
||||
|
||||
z3::sort_vector Z3Interface::z3Sort(vector<SortPointer> const& _sorts)
|
||||
{
|
||||
z3::sort_vector z3Sorts(m_context);
|
||||
for (auto const& _sort: _sorts)
|
||||
z3Sorts.push_back(z3Sort(*_sort));
|
||||
return z3Sorts;
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libsolidity/formal/SolverInterface.h>
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <z3++.h>
|
||||
|
||||
namespace solidity::frontend::smt
|
||||
{
|
||||
|
||||
class Z3Interface: public SolverInterface, public boost::noncopyable
|
||||
{
|
||||
public:
|
||||
Z3Interface();
|
||||
|
||||
void reset() override;
|
||||
|
||||
void push() override;
|
||||
void pop() override;
|
||||
|
||||
void declareVariable(std::string const& _name, SortPointer const& _sort) override;
|
||||
|
||||
void addAssertion(Expression const& _expr) override;
|
||||
std::pair<CheckResult, std::vector<std::string>> check(std::vector<Expression> const& _expressionsToEvaluate) override;
|
||||
|
||||
z3::expr toZ3Expr(Expression const& _expr);
|
||||
|
||||
std::map<std::string, z3::expr> constants() const { return m_constants; }
|
||||
std::map<std::string, z3::func_decl> functions() const { return m_functions; }
|
||||
|
||||
z3::context* context() { return &m_context; }
|
||||
|
||||
// Z3 "basic resources" limit.
|
||||
// This is used to make the runs more deterministic and platform/machine independent.
|
||||
// The tests start failing for Z3 with less than 20000000,
|
||||
// so using double that.
|
||||
static int const resourceLimit = 40000000;
|
||||
|
||||
private:
|
||||
void declareFunction(std::string const& _name, Sort const& _sort);
|
||||
|
||||
z3::sort z3Sort(smt::Sort const& _sort);
|
||||
z3::sort_vector z3Sort(std::vector<smt::SortPointer> const& _sorts);
|
||||
|
||||
z3::context m_context;
|
||||
z3::solver m_solver;
|
||||
|
||||
std::map<std::string, z3::expr> m_constants;
|
||||
std::map<std::string, z3::func_decl> m_functions;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -27,7 +27,8 @@
|
||||
#include <libsolidity/interface/OptimiserSettings.h>
|
||||
#include <libsolidity/interface/Version.h>
|
||||
#include <libsolidity/interface/DebugSettings.h>
|
||||
#include <libsolidity/formal/SolverInterface.h>
|
||||
|
||||
#include <libsmtutil/SolverInterface.h>
|
||||
|
||||
#include <liblangutil/ErrorReporter.h>
|
||||
#include <liblangutil/EVMVersion.h>
|
||||
|
||||
Reference in New Issue
Block a user