Skip to content

Commit 80e85b7

Browse files
author
Leonardo Alt
committed
[SMTChecker] Apply const eval to arithmetic binary expressions
1 parent e347545 commit 80e85b7

File tree

6 files changed

+67
-13
lines changed

6 files changed

+67
-13
lines changed

Diff for: Changelog.md

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ Language Features:
1111
Compiler Features:
1212
* Build System: Optionally support dynamic loading of Z3 and use that mechanism for Linux release builds.
1313
* Code Generator: Avoid memory allocation for default value if it is not used.
14+
* SMTChecker: Apply constant evaluation on binary arithmetic expressions.
1415
* SMTChecker: Create underflow and overflow verification targets for increment/decrement in the CHC engine.
1516
* SMTChecker: Report struct values in counterexamples from CHC engine.
1617
* SMTChecker: Support early returns in the CHC engine.

Diff for: libsolidity/formal/SMTEncoder.cpp

+31-12
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
#include <libsolidity/formal/SymbolicState.h>
2323
#include <libsolidity/formal/SymbolicTypes.h>
2424

25+
#include <libsolidity/analysis/ConstantEvaluator.h>
26+
2527
#include <libsmtutil/SMTPortfolio.h>
2628
#include <libsmtutil/Helpers.h>
2729

@@ -600,7 +602,8 @@ bool SMTEncoder::visit(BinaryOperation const& _op)
600602

601603
void SMTEncoder::endVisit(BinaryOperation const& _op)
602604
{
603-
if (_op.annotation().type->category() == Type::Category::RationalNumber)
605+
/// If _op is const evaluated the RationalNumber shortcut was taken.
606+
if (isConstant(_op))
604607
return;
605608
if (TokenTraits::isBooleanOp(_op.getOperator()))
606609
return;
@@ -1628,17 +1631,17 @@ void SMTEncoder::defineGlobalVariable(string const& _name, Expression const& _ex
16281631

16291632
bool SMTEncoder::shortcutRationalNumber(Expression const& _expr)
16301633
{
1631-
if (_expr.annotation().type->category() == Type::Category::RationalNumber)
1632-
{
1633-
auto rationalType = dynamic_cast<RationalNumberType const*>(_expr.annotation().type);
1634-
solAssert(rationalType, "");
1635-
if (rationalType->isNegative())
1636-
defineExpr(_expr, smtutil::Expression(u2s(rationalType->literalValue(nullptr))));
1637-
else
1638-
defineExpr(_expr, smtutil::Expression(rationalType->literalValue(nullptr)));
1639-
return true;
1640-
}
1641-
return false;
1634+
auto type = isConstant(_expr);
1635+
if (!type)
1636+
return false;
1637+
1638+
solAssert(type->category() == Type::Category::RationalNumber, "");
1639+
auto const& rationalType = dynamic_cast<RationalNumberType const&>(*type);
1640+
if (rationalType.isNegative())
1641+
defineExpr(_expr, smtutil::Expression(u2s(rationalType.literalValue(nullptr))));
1642+
else
1643+
defineExpr(_expr, smtutil::Expression(rationalType.literalValue(nullptr)));
1644+
return true;
16421645
}
16431646

16441647
void SMTEncoder::arithmeticOperation(BinaryOperation const& _op)
@@ -2660,6 +2663,22 @@ map<ContractDefinition const*, vector<ASTPointer<frontend::Expression>>> SMTEnco
26602663
return baseArgs;
26612664
}
26622665

2666+
TypePointer SMTEncoder::isConstant(Expression const& _expr)
2667+
{
2668+
if (
2669+
auto type = _expr.annotation().type;
2670+
type->category() == Type::Category::RationalNumber
2671+
)
2672+
return type;
2673+
2674+
// _expr may not be constant evaluable.
2675+
// In that case we ignore any warnings emitted by the constant evaluator,
2676+
// as it will return nullptr in case of failure.
2677+
ErrorList l;
2678+
ErrorReporter e(l);
2679+
return ConstantEvaluator(e).evaluate(_expr);
2680+
}
2681+
26632682
void SMTEncoder::createReturnedExpressions(FunctionCall const& _funCall)
26642683
{
26652684
FunctionDefinition const* funDef = functionCallToDefinition(_funCall);

Diff for: libsolidity/formal/SMTEncoder.h

+4
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,10 @@ class SMTEncoder: public ASTConstVisitor
8383
/// @returns the arguments for each base constructor call in the hierarchy of @a _contract.
8484
std::map<ContractDefinition const*, std::vector<ASTPointer<frontend::Expression>>> baseArguments(ContractDefinition const& _contract);
8585

86+
/// @returns a valid RationalNumberType pointer if _expr has type
87+
/// RationalNumberType or can be const evaluated, and nullptr otherwise.
88+
static TypePointer isConstant(Expression const& _expr);
89+
8690
protected:
8791
// TODO: Check that we do not have concurrent reads and writes to a variable,
8892
// because the order of expression evaluation is undefined

Diff for: test/libsolidity/smtCheckerTests/complex/slither/const_state_variables.sol

+3-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,9 @@ contract MyConc{
5050
}
5151

5252
}
53+
// ====
54+
// SMTIgnoreCex: yes
5355
// ----
5456
// Warning 2519: (773-792): This declaration shadows an existing declaration.
5557
// Warning 2018: (1009-1086): Function state mutability can be restricted to view
56-
// Warning 4984: (985-1002): CHC: Overflow (resulting value larger than 2**256 - 1) happens here.\nCounterexample:\nA = 1, should_be_constant = 0, should_be_constant_2 = 2, not_constant = 0, not_constant_2 = 115792089237316195423570985008687907853269984665640564039457584007913129639926, not_constant_3 = 0\n\nTransaction trace:\nconstructor()
58+
// Warning 4984: (985-1002): CHC: Overflow (resulting value larger than 2**256 - 1) happens here.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
pragma experimental SMTChecker;
2+
3+
contract C {
4+
uint constant x = 2;
5+
uint constant y = x ** 10;
6+
7+
function f() public view {
8+
assert(y == 2 ** 10);
9+
assert(y == 1024);
10+
assert(y == 14); // should fail
11+
}
12+
}
13+
// ----
14+
// Warning 2018: (98-206): Function state mutability can be restricted to pure
15+
// Warning 6328: (172-187): CHC: Assertion violation happens here.\nCounterexample:\nx = 2, y = 1024\n\n\n\nTransaction trace:\nconstructor()\nState: x = 2, y = 1024\nf()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
pragma experimental SMTChecker;
2+
3+
contract C {
4+
uint constant DEPOSIT_CONTRACT_TREE_DEPTH = 32;
5+
uint constant MAX_DEPOSIT_COUNT = 2**DEPOSIT_CONTRACT_TREE_DEPTH - 1;
6+
function f() public pure {
7+
assert(DEPOSIT_CONTRACT_TREE_DEPTH == 32);
8+
assert(MAX_DEPOSIT_COUNT == 4294967295);
9+
assert(MAX_DEPOSIT_COUNT == 2); // should fail
10+
}
11+
}
12+
// ----
13+
// Warning 6328: (284-314): CHC: Assertion violation happens here.\nCounterexample:\nDEPOSIT_CONTRACT_TREE_DEPTH = 32, MAX_DEPOSIT_COUNT = 4294967295\n\n\n\nTransaction trace:\nconstructor()\nState: DEPOSIT_CONTRACT_TREE_DEPTH = 32, MAX_DEPOSIT_COUNT = 4294967295\nf()

0 commit comments

Comments
 (0)