/* 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 #include using namespace std; using namespace dev; using namespace dev::solidity; using namespace dev::solidity::smt; SMTPortfolio::SMTPortfolio(map const& _smtlib2Responses) { m_solvers.emplace_back(make_unique(_smtlib2Responses)); #ifdef HAVE_Z3 m_solvers.emplace_back(make_unique()); #endif #ifdef HAVE_CVC4 m_solvers.emplace_back(make_unique()); #endif } void SMTPortfolio::reset() { m_assertions.clear(); for (auto const& s: m_solvers) s->reset(); } void SMTPortfolio::push() { m_assertions.push_back(Expression(true)); for (auto const& s: m_solvers) s->push(); } void SMTPortfolio::pop() { m_assertions.pop_back(); for (auto const& s: m_solvers) s->pop(); } void SMTPortfolio::declareVariable(string const& _name, Sort const& _sort) { for (auto const& s: m_solvers) s->declareVariable(_name, _sort); } void SMTPortfolio::addAssertion(Expression const& _expr) { if (m_assertions.empty()) m_assertions.push_back(_expr); else m_assertions.back() = _expr && move(m_assertions.back()); for (auto const& s: m_solvers) s->addAssertion(_expr); } Expression SMTPortfolio::assertions() { if (m_assertions.empty()) return Expression(true); return m_assertions.back(); } /* * 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 const& 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); } vector SMTPortfolio::unhandledQueries() { // This code assumes that the constructor guarantees that // SmtLib2Interface is in position 0. solAssert(!m_solvers.empty(), ""); solAssert(dynamic_cast(m_solvers.front().get()), ""); return m_solvers.front()->unhandledQueries(); } bool SMTPortfolio::solverAnswered(CheckResult result) { return result == CheckResult::SATISFIABLE || result == CheckResult::UNSATISFIABLE; }