/* 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 . */ #include #ifdef HAVE_Z3 #include #endif #ifdef HAVE_CVC4 #include #endif #if !defined (HAVE_Z3) && !defined (HAVE_CVC4) #include #endif using namespace std; using namespace dev; using namespace dev::solidity; using namespace dev::solidity::smt; SMTPortfolio::SMTPortfolio(map const& _smtlib2Responses) { #ifdef HAVE_Z3 m_solvers.emplace_back(make_shared()); #endif #ifdef HAVE_CVC4 m_solvers.emplace_back(make_shared()); #endif #if !defined (HAVE_Z3) && !defined (HAVE_CVC4) m_solvers.emplace_back(make_shared(_smtlib2Responses)); #else if (!_smtlib2Responses.empty()) m_errorReporter.warning( "Query responses for smtlib2 were given in the auxiliary input, " "but this Solidity binary uses an SMT solver directly." ); #endif (void)_smtlib2Responses; } void SMTPortfolio::reset() { for (auto s : m_solvers) s->reset(); } void SMTPortfolio::push() { for (auto s : m_solvers) s->push(); } void SMTPortfolio::pop() { for (auto s : m_solvers) s->pop(); } void SMTPortfolio::declareVariable(string const& _name, Sort const& _sort) { for (auto s : m_solvers) s->declareVariable(_name, _sort); } void SMTPortfolio::addAssertion(Expression const& _expr) { for (auto 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> SMTPortfolio::check(vector const& _expressionsToEvaluate) { CheckResult lastResult = CheckResult::ERROR; vector finalValues; for (auto s : m_solvers) { CheckResult result; vector 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); } bool SMTPortfolio::solverAnswered(CheckResult result) { return result == CheckResult::SATISFIABLE || result == CheckResult::UNSATISFIABLE; }