diff options
Diffstat (limited to 'libsolidity/formal')
-rw-r--r-- | libsolidity/formal/CVC4Interface.cpp | 73 | ||||
-rw-r--r-- | libsolidity/formal/CVC4Interface.h | 10 | ||||
-rw-r--r-- | libsolidity/formal/SMTChecker.cpp | 80 | ||||
-rw-r--r-- | libsolidity/formal/SMTChecker.h | 74 | ||||
-rw-r--r-- | libsolidity/formal/SMTLib2Interface.cpp | 104 | ||||
-rw-r--r-- | libsolidity/formal/SMTLib2Interface.h | 23 | ||||
-rw-r--r-- | libsolidity/formal/SMTPortfolio.cpp | 25 | ||||
-rw-r--r-- | libsolidity/formal/SMTPortfolio.h | 10 | ||||
-rw-r--r-- | libsolidity/formal/SolverInterface.h | 214 | ||||
-rw-r--r-- | libsolidity/formal/SymbolicTypes.cpp | 73 | ||||
-rw-r--r-- | libsolidity/formal/SymbolicTypes.h | 16 | ||||
-rw-r--r-- | libsolidity/formal/SymbolicVariables.cpp | 31 | ||||
-rw-r--r-- | libsolidity/formal/SymbolicVariables.h | 23 | ||||
-rw-r--r-- | libsolidity/formal/Z3Interface.cpp | 51 | ||||
-rw-r--r-- | libsolidity/formal/Z3Interface.h | 9 |
15 files changed, 523 insertions, 293 deletions
diff --git a/libsolidity/formal/CVC4Interface.cpp b/libsolidity/formal/CVC4Interface.cpp index 6cb91483..de5e4430 100644 --- a/libsolidity/formal/CVC4Interface.cpp +++ b/libsolidity/formal/CVC4Interface.cpp @@ -17,7 +17,7 @@ #include <libsolidity/formal/CVC4Interface.h> -#include <libsolidity/interface/Exceptions.h> +#include <liblangutil/Exceptions.h> #include <libdevcore/CommonIO.h> @@ -33,8 +33,7 @@ CVC4Interface::CVC4Interface(): void CVC4Interface::reset() { - m_constants.clear(); - m_functions.clear(); + m_variables.clear(); m_solver.reset(); m_solver.setOption("produce-models", true); m_solver.setTimeLimit(queryTimeout); @@ -50,25 +49,10 @@ void CVC4Interface::pop() m_solver.pop(); } -void CVC4Interface::declareFunction(string _name, Sort _domain, Sort _codomain) +void CVC4Interface::declareVariable(string const& _name, Sort const& _sort) { - if (!m_functions.count(_name)) - { - CVC4::Type fType = m_context.mkFunctionType(cvc4Sort(_domain), cvc4Sort(_codomain)); - m_functions.insert({_name, m_context.mkVar(_name.c_str(), fType)}); - } -} - -void CVC4Interface::declareInteger(string _name) -{ - if (!m_constants.count(_name)) - m_constants.insert({_name, m_context.mkVar(_name.c_str(), m_context.integerType())}); -} - -void CVC4Interface::declareBool(string _name) -{ - if (!m_constants.count(_name)) - m_constants.insert({_name, m_context.mkVar(_name.c_str(), m_context.booleanType())}); + if (!m_variables.count(_name)) + m_variables.insert({_name, m_context.mkVar(_name.c_str(), cvc4Sort(_sort))}); } void CVC4Interface::addAssertion(Expression const& _expr) @@ -129,20 +113,19 @@ pair<CheckResult, vector<string>> CVC4Interface::check(vector<Expression> const& CVC4::Expr CVC4Interface::toCVC4Expr(Expression const& _expr) { - if (_expr.arguments.empty() && m_constants.count(_expr.name)) - return m_constants.at(_expr.name); + // 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)); string const& n = _expr.name; - if (m_functions.count(n)) - return m_context.mkExpr(CVC4::kind::APPLY_UF, m_functions[n], arguments); - else if (m_constants.count(n)) - { - solAssert(arguments.empty(), ""); - return m_constants.at(n); - } + // 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") @@ -181,19 +164,33 @@ CVC4::Expr CVC4Interface::toCVC4Expr(Expression const& _expr) 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 == "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]); // Cannot reach here. solAssert(false, ""); return arguments[0]; } -CVC4::Type CVC4Interface::cvc4Sort(Sort _sort) +CVC4::Type CVC4Interface::cvc4Sort(Sort const& _sort) { - switch (_sort) + switch (_sort.kind) { - case Sort::Bool: + case Kind::Bool: return m_context.booleanType(); - case Sort::Int: + 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)); + } default: break; } @@ -201,3 +198,11 @@ CVC4::Type CVC4Interface::cvc4Sort(Sort _sort) // 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; +} diff --git a/libsolidity/formal/CVC4Interface.h b/libsolidity/formal/CVC4Interface.h index cd6d761d..bbe23855 100644 --- a/libsolidity/formal/CVC4Interface.h +++ b/libsolidity/formal/CVC4Interface.h @@ -51,21 +51,19 @@ public: void push() override; void pop() override; - void declareFunction(std::string _name, Sort _domain, Sort _codomain) override; - void declareInteger(std::string _name) override; - void declareBool(std::string _name) override; + void declareVariable(std::string const&, Sort 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 _sort); + 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_constants; - std::map<std::string, CVC4::Expr> m_functions; + std::map<std::string, CVC4::Expr> m_variables; }; } diff --git a/libsolidity/formal/SMTChecker.cpp b/libsolidity/formal/SMTChecker.cpp index cc580021..ebb09f0a 100644 --- a/libsolidity/formal/SMTChecker.cpp +++ b/libsolidity/formal/SMTChecker.cpp @@ -22,19 +22,29 @@ #include <libsolidity/formal/VariableUsage.h> #include <libsolidity/formal/SymbolicTypes.h> -#include <libsolidity/interface/ErrorReporter.h> +#include <liblangutil/ErrorReporter.h> #include <boost/range/adaptor/map.hpp> #include <boost/algorithm/string/replace.hpp> using namespace std; using namespace dev; +using namespace langutil; using namespace dev::solidity; -SMTChecker::SMTChecker(ErrorReporter& _errorReporter, ReadCallback::Callback const& _readFileCallback): - m_interface(make_shared<smt::SMTPortfolio>(_readFileCallback)), +SMTChecker::SMTChecker(ErrorReporter& _errorReporter, map<h256, string> const& _smtlib2Responses): + m_interface(make_shared<smt::SMTPortfolio>(_smtlib2Responses)), m_errorReporter(_errorReporter) { +#if defined (HAVE_Z3) || defined (HAVE_CVC4) + if (!_smtlib2Responses.empty()) + m_errorReporter.warning( + "SMT-LIB2 query responses were given in the auxiliary input, " + "but this Solidity binary uses an SMT solver (Z3/CVC4) directly." + "These responses will be ignored." + "Consider disabling Z3/CVC4 at compilation time in order to use SMT-LIB2 responses." + ); +#endif } void SMTChecker::analyze(SourceUnit const& _source, shared_ptr<Scanner> const& _scanner) @@ -78,6 +88,9 @@ bool SMTChecker::visit(FunctionDefinition const& _function) m_interface->reset(); m_pathConditions.clear(); m_expressions.clear(); + m_specialVariables.clear(); + m_uninterpretedFunctions.clear(); + m_uninterpretedTerms.clear(); resetStateVariables(); initializeLocalVariables(_function); } @@ -310,9 +323,6 @@ void SMTChecker::endVisit(UnaryOperation const& _op) ); break; } - case Token::Add: // + - defineExpr(_op, expr(_op.subExpression())); - break; case Token::Sub: // - { defineExpr(_op, 0 - expr(_op.subExpression())); @@ -405,16 +415,24 @@ void SMTChecker::visitGasLeft(FunctionCall const& _funCall) auto const& symbolicVar = m_specialVariables.at(gasLeft); unsigned index = symbolicVar->index(); // We set the current value to unknown anyway to add type constraints. - symbolicVar->setUnknownValue(); + setUnknownValue(*symbolicVar); if (index > 0) m_interface->addAssertion(symbolicVar->currentValue() <= symbolicVar->valueAtIndex(index - 1)); } void SMTChecker::visitBlockHash(FunctionCall const& _funCall) { - string blockHash = "blockhash()"; - // TODO Define blockhash as an uninterpreted function - defineSpecialVariable(blockHash, _funCall); + string blockHash = "blockhash"; + auto const& arguments = _funCall.arguments(); + solAssert(arguments.size() == 1, ""); + smt::SortPointer paramSort = smtSort(*arguments.at(0)->annotation().type); + smt::SortPointer returnSort = smtSort(*_funCall.annotation().type); + defineUninterpretedFunction( + blockHash, + make_shared<smt::FunctionSort>(vector<smt::SortPointer>{paramSort}, returnSort) + ); + defineExpr(_funCall, m_uninterpretedFunctions.at(blockHash)({expr(*arguments.at(0))})); + m_uninterpretedTerms.push_back(&_funCall); } void SMTChecker::inlineFunctionCall(FunctionCall const& _funCall) @@ -451,6 +469,14 @@ void SMTChecker::inlineFunctionCall(FunctionCall const& _funCall) else if (_funDef && _funDef->isImplemented()) { vector<smt::Expression> funArgs; + auto const& funType = dynamic_cast<FunctionType const*>(_calledExpr->annotation().type.get()); + solAssert(funType, ""); + if (funType->bound()) + { + auto const& boundFunction = dynamic_cast<MemberAccess const*>(_calledExpr); + solAssert(boundFunction, ""); + funArgs.push_back(expr(boundFunction->expression())); + } for (auto arg: _funCall.arguments()) funArgs.push_back(expr(*arg)); initializeFunctionCallParameters(*_funDef, funArgs); @@ -542,6 +568,10 @@ void SMTChecker::endVisit(Return const& _return) bool SMTChecker::visit(MemberAccess const& _memberAccess) { + auto const& accessType = _memberAccess.annotation().type; + if (accessType->category() == Type::Category::Function) + return true; + auto const& exprType = _memberAccess.expression().annotation().type; solAssert(exprType, ""); if (exprType->category() == Type::Category::Magic) @@ -573,7 +603,7 @@ void SMTChecker::defineSpecialVariable(string const& _name, Expression const& _e { auto result = newSymbolicVariable(*_expr.annotation().type, _name, *m_interface); m_specialVariables.emplace(_name, result.second); - result.second->setUnknownValue(); + setUnknownValue(*result.second); if (result.first) m_errorReporter.warning( _expr.location(), @@ -587,6 +617,11 @@ void SMTChecker::defineSpecialVariable(string const& _name, Expression const& _e defineExpr(_expr, m_specialVariables.at(_name)->currentValue()); } +void SMTChecker::defineUninterpretedFunction(string const& _name, smt::SortPointer _sort) +{ + if (!m_uninterpretedFunctions.count(_name)) + m_uninterpretedFunctions.emplace(_name, m_interface->newVariable(_name, _sort)); +} void SMTChecker::arithmeticOperation(BinaryOperation const& _op) { @@ -770,6 +805,11 @@ void SMTChecker::checkCondition( expressionsToEvaluate.emplace_back(var.second->currentValue()); expressionNames.push_back(var.first); } + for (auto const& uf: m_uninterpretedTerms) + { + expressionsToEvaluate.emplace_back(expr(*uf)); + expressionNames.push_back(m_scanner->sourceAt(uf->location())); + } } smt::CheckResult result; vector<string> values; @@ -846,6 +886,10 @@ void SMTChecker::checkBooleanNotConstant(Expression const& _condition, string co { // everything fine. } + else if (positiveResult == smt::CheckResult::UNKNOWN || negatedResult == smt::CheckResult::UNKNOWN) + { + // can't do anything. + } else if (positiveResult == smt::CheckResult::UNSATISFIABLE && negatedResult == smt::CheckResult::UNSATISFIABLE) m_errorReporter.warning(_condition.location(), "Condition unreachable."); else @@ -1037,13 +1081,23 @@ smt::Expression SMTChecker::newValue(VariableDeclaration const& _decl) void SMTChecker::setZeroValue(VariableDeclaration const& _decl) { solAssert(knownVariable(_decl), ""); - m_variables.at(&_decl)->setZeroValue(); + setZeroValue(*m_variables.at(&_decl)); +} + +void SMTChecker::setZeroValue(SymbolicVariable& _variable) +{ + smt::setSymbolicZeroValue(_variable, *m_interface); } void SMTChecker::setUnknownValue(VariableDeclaration const& _decl) { solAssert(knownVariable(_decl), ""); - m_variables.at(&_decl)->setUnknownValue(); + setUnknownValue(*m_variables.at(&_decl)); +} + +void SMTChecker::setUnknownValue(SymbolicVariable& _variable) +{ + smt::setSymbolicUnknownValue(_variable, *m_interface); } smt::Expression SMTChecker::expr(Expression const& _e) diff --git a/libsolidity/formal/SMTChecker.h b/libsolidity/formal/SMTChecker.h index a7f955dd..34724848 100644 --- a/libsolidity/formal/SMTChecker.h +++ b/libsolidity/formal/SMTChecker.h @@ -25,50 +25,60 @@ #include <libsolidity/interface/ReadFile.h> -#include <libsolidity/parsing/Scanner.h> +#include <liblangutil/Scanner.h> #include <unordered_map> #include <string> #include <vector> +namespace langutil +{ +class ErrorReporter; +struct SourceLocation; +} + namespace dev { namespace solidity { class VariableUsage; -class ErrorReporter; class SMTChecker: private ASTConstVisitor { public: - SMTChecker(ErrorReporter& _errorReporter, ReadCallback::Callback const& _readCallback); + SMTChecker(langutil::ErrorReporter& _errorReporter, std::map<h256, std::string> const& _smtlib2Responses); + + void analyze(SourceUnit const& _sources, std::shared_ptr<langutil::Scanner> const& _scanner); - void analyze(SourceUnit const& _sources, std::shared_ptr<Scanner> const& _scanner); + /// This is used if the SMT solver is not directly linked into this binary. + /// @returns a list of inputs to the SMT solver that were not part of the argument to + /// the constructor. + std::vector<std::string> unhandledQueries() { return m_interface->unhandledQueries(); } private: // TODO: Check that we do not have concurrent reads and writes to a variable, // because the order of expression evaluation is undefined // TODO: or just force a certain order, but people might have a different idea about that. - virtual bool visit(ContractDefinition const& _node) override; - virtual void endVisit(ContractDefinition const& _node) override; - virtual void endVisit(VariableDeclaration const& _node) override; - virtual bool visit(FunctionDefinition const& _node) override; - virtual void endVisit(FunctionDefinition const& _node) override; - virtual bool visit(IfStatement const& _node) override; - virtual bool visit(WhileStatement const& _node) override; - virtual bool visit(ForStatement const& _node) override; - virtual void endVisit(VariableDeclarationStatement const& _node) override; - virtual void endVisit(Assignment const& _node) override; - virtual void endVisit(TupleExpression const& _node) override; - virtual void endVisit(UnaryOperation const& _node) override; - virtual void endVisit(BinaryOperation const& _node) override; - virtual void endVisit(FunctionCall const& _node) override; - virtual void endVisit(Identifier const& _node) override; - virtual void endVisit(Literal const& _node) override; - virtual void endVisit(Return const& _node) override; - virtual bool visit(MemberAccess const& _node) override; + bool visit(ContractDefinition const& _node) override; + void endVisit(ContractDefinition const& _node) override; + void endVisit(VariableDeclaration const& _node) override; + bool visit(FunctionDefinition const& _node) override; + void endVisit(FunctionDefinition const& _node) override; + bool visit(IfStatement const& _node) override; + bool visit(WhileStatement const& _node) override; + bool visit(ForStatement const& _node) override; + void endVisit(VariableDeclarationStatement const& _node) override; + void endVisit(Assignment const& _node) override; + void endVisit(TupleExpression const& _node) override; + void endVisit(UnaryOperation const& _node) override; + void endVisit(BinaryOperation const& _node) override; + void endVisit(FunctionCall const& _node) override; + void endVisit(Identifier const& _node) override; + void endVisit(Literal const& _node) override; + void endVisit(Return const& _node) override; + bool visit(MemberAccess const& _node) override; void arithmeticOperation(BinaryOperation const& _op); void compareOperation(BinaryOperation const& _op); @@ -83,13 +93,14 @@ private: void inlineFunctionCall(FunctionCall const&); void defineSpecialVariable(std::string const& _name, Expression const& _expr, bool _increaseIndex = false); + void defineUninterpretedFunction(std::string const& _name, smt::SortPointer _sort); /// Division expression in the given type. Requires special treatment because /// of rounding for signed division. smt::Expression division(smt::Expression _left, smt::Expression _right, IntegerType const& _type); - void assignment(VariableDeclaration const& _variable, Expression const& _value, SourceLocation const& _location); - void assignment(VariableDeclaration const& _variable, smt::Expression const& _value, SourceLocation const& _location); + void assignment(VariableDeclaration const& _variable, Expression const& _value, langutil::SourceLocation const& _location); + void assignment(VariableDeclaration const& _variable, smt::Expression const& _value, langutil::SourceLocation const& _location); /// Maps a variable to an SSA index. using VariableIndices = std::unordered_map<VariableDeclaration const*, int>; @@ -103,7 +114,7 @@ private: /// Check that a condition can be satisfied. void checkCondition( smt::Expression _condition, - SourceLocation const& _location, + langutil::SourceLocation const& _location, std::string const& _description, std::string const& _additionalValueName = "", smt::Expression* _additionalValue = nullptr @@ -116,7 +127,7 @@ private: std::string const& _description ); /// Checks that the value is in the range given by the type. - void checkUnderOverflow(smt::Expression _value, IntegerType const& _Type, SourceLocation const& _location); + void checkUnderOverflow(smt::Expression _value, IntegerType const& _Type, langutil::SourceLocation const& _location); std::pair<smt::CheckResult, std::vector<std::string>> @@ -151,8 +162,10 @@ private: /// Sets the value of the declaration to zero. void setZeroValue(VariableDeclaration const& _decl); + void setZeroValue(SymbolicVariable& _variable); /// Resets the variable to an unknown value (in its range). void setUnknownValue(VariableDeclaration const& decl); + void setUnknownValue(SymbolicVariable& _variable); /// Returns the expression corresponding to the AST node. Throws if the expression does not exist. smt::Expression expr(Expression const& _e); @@ -193,9 +206,14 @@ private: std::unordered_map<Expression const*, std::shared_ptr<SymbolicVariable>> m_expressions; std::unordered_map<VariableDeclaration const*, std::shared_ptr<SymbolicVariable>> m_variables; std::unordered_map<std::string, std::shared_ptr<SymbolicVariable>> m_specialVariables; + /// Stores the declaration of an Uninterpreted Function. + std::unordered_map<std::string, smt::Expression> m_uninterpretedFunctions; + /// Stores the instances of an Uninterpreted Function applied to arguments. + /// Used to retrieve models. + std::vector<Expression const*> m_uninterpretedTerms; std::vector<smt::Expression> m_pathConditions; - ErrorReporter& m_errorReporter; - std::shared_ptr<Scanner> m_scanner; + langutil::ErrorReporter& m_errorReporter; + std::shared_ptr<langutil::Scanner> m_scanner; /// Stores the current path of function calls. std::vector<FunctionDefinition const*> m_functionPath; diff --git a/libsolidity/formal/SMTLib2Interface.cpp b/libsolidity/formal/SMTLib2Interface.cpp index a6c1f87c..3cfa01b1 100644 --- a/libsolidity/formal/SMTLib2Interface.cpp +++ b/libsolidity/formal/SMTLib2Interface.cpp @@ -17,9 +17,11 @@ #include <libsolidity/formal/SMTLib2Interface.h> -#include <libsolidity/interface/Exceptions.h> +#include <liblangutil/Exceptions.h> #include <libsolidity/interface/ReadFile.h> +#include <libdevcore/Keccak256.h> + #include <boost/algorithm/string/predicate.hpp> #include <boost/algorithm/string/join.hpp> #include <boost/filesystem/operations.hpp> @@ -37,8 +39,8 @@ using namespace dev; using namespace dev::solidity; using namespace dev::solidity::smt; -SMTLib2Interface::SMTLib2Interface(ReadCallback::Callback const& _queryCallback): - m_queryCallback(_queryCallback) +SMTLib2Interface::SMTLib2Interface(map<h256, string> const& _queryResponses): + m_queryResponses(_queryResponses) { reset(); } @@ -47,8 +49,7 @@ void SMTLib2Interface::reset() { m_accumulatedOutput.clear(); m_accumulatedOutput.emplace_back(); - m_constants.clear(); - m_functions.clear(); + m_variables.clear(); write("(set-option :produce-models true)"); write("(set-logic QF_UFLIA)"); } @@ -64,42 +65,39 @@ void SMTLib2Interface::pop() m_accumulatedOutput.pop_back(); } -void SMTLib2Interface::declareFunction(string _name, Sort _domain, Sort _codomain) +void SMTLib2Interface::declareVariable(string const& _name, Sort const& _sort) +{ + if (_sort.kind == Kind::Function) + declareFunction(_name, _sort); + else if (!m_variables.count(_name)) + { + m_variables.insert(_name); + write("(declare-fun |" + _name + "| () " + toSmtLibSort(_sort) + ')'); + } +} + +void SMTLib2Interface::declareFunction(string const& _name, Sort const& _sort) { + solAssert(_sort.kind == smt::Kind::Function, ""); // TODO Use domain and codomain as key as well - if (!m_functions.count(_name)) + if (!m_variables.count(_name)) { - m_functions.insert(_name); + FunctionSort fSort = dynamic_cast<FunctionSort const&>(_sort); + string domain = toSmtLibSort(fSort.domain); + string codomain = toSmtLibSort(*fSort.codomain); + m_variables.insert(_name); write( "(declare-fun |" + _name + - "| (" + - (_domain == Sort::Int ? "Int" : "Bool") + - ") " + - (_codomain == Sort::Int ? "Int" : "Bool") + + "| " + + domain + + " " + + codomain + ")" ); } } -void SMTLib2Interface::declareInteger(string _name) -{ - if (!m_constants.count(_name)) - { - m_constants.insert(_name); - write("(declare-const |" + _name + "| Int)"); - } -} - -void SMTLib2Interface::declareBool(string _name) -{ - if (!m_constants.count(_name)) - { - m_constants.insert(_name); - write("(declare-const |" + _name + "| Bool)"); - } -} - void SMTLib2Interface::addAssertion(Expression const& _expr) { write("(assert " + toSExpr(_expr) + ")"); @@ -140,6 +138,33 @@ string SMTLib2Interface::toSExpr(Expression const& _expr) 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); + return "(Array " + toSmtLibSort(*arraySort.domain) + ' ' + toSmtLibSort(*arraySort.range) + ')'; + } + 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(), ""); @@ -157,8 +182,8 @@ string SMTLib2Interface::checkSatAndGetValuesCommand(vector<Expression> const& _ for (size_t i = 0; i < _expressionsToEvaluate.size(); i++) { auto const& e = _expressionsToEvaluate.at(i); - solAssert(e.sort == Sort::Int || e.sort == Sort::Bool, "Invalid sort for expression to evaluate."); - command += "(declare-const |EVALEXPR_" + to_string(i) + "| " + (e.sort == Sort::Int ? "Int" : "Bool") + ")\n"; + 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"; @@ -189,11 +214,12 @@ vector<string> SMTLib2Interface::parseValues(string::const_iterator _start, stri string SMTLib2Interface::querySolver(string const& _input) { - if (!m_queryCallback) - BOOST_THROW_EXCEPTION(SolverError() << errinfo_comment("No SMT solver available.")); - - ReadCallback::Result queryResult = m_queryCallback(_input); - if (!queryResult.success) - BOOST_THROW_EXCEPTION(SolverError() << errinfo_comment(queryResult.responseOrErrorMessage)); - return queryResult.responseOrErrorMessage; + h256 inputHash = dev::keccak256(_input); + if (m_queryResponses.count(inputHash)) + return m_queryResponses.at(inputHash); + else + { + m_unhandledQueries.push_back(_input); + return "unknown\n"; + } } diff --git a/libsolidity/formal/SMTLib2Interface.h b/libsolidity/formal/SMTLib2Interface.h index eb876a7f..55fc4096 100644 --- a/libsolidity/formal/SMTLib2Interface.h +++ b/libsolidity/formal/SMTLib2Interface.h @@ -19,9 +19,11 @@ #include <libsolidity/formal/SolverInterface.h> -#include <libsolidity/interface/Exceptions.h> +#include <liblangutil/Exceptions.h> #include <libsolidity/interface/ReadFile.h> +#include <libdevcore/FixedHash.h> + #include <libdevcore/Common.h> #include <boost/noncopyable.hpp> @@ -42,22 +44,26 @@ namespace smt class SMTLib2Interface: public SolverInterface, public boost::noncopyable { public: - explicit SMTLib2Interface(ReadCallback::Callback const& _queryCallback); + explicit SMTLib2Interface(std::map<h256, std::string> const& _queryResponses); void reset() override; void push() override; void pop() override; - void declareFunction(std::string _name, Sort _domain, Sort _codomain) override; - void declareInteger(std::string _name) override; - void declareBool(std::string _name) override; + void declareVariable(std::string const&, Sort const&) override; void addAssertion(Expression const& _expr) override; std::pair<CheckResult, std::vector<std::string>> check(std::vector<Expression> const& _expressionsToEvaluate) override; + std::vector<std::string> unhandledQueries() override { return m_unhandledQueries; } + private: + void declareFunction(std::string const&, Sort const&); + std::string toSExpr(Expression const& _expr); + std::string toSmtLibSort(Sort const& _sort); + std::string toSmtLibSort(std::vector<SortPointer> const& _sort); void write(std::string _data); @@ -67,10 +73,11 @@ private: /// Communicates with the solver via the callback. Throws SMTSolverError on error. std::string querySolver(std::string const& _input); - ReadCallback::Callback m_queryCallback; std::vector<std::string> m_accumulatedOutput; - std::set<std::string> m_constants; - std::set<std::string> m_functions; + std::set<std::string> m_variables; + + std::map<h256, std::string> const& m_queryResponses; + std::vector<std::string> m_unhandledQueries; }; } diff --git a/libsolidity/formal/SMTPortfolio.cpp b/libsolidity/formal/SMTPortfolio.cpp index 8b9fe9ce..2a109b89 100644 --- a/libsolidity/formal/SMTPortfolio.cpp +++ b/libsolidity/formal/SMTPortfolio.cpp @@ -23,27 +23,22 @@ #ifdef HAVE_CVC4 #include <libsolidity/formal/CVC4Interface.h> #endif -#if !defined (HAVE_Z3) && !defined (HAVE_CVC4) #include <libsolidity/formal/SMTLib2Interface.h> -#endif using namespace std; using namespace dev; using namespace dev::solidity; using namespace dev::solidity::smt; -SMTPortfolio::SMTPortfolio(ReadCallback::Callback const& _readCallback) +SMTPortfolio::SMTPortfolio(map<h256, string> const& _smtlib2Responses) { + m_solvers.emplace_back(make_shared<smt::SMTLib2Interface>(_smtlib2Responses)); #ifdef HAVE_Z3 m_solvers.emplace_back(make_shared<smt::Z3Interface>()); #endif #ifdef HAVE_CVC4 m_solvers.emplace_back(make_shared<smt::CVC4Interface>()); #endif -#if !defined (HAVE_Z3) && !defined (HAVE_CVC4) - m_solvers.emplace_back(make_shared<smt::SMTLib2Interface>(_readCallback)), -#endif - (void)_readCallback; } void SMTPortfolio::reset() @@ -64,22 +59,10 @@ void SMTPortfolio::pop() s->pop(); } -void SMTPortfolio::declareFunction(string _name, Sort _domain, Sort _codomain) -{ - for (auto s : m_solvers) - s->declareFunction(_name, _domain, _codomain); -} - -void SMTPortfolio::declareInteger(string _name) -{ - for (auto s : m_solvers) - s->declareInteger(_name); -} - -void SMTPortfolio::declareBool(string _name) +void SMTPortfolio::declareVariable(string const& _name, Sort const& _sort) { for (auto s : m_solvers) - s->declareBool(_name); + s->declareVariable(_name, _sort); } void SMTPortfolio::addAssertion(Expression const& _expr) diff --git a/libsolidity/formal/SMTPortfolio.h b/libsolidity/formal/SMTPortfolio.h index 96c7ff57..7f5ba37e 100644 --- a/libsolidity/formal/SMTPortfolio.h +++ b/libsolidity/formal/SMTPortfolio.h @@ -22,8 +22,11 @@ #include <libsolidity/interface/ReadFile.h> +#include <libdevcore/FixedHash.h> + #include <boost/noncopyable.hpp> +#include <map> #include <vector> namespace dev @@ -42,20 +45,19 @@ namespace smt class SMTPortfolio: public SolverInterface, public boost::noncopyable { public: - SMTPortfolio(ReadCallback::Callback const& _readCallback); + SMTPortfolio(std::map<h256, std::string> const& _smtlib2Responses); void reset() override; void push() override; void pop() override; - void declareFunction(std::string _name, Sort _domain, Sort _codomain) override; - void declareInteger(std::string _name) override; - void declareBool(std::string _name) override; + void declareVariable(std::string const&, Sort const&) override; void addAssertion(Expression const& _expr) override; std::pair<CheckResult, std::vector<std::string>> check(std::vector<Expression> const& _expressionsToEvaluate) override; + std::vector<std::string> unhandledQueries() override { return m_solvers.at(0)->unhandledQueries(); } private: static bool solverAnswered(CheckResult result); diff --git a/libsolidity/formal/SolverInterface.h b/libsolidity/formal/SolverInterface.h index af1cc8e4..4a4b3fb1 100644 --- a/libsolidity/formal/SolverInterface.h +++ b/libsolidity/formal/SolverInterface.h @@ -17,7 +17,7 @@ #pragma once -#include <libsolidity/interface/Exceptions.h> +#include <liblangutil/Exceptions.h> #include <libsolidity/interface/ReadFile.h> #include <libdevcore/Common.h> @@ -42,12 +42,68 @@ enum class CheckResult SATISFIABLE, UNSATISFIABLE, UNKNOWN, CONFLICTING, ERROR }; -enum class Sort +enum class Kind { Int, Bool, - IntIntFun, // Function of one Int returning a single Int - IntBoolFun // Function of one Int returning a single Bool + Function, + Array +}; + +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; + 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, ""); + return *domain == *_otherArray->domain && *range == *_otherArray->range; + } + + SortPointer domain; + SortPointer range; }; /// C++ representation of an SMTLIB2 expression. @@ -55,10 +111,10 @@ class Expression { friend class SolverInterface; public: - explicit Expression(bool _v): name(_v ? "true" : "false"), sort(Sort::Bool) {} - Expression(size_t _number): name(std::to_string(_number)), sort(Sort::Int) {} - Expression(u256 const& _number): name(_number.str()), sort(Sort::Int) {} - Expression(bigint const& _number): name(_number.str()), sort(Sort::Int) {} + explicit Expression(bool _v): Expression(_v ? "true" : "false", Kind::Bool) {} + Expression(size_t _number): Expression(std::to_string(_number), Kind::Int) {} + Expression(u256 const& _number): Expression(_number.str(), Kind::Int) {} + Expression(bigint const& _number): Expression(_number.str(), Kind::Int) {} Expression(Expression const&) = default; Expression(Expression&&) = default; @@ -80,17 +136,20 @@ public: {"+", 2}, {"-", 2}, {"*", 2}, - {"/", 2} + {"/", 2}, + {"select", 2}, + {"store", 3} }; return operatorsArity.count(name) && operatorsArity.at(name) == arguments.size(); } static Expression ite(Expression _condition, Expression _trueValue, Expression _falseValue) { - solAssert(_trueValue.sort == _falseValue.sort, ""); + solAssert(*_trueValue.sort == *_falseValue.sort, ""); + SortPointer sort = _trueValue.sort; return Expression("ite", std::vector<Expression>{ std::move(_condition), std::move(_trueValue), std::move(_falseValue) - }, _trueValue.sort); + }, std::move(sort)); } static Expression implies(Expression _a, Expression _b) @@ -98,21 +157,51 @@ public: return !std::move(_a) || std::move(_b); } + /// select is the SMT representation of an array index access. + static Expression select(Expression _array, Expression _index) + { + solAssert(_array.sort->kind == Kind::Array, ""); + auto const& arraySort = dynamic_cast<ArraySort const*>(_array.sort.get()); + solAssert(arraySort, ""); + 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) + { + solAssert(_array.sort->kind == Kind::Array, ""); + auto const& arraySort = dynamic_cast<ArraySort const*>(_array.sort.get()); + solAssert(arraySort, ""); + 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)}, + _array.sort + ); + } + friend Expression operator!(Expression _a) { - return Expression("not", std::move(_a), Sort::Bool); + return Expression("not", std::move(_a), Kind::Bool); } friend Expression operator&&(Expression _a, Expression _b) { - return Expression("and", std::move(_a), std::move(_b), Sort::Bool); + 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), Sort::Bool); + 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), Sort::Bool); + return Expression("=", std::move(_a), std::move(_b), Kind::Bool); } friend Expression operator!=(Expression _a, Expression _b) { @@ -120,72 +209,64 @@ public: } friend Expression operator<(Expression _a, Expression _b) { - return Expression("<", std::move(_a), std::move(_b), Sort::Bool); + 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), Sort::Bool); + 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), Sort::Bool); + 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), Sort::Bool); + 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), Sort::Int); + 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), Sort::Int); + 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), Sort::Int); + 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), Sort::Int); + return Expression("/", std::move(_a), std::move(_b), Kind::Int); } - Expression operator()(Expression _a) const + Expression operator()(std::vector<Expression> _arguments) const { solAssert( - arguments.empty(), + sort->kind == Kind::Function, "Attempted function application to non-function." ); - switch (sort) - { - case Sort::IntIntFun: - return Expression(name, _a, Sort::Int); - case Sort::IntBoolFun: - return Expression(name, _a, Sort::Bool); - default: - solAssert( - false, - "Attempted function application to invalid type." - ); - break; - } + 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; - Sort sort; + SortPointer sort; private: - /// Manual constructor, should only be used by SolverInterface and this class itself. - Expression(std::string _name, std::vector<Expression> _arguments, Sort _sort): - name(std::move(_name)), arguments(std::move(_arguments)), sort(_sort) {} - - explicit Expression(std::string _name, Sort _sort): - Expression(std::move(_name), std::vector<Expression>{}, _sort) {} - Expression(std::string _name, Expression _arg, Sort _sort): - Expression(std::move(_name), std::vector<Expression>{std::move(_arg)}, _sort) {} - Expression(std::string _name, Expression _arg1, Expression _arg2, Sort _sort): - Expression(std::move(_name), std::vector<Expression>{std::move(_arg1), std::move(_arg2)}, _sort) {} + /// 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); @@ -199,36 +280,12 @@ public: virtual void push() = 0; virtual void pop() = 0; - virtual void declareFunction(std::string _name, Sort _domain, Sort _codomain) = 0; - Expression newFunction(std::string _name, Sort _domain, Sort _codomain) - { - declareFunction(_name, _domain, _codomain); - solAssert(_domain == Sort::Int, "Function sort not supported."); - // Subclasses should do something here - switch (_codomain) - { - case Sort::Int: - return Expression(std::move(_name), {}, Sort::IntIntFun); - case Sort::Bool: - return Expression(std::move(_name), {}, Sort::IntBoolFun); - default: - solAssert(false, "Function sort not supported."); - break; - } - } - virtual void declareInteger(std::string _name) = 0; - Expression newInteger(std::string _name) + virtual void declareVariable(std::string const& _name, Sort const& _sort) = 0; + Expression newVariable(std::string _name, SortPointer _sort) { // Subclasses should do something here - declareInteger(_name); - return Expression(std::move(_name), {}, Sort::Int); - } - virtual void declareBool(std::string _name) = 0; - Expression newBool(std::string _name) - { - // Subclasses should do something here - declareBool(_name); - return Expression(std::move(_name), {}, Sort::Bool); + declareVariable(_name, *_sort); + return Expression(std::move(_name), {}, std::move(_sort)); } virtual void addAssertion(Expression const& _expr) = 0; @@ -238,6 +295,9 @@ public: 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 {}; } + protected: // SMT query timeout in milliseconds. static int const queryTimeout = 10000; diff --git a/libsolidity/formal/SymbolicTypes.cpp b/libsolidity/formal/SymbolicTypes.cpp index 3eb1c1ce..c297c807 100644 --- a/libsolidity/formal/SymbolicTypes.cpp +++ b/libsolidity/formal/SymbolicTypes.cpp @@ -24,6 +24,50 @@ using namespace std; using namespace dev::solidity; +smt::SortPointer dev::solidity::smtSort(Type const& _type) +{ + switch (smtKind(_type.category())) + { + case smt::Kind::Int: + return make_shared<smt::Sort>(smt::Kind::Int); + case smt::Kind::Bool: + return make_shared<smt::Sort>(smt::Kind::Bool); + case smt::Kind::Function: + { + auto fType = dynamic_cast<FunctionType const*>(&_type); + solAssert(fType, ""); + vector<smt::SortPointer> parameterSorts = smtSort(fType->parameterTypes()); + auto returnTypes = fType->returnParameterTypes(); + // TODO remove this when we support tuples. + solAssert(returnTypes.size() == 1, ""); + smt::SortPointer returnSort = smtSort(*returnTypes.at(0)); + return make_shared<smt::FunctionSort>(parameterSorts, returnSort); + } + case smt::Kind::Array: + { + solUnimplementedAssert(false, "Invalid type"); + } + } + solAssert(false, "Invalid type"); +} + +vector<smt::SortPointer> dev::solidity::smtSort(vector<TypePointer> const& _types) +{ + vector<smt::SortPointer> sorts; + for (auto const& type: _types) + sorts.push_back(smtSort(*type)); + return sorts; +} + +smt::Kind dev::solidity::smtKind(Type::Category _category) +{ + if (isNumber(_category)) + return smt::Kind::Int; + else if (isBool(_category)) + return smt::Kind::Bool; + solAssert(false, "Invalid type"); +} + bool dev::solidity::isSupportedType(Type::Category _category) { return isNumber(_category) || @@ -125,3 +169,32 @@ smt::Expression dev::solidity::maxValue(IntegerType const& _type) { return smt::Expression(_type.maxValue()); } + +void dev::solidity::smt::setSymbolicZeroValue(SymbolicVariable const& _variable, smt::SolverInterface& _interface) +{ + setSymbolicZeroValue(_variable.currentValue(), _variable.type(), _interface); +} + +void dev::solidity::smt::setSymbolicZeroValue(smt::Expression _expr, TypePointer const& _type, smt::SolverInterface& _interface) +{ + if (isInteger(_type->category())) + _interface.addAssertion(_expr == 0); + else if (isBool(_type->category())) + _interface.addAssertion(_expr == smt::Expression(false)); +} + +void dev::solidity::smt::setSymbolicUnknownValue(SymbolicVariable const& _variable, smt::SolverInterface& _interface) +{ + setSymbolicUnknownValue(_variable.currentValue(), _variable.type(), _interface); +} + +void dev::solidity::smt::setSymbolicUnknownValue(smt::Expression _expr, TypePointer const& _type, smt::SolverInterface& _interface) +{ + if (isInteger(_type->category())) + { + auto intType = dynamic_cast<IntegerType const*>(_type.get()); + solAssert(intType, ""); + _interface.addAssertion(_expr >= minValue(*intType)); + _interface.addAssertion(_expr <= maxValue(*intType)); + } +} diff --git a/libsolidity/formal/SymbolicTypes.h b/libsolidity/formal/SymbolicTypes.h index dcdd9ea4..984653b3 100644 --- a/libsolidity/formal/SymbolicTypes.h +++ b/libsolidity/formal/SymbolicTypes.h @@ -28,6 +28,12 @@ namespace dev namespace solidity { +/// Returns the SMT sort that models the Solidity type _type. +smt::SortPointer smtSort(Type const& _type); +std::vector<smt::SortPointer> smtSort(std::vector<TypePointer> const& _types); +/// Returns the SMT kind that models the Solidity type type category _category. +smt::Kind smtKind(Type::Category _category); + /// So far int, bool and address are supported. /// Returns true if type is supported. bool isSupportedType(Type::Category _category); @@ -49,5 +55,15 @@ std::pair<bool, std::shared_ptr<SymbolicVariable>> newSymbolicVariable(Type cons smt::Expression minValue(IntegerType const& _type); smt::Expression maxValue(IntegerType const& _type); +namespace smt +{ + +void setSymbolicZeroValue(SymbolicVariable const& _variable, smt::SolverInterface& _interface); +void setSymbolicZeroValue(smt::Expression _expr, TypePointer const& _type, smt::SolverInterface& _interface); +void setSymbolicUnknownValue(SymbolicVariable const& _variable, smt::SolverInterface& _interface); +void setSymbolicUnknownValue(smt::Expression _expr, TypePointer const& _type, smt::SolverInterface& _interface); + +} + } } diff --git a/libsolidity/formal/SymbolicVariables.cpp b/libsolidity/formal/SymbolicVariables.cpp index 85818ba0..efaeb97a 100644 --- a/libsolidity/formal/SymbolicVariables.cpp +++ b/libsolidity/formal/SymbolicVariables.cpp @@ -37,6 +37,11 @@ SymbolicVariable::SymbolicVariable( { } +string SymbolicVariable::currentName() const +{ + return uniqueSymbol(m_ssa->index()); +} + string SymbolicVariable::uniqueSymbol(unsigned _index) const { return m_uniqueName + "_" + to_string(_index); @@ -54,16 +59,7 @@ SymbolicBoolVariable::SymbolicBoolVariable( smt::Expression SymbolicBoolVariable::valueAtIndex(int _index) const { - return m_interface.newBool(uniqueSymbol(_index)); -} - -void SymbolicBoolVariable::setZeroValue() -{ - m_interface.addAssertion(currentValue() == smt::Expression(false)); -} - -void SymbolicBoolVariable::setUnknownValue() -{ + return m_interface.newVariable(uniqueSymbol(_index), make_shared<smt::Sort>(smt::Kind::Bool)); } SymbolicIntVariable::SymbolicIntVariable( @@ -78,20 +74,7 @@ SymbolicIntVariable::SymbolicIntVariable( smt::Expression SymbolicIntVariable::valueAtIndex(int _index) const { - return m_interface.newInteger(uniqueSymbol(_index)); -} - -void SymbolicIntVariable::setZeroValue() -{ - m_interface.addAssertion(currentValue() == 0); -} - -void SymbolicIntVariable::setUnknownValue() -{ - auto intType = dynamic_cast<IntegerType const*>(m_type.get()); - solAssert(intType, ""); - m_interface.addAssertion(currentValue() >= minValue(*intType)); - m_interface.addAssertion(currentValue() <= maxValue(*intType)); + return m_interface.newVariable(uniqueSymbol(_index), make_shared<smt::Sort>(smt::Kind::Int)); } SymbolicAddressVariable::SymbolicAddressVariable( diff --git a/libsolidity/formal/SymbolicVariables.h b/libsolidity/formal/SymbolicVariables.h index 4fd9b245..fcf32760 100644 --- a/libsolidity/formal/SymbolicVariables.h +++ b/libsolidity/formal/SymbolicVariables.h @@ -51,6 +51,8 @@ public: return valueAtIndex(m_ssa->index()); } + std::string currentName() const; + virtual smt::Expression valueAtIndex(int _index) const = 0; smt::Expression increaseIndex() @@ -62,20 +64,15 @@ public: unsigned index() const { return m_ssa->index(); } unsigned& index() { return m_ssa->index(); } - /// Sets the var to the default value of its type. - /// Inherited types must implement. - virtual void setZeroValue() = 0; - /// The unknown value is the full range of valid values. - /// It is sub-type dependent, but not mandatory. - virtual void setUnknownValue() {} + TypePointer const& type() const { return m_type; } protected: std::string uniqueSymbol(unsigned _index) const; - TypePointer m_type = nullptr; + TypePointer m_type; std::string m_uniqueName; smt::SolverInterface& m_interface; - std::shared_ptr<SSAVariable> m_ssa = nullptr; + std::shared_ptr<SSAVariable> m_ssa; }; /** @@ -90,11 +87,6 @@ public: smt::SolverInterface& _interface ); - /// Sets the var to false. - void setZeroValue(); - /// Does nothing since the SMT solver already knows the valid values for Bool. - void setUnknownValue(); - protected: smt::Expression valueAtIndex(int _index) const; }; @@ -111,11 +103,6 @@ public: smt::SolverInterface& _interface ); - /// Sets the var to 0. - void setZeroValue(); - /// Sets the variable to the full valid value range. - void setUnknownValue(); - protected: smt::Expression valueAtIndex(int _index) const; }; diff --git a/libsolidity/formal/Z3Interface.cpp b/libsolidity/formal/Z3Interface.cpp index 9a0ccf48..cb01dc61 100644 --- a/libsolidity/formal/Z3Interface.cpp +++ b/libsolidity/formal/Z3Interface.cpp @@ -17,7 +17,7 @@ #include <libsolidity/formal/Z3Interface.h> -#include <libsolidity/interface/Exceptions.h> +#include <liblangutil/Exceptions.h> #include <libdevcore/CommonIO.h> @@ -51,22 +51,22 @@ void Z3Interface::pop() m_solver.pop(); } -void Z3Interface::declareFunction(string _name, Sort _domain, Sort _codomain) +void Z3Interface::declareVariable(string const& _name, Sort const& _sort) { - if (!m_functions.count(_name)) - m_functions.insert({_name, m_context.function(_name.c_str(), z3Sort(_domain), z3Sort(_codomain))}); -} - -void Z3Interface::declareInteger(string _name) -{ - if (!m_constants.count(_name)) - m_constants.insert({_name, m_context.int_const(_name.c_str())}); + if (_sort.kind == Kind::Function) + declareFunction(_name, _sort); + else if (!m_constants.count(_name)) + m_constants.insert({_name, m_context.constant(_name.c_str(), z3Sort(_sort))}); } -void Z3Interface::declareBool(string _name) +void Z3Interface::declareFunction(string const& _name, Sort const& _sort) { - if (!m_constants.count(_name)) - m_constants.insert({_name, m_context.bool_const(_name.c_str())}); + solAssert(_sort.kind == smt::Kind::Function, ""); + if (!m_functions.count(_name)) + { + FunctionSort fSort = dynamic_cast<FunctionSort const&>(_sort); + m_functions.insert({_name, m_context.function(_name.c_str(), z3Sort(fSort.domain), z3Sort(*fSort.codomain))}); + } } void Z3Interface::addAssertion(Expression const& _expr) @@ -163,19 +163,28 @@ z3::expr Z3Interface::toZ3Expr(Expression const& _expr) return arguments[0] * arguments[1]; else if (n == "/") return 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]); // Cannot reach here. solAssert(false, ""); return arguments[0]; } -z3::sort Z3Interface::z3Sort(Sort _sort) +z3::sort Z3Interface::z3Sort(Sort const& _sort) { - switch (_sort) + switch (_sort.kind) { - case Sort::Bool: + case Kind::Bool: return m_context.bool_sort(); - case Sort::Int: + 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)); + } default: break; } @@ -183,3 +192,11 @@ z3::sort Z3Interface::z3Sort(Sort _sort) // 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; +} diff --git a/libsolidity/formal/Z3Interface.h b/libsolidity/formal/Z3Interface.h index 84880ff3..86e1badd 100644 --- a/libsolidity/formal/Z3Interface.h +++ b/libsolidity/formal/Z3Interface.h @@ -40,16 +40,17 @@ public: void push() override; void pop() override; - void declareFunction(std::string _name, Sort _domain, Sort _codomain) override; - void declareInteger(std::string _name) override; - void declareBool(std::string _name) override; + void declareVariable(std::string const& _name, Sort const& _sort) override; void addAssertion(Expression const& _expr) override; std::pair<CheckResult, std::vector<std::string>> check(std::vector<Expression> const& _expressionsToEvaluate) override; private: + void declareFunction(std::string const& _name, Sort const& _sort); + z3::expr toZ3Expr(Expression const& _expr); - z3::sort z3Sort(smt::Sort _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; |