mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Partial support for if statements.
This commit is contained in:
parent
5bfd5d98c1
commit
75f09f2a84
@ -25,6 +25,8 @@
|
|||||||
|
|
||||||
#include <libsolidity/interface/ErrorReporter.h>
|
#include <libsolidity/interface/ErrorReporter.h>
|
||||||
|
|
||||||
|
#include <boost/range/adaptor/map.hpp>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace dev;
|
using namespace dev;
|
||||||
using namespace dev::solidity;
|
using namespace dev::solidity;
|
||||||
@ -51,6 +53,7 @@ void SMTChecker::analyze(SourceUnit const& _source)
|
|||||||
{
|
{
|
||||||
m_interface->reset();
|
m_interface->reset();
|
||||||
m_currentSequenceCounter.clear();
|
m_currentSequenceCounter.clear();
|
||||||
|
m_nextFreeSequenceCounter.clear();
|
||||||
_source.accept(*this);
|
_source.accept(*this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -89,10 +92,68 @@ void SMTChecker::endVisit(FunctionDefinition const&)
|
|||||||
// We only handle local variables, so we clear everything.
|
// We only handle local variables, so we clear everything.
|
||||||
// If we add storage variables, those should be cleared differently.
|
// If we add storage variables, those should be cleared differently.
|
||||||
m_currentSequenceCounter.clear();
|
m_currentSequenceCounter.clear();
|
||||||
|
m_nextFreeSequenceCounter.clear();
|
||||||
m_interface->pop();
|
m_interface->pop();
|
||||||
m_currentFunction = nullptr;
|
m_currentFunction = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SMTChecker::visit(IfStatement const& _node)
|
||||||
|
{
|
||||||
|
_node.condition().accept(*this);
|
||||||
|
|
||||||
|
// TODO Check if condition is always true
|
||||||
|
|
||||||
|
auto countersAtStart = m_currentSequenceCounter;
|
||||||
|
m_interface->push();
|
||||||
|
m_interface->addAssertion(expr(_node.condition()));
|
||||||
|
_node.trueStatement().accept(*this);
|
||||||
|
auto countersAtEndOfTrue = m_currentSequenceCounter;
|
||||||
|
m_interface->pop();
|
||||||
|
|
||||||
|
decltype(m_currentSequenceCounter) countersAtEndOfFalse;
|
||||||
|
if (_node.falseStatement())
|
||||||
|
{
|
||||||
|
m_currentSequenceCounter = countersAtStart;
|
||||||
|
m_interface->push();
|
||||||
|
m_interface->addAssertion(!expr(_node.condition()));
|
||||||
|
_node.falseStatement()->accept(*this);
|
||||||
|
countersAtEndOfFalse = m_currentSequenceCounter;
|
||||||
|
m_interface->pop();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
countersAtEndOfFalse = countersAtStart;
|
||||||
|
|
||||||
|
// Reset all values that have been touched.
|
||||||
|
|
||||||
|
// TODO this should use a previously generated side-effect structure
|
||||||
|
|
||||||
|
solAssert(countersAtEndOfFalse.size() == countersAtEndOfTrue.size(), "");
|
||||||
|
for (auto const& declCounter: countersAtEndOfTrue)
|
||||||
|
{
|
||||||
|
solAssert(countersAtEndOfFalse.count(declCounter.first), "");
|
||||||
|
auto decl = declCounter.first;
|
||||||
|
int trueCounter = countersAtEndOfTrue.at(decl);
|
||||||
|
int falseCounter = countersAtEndOfFalse.at(decl);
|
||||||
|
if (trueCounter == falseCounter)
|
||||||
|
continue; // Was not modified
|
||||||
|
newValue(*decl);
|
||||||
|
setValue(*decl, 0);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SMTChecker::visit(WhileStatement const& _node)
|
||||||
|
{
|
||||||
|
_node.condition().accept(*this);
|
||||||
|
|
||||||
|
//m_interface->push();
|
||||||
|
//m_interface->addAssertion(expr(_node.condition()));
|
||||||
|
// TDOO clear knowledge (increment sequence numbers and add bounds assertions ) apart from assertions
|
||||||
|
|
||||||
|
// TODO combine similar to if
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void SMTChecker::endVisit(VariableDeclarationStatement const& _varDecl)
|
void SMTChecker::endVisit(VariableDeclarationStatement const& _varDecl)
|
||||||
{
|
{
|
||||||
if (_varDecl.declarations().size() != 1)
|
if (_varDecl.declarations().size() != 1)
|
||||||
@ -100,10 +161,13 @@ void SMTChecker::endVisit(VariableDeclarationStatement const& _varDecl)
|
|||||||
_varDecl.location(),
|
_varDecl.location(),
|
||||||
"Assertion checker does not yet support such variable declarations."
|
"Assertion checker does not yet support such variable declarations."
|
||||||
);
|
);
|
||||||
else if (knownVariable(*_varDecl.declarations()[0]) && _varDecl.initialValue())
|
else if (knownVariable(*_varDecl.declarations()[0]))
|
||||||
// TODO more checks?
|
{
|
||||||
// TODO add restrictions about type (might be assignment from smaller type)
|
if (_varDecl.initialValue())
|
||||||
m_interface->addAssertion(newValue(*_varDecl.declarations()[0]) == expr(*_varDecl.initialValue()));
|
// TODO more checks?
|
||||||
|
// TODO add restrictions about type (might be assignment from smaller type)
|
||||||
|
m_interface->addAssertion(newValue(*_varDecl.declarations()[0]) == expr(*_varDecl.initialValue()));
|
||||||
|
}
|
||||||
else
|
else
|
||||||
m_errorReporter.warning(
|
m_errorReporter.warning(
|
||||||
_varDecl.location(),
|
_varDecl.location(),
|
||||||
@ -421,19 +485,15 @@ void SMTChecker::checkCondition(
|
|||||||
|
|
||||||
void SMTChecker::createVariable(VariableDeclaration const& _varDecl, bool _setToZero)
|
void SMTChecker::createVariable(VariableDeclaration const& _varDecl, bool _setToZero)
|
||||||
{
|
{
|
||||||
if (auto intType = dynamic_cast<IntegerType const*>(_varDecl.type().get()))
|
if (dynamic_cast<IntegerType const*>(_varDecl.type().get()))
|
||||||
{
|
{
|
||||||
solAssert(m_currentSequenceCounter.count(&_varDecl) == 0, "");
|
solAssert(m_currentSequenceCounter.count(&_varDecl) == 0, "");
|
||||||
|
solAssert(m_nextFreeSequenceCounter.count(&_varDecl) == 0, "");
|
||||||
solAssert(m_z3Variables.count(&_varDecl) == 0, "");
|
solAssert(m_z3Variables.count(&_varDecl) == 0, "");
|
||||||
m_currentSequenceCounter[&_varDecl] = 0;
|
m_currentSequenceCounter[&_varDecl] = 0;
|
||||||
|
m_nextFreeSequenceCounter[&_varDecl] = 1;
|
||||||
m_z3Variables.emplace(&_varDecl, m_interface->newFunction(uniqueSymbol(_varDecl), smt::Sort::Int, smt::Sort::Int));
|
m_z3Variables.emplace(&_varDecl, m_interface->newFunction(uniqueSymbol(_varDecl), smt::Sort::Int, smt::Sort::Int));
|
||||||
if (_setToZero)
|
setValue(_varDecl, _setToZero);
|
||||||
m_interface->addAssertion(currentValue(_varDecl) == 0);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_interface->addAssertion(currentValue(_varDecl) >= minValue(*intType));
|
|
||||||
m_interface->addAssertion(currentValue(_varDecl) <= maxValue(*intType));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
m_errorReporter.warning(
|
m_errorReporter.warning(
|
||||||
@ -460,16 +520,35 @@ bool SMTChecker::knownVariable(Declaration const& _decl)
|
|||||||
smt::Expression SMTChecker::currentValue(Declaration const& _decl)
|
smt::Expression SMTChecker::currentValue(Declaration const& _decl)
|
||||||
{
|
{
|
||||||
solAssert(m_currentSequenceCounter.count(&_decl), "");
|
solAssert(m_currentSequenceCounter.count(&_decl), "");
|
||||||
return var(_decl)(m_currentSequenceCounter.at(&_decl));
|
return valueAtSequence(_decl, m_currentSequenceCounter.at(&_decl));
|
||||||
}
|
}
|
||||||
|
|
||||||
smt::Expression SMTChecker::newValue(const Declaration& _decl)
|
smt::Expression SMTChecker::valueAtSequence(const Declaration& _decl, int _sequence)
|
||||||
|
{
|
||||||
|
return var(_decl)(_sequence);
|
||||||
|
}
|
||||||
|
|
||||||
|
smt::Expression SMTChecker::newValue(Declaration const& _decl)
|
||||||
{
|
{
|
||||||
solAssert(m_currentSequenceCounter.count(&_decl), "");
|
solAssert(m_currentSequenceCounter.count(&_decl), "");
|
||||||
m_currentSequenceCounter[&_decl]++;
|
solAssert(m_nextFreeSequenceCounter.count(&_decl), "");
|
||||||
|
m_currentSequenceCounter[&_decl] = m_nextFreeSequenceCounter[&_decl]++;
|
||||||
return currentValue(_decl);
|
return currentValue(_decl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SMTChecker::setValue(Declaration const& _decl, bool _setToZero)
|
||||||
|
{
|
||||||
|
auto const& intType = dynamic_cast<IntegerType const&>(*_decl.type());
|
||||||
|
|
||||||
|
if (_setToZero)
|
||||||
|
m_interface->addAssertion(currentValue(_decl) == 0);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_interface->addAssertion(currentValue(_decl) >= minValue(intType));
|
||||||
|
m_interface->addAssertion(currentValue(_decl) <= maxValue(intType));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
smt::Expression SMTChecker::minValue(IntegerType const& _t)
|
smt::Expression SMTChecker::minValue(IntegerType const& _t)
|
||||||
{
|
{
|
||||||
return smt::Expression(_t.minValue());
|
return smt::Expression(_t.minValue());
|
||||||
|
@ -46,6 +46,8 @@ private:
|
|||||||
virtual void endVisit(VariableDeclaration const& _node) override;
|
virtual void endVisit(VariableDeclaration const& _node) override;
|
||||||
virtual bool visit(FunctionDefinition const& _node) override;
|
virtual bool visit(FunctionDefinition const& _node) override;
|
||||||
virtual void endVisit(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 void endVisit(VariableDeclarationStatement const& _node) override;
|
virtual void endVisit(VariableDeclarationStatement const& _node) override;
|
||||||
virtual void endVisit(ExpressionStatement const& _node) override;
|
virtual void endVisit(ExpressionStatement const& _node) override;
|
||||||
virtual void endVisit(Assignment const& _node) override;
|
virtual void endVisit(Assignment const& _node) override;
|
||||||
@ -71,10 +73,23 @@ private:
|
|||||||
|
|
||||||
std::string uniqueSymbol(Declaration const& _decl);
|
std::string uniqueSymbol(Declaration const& _decl);
|
||||||
std::string uniqueSymbol(Expression const& _expr);
|
std::string uniqueSymbol(Expression const& _expr);
|
||||||
|
|
||||||
|
/// @returns true if _delc is a variable that is known at the current point, i.e.
|
||||||
|
/// has a valid sequence number
|
||||||
bool knownVariable(Declaration const& _decl);
|
bool knownVariable(Declaration const& _decl);
|
||||||
|
/// @returns an expression denoting the value of the variable declared in @a _decl
|
||||||
|
/// at the current point.
|
||||||
smt::Expression currentValue(Declaration const& _decl);
|
smt::Expression currentValue(Declaration const& _decl);
|
||||||
|
/// @returns an expression denoting the value of the variable declared in @a _decl
|
||||||
|
/// at the given sequence point. Does not ensure that this sequence point exists.
|
||||||
|
smt::Expression valueAtSequence(Declaration const& _decl, int _sequence);
|
||||||
|
/// Allocates a new sequence number for the declaration, updates the current
|
||||||
|
/// sequence number to this value and returns the expression.
|
||||||
smt::Expression newValue(Declaration const& _decl);
|
smt::Expression newValue(Declaration const& _decl);
|
||||||
|
|
||||||
|
/// Sets the value of the declaration either to zero or to its intrinsic range.
|
||||||
|
void setValue(Declaration const& _decl, bool _setToZero);
|
||||||
|
|
||||||
smt::Expression minValue(IntegerType const& _t);
|
smt::Expression minValue(IntegerType const& _t);
|
||||||
smt::Expression maxValue(IntegerType const& _t);
|
smt::Expression maxValue(IntegerType const& _t);
|
||||||
|
|
||||||
@ -87,6 +102,7 @@ private:
|
|||||||
|
|
||||||
std::shared_ptr<smt::SolverInterface> m_interface;
|
std::shared_ptr<smt::SolverInterface> m_interface;
|
||||||
std::map<Declaration const*, int> m_currentSequenceCounter;
|
std::map<Declaration const*, int> m_currentSequenceCounter;
|
||||||
|
std::map<Declaration const*, int> m_nextFreeSequenceCounter;
|
||||||
std::map<Expression const*, smt::Expression> m_z3Expressions;
|
std::map<Expression const*, smt::Expression> m_z3Expressions;
|
||||||
std::map<Declaration const*, smt::Expression> m_z3Variables;
|
std::map<Declaration const*, smt::Expression> m_z3Variables;
|
||||||
ErrorReporter& m_errorReporter;
|
ErrorReporter& m_errorReporter;
|
||||||
|
@ -61,6 +61,13 @@ public:
|
|||||||
Expression& operator=(Expression const& _other) = default;
|
Expression& operator=(Expression const& _other) = default;
|
||||||
Expression& operator=(Expression&& _other) = default;
|
Expression& operator=(Expression&& _other) = default;
|
||||||
|
|
||||||
|
static Expression ite(Expression _condition, Expression _trueValue, Expression _falseValue)
|
||||||
|
{
|
||||||
|
return Expression("ite", std::vector<Expression>{
|
||||||
|
std::move(_condition), std::move(_trueValue), std::move(_falseValue)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
friend Expression operator!(Expression _a)
|
friend Expression operator!(Expression _a)
|
||||||
{
|
{
|
||||||
return Expression("not", std::move(_a));
|
return Expression("not", std::move(_a));
|
||||||
|
@ -72,21 +72,28 @@ void Z3Interface::addAssertion(Expression const& _expr)
|
|||||||
|
|
||||||
pair<CheckResult, vector<string>> Z3Interface::check(vector<Expression> const& _expressionsToEvaluate)
|
pair<CheckResult, vector<string>> Z3Interface::check(vector<Expression> const& _expressionsToEvaluate)
|
||||||
{
|
{
|
||||||
|
// cout << "---------------------------------" << endl;
|
||||||
|
// cout << m_solver << endl;
|
||||||
CheckResult result;
|
CheckResult result;
|
||||||
switch (m_solver.check())
|
switch (m_solver.check())
|
||||||
{
|
{
|
||||||
case z3::check_result::sat:
|
case z3::check_result::sat:
|
||||||
result = CheckResult::SAT;
|
result = CheckResult::SAT;
|
||||||
|
cout << "sat" << endl;
|
||||||
break;
|
break;
|
||||||
case z3::check_result::unsat:
|
case z3::check_result::unsat:
|
||||||
result = CheckResult::UNSAT;
|
result = CheckResult::UNSAT;
|
||||||
|
cout << "unsat" << endl;
|
||||||
break;
|
break;
|
||||||
case z3::check_result::unknown:
|
case z3::check_result::unknown:
|
||||||
result = CheckResult::UNKNOWN;
|
result = CheckResult::UNKNOWN;
|
||||||
|
cout << "unknown" << endl;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
solAssert(false, "");
|
solAssert(false, "");
|
||||||
}
|
}
|
||||||
|
// cout << "---------------------------------" << endl;
|
||||||
|
|
||||||
|
|
||||||
vector<string> values;
|
vector<string> values;
|
||||||
if (result != CheckResult::UNSAT)
|
if (result != CheckResult::UNSAT)
|
||||||
@ -107,6 +114,7 @@ z3::expr Z3Interface::toZ3Expr(Expression const& _expr)
|
|||||||
arguments.push_back(toZ3Expr(arg));
|
arguments.push_back(toZ3Expr(arg));
|
||||||
|
|
||||||
static map<string, unsigned> arity{
|
static map<string, unsigned> arity{
|
||||||
|
{"ite", 3},
|
||||||
{"not", 1},
|
{"not", 1},
|
||||||
{"and", 2},
|
{"and", 2},
|
||||||
{"or", 2},
|
{"or", 2},
|
||||||
@ -135,7 +143,9 @@ z3::expr Z3Interface::toZ3Expr(Expression const& _expr)
|
|||||||
}
|
}
|
||||||
|
|
||||||
assert(arity.count(n) && arity.at(n) == arguments.size());
|
assert(arity.count(n) && arity.at(n) == arguments.size());
|
||||||
if (n == "not")
|
if (n == "ite")
|
||||||
|
return z3::ite(arguments[0], arguments[1], arguments[2]);
|
||||||
|
else if (n == "not")
|
||||||
return !arguments[0];
|
return !arguments[0];
|
||||||
else if (n == "and")
|
else if (n == "and")
|
||||||
return arguments[0] && arguments[1];
|
return arguments[0] && arguments[1];
|
||||||
|
Loading…
Reference in New Issue
Block a user