mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #10721 from blishko/smt-try-catch
[SMTChecker] Support try-catch statements
This commit is contained in:
commit
66a773aef9
@ -9,6 +9,7 @@ Compiler Features:
|
||||
* SMTChecker: Support ABI functions as uninterpreted functions.
|
||||
* SMTChecker: Use checked arithmetic by default and support ``unchecked`` blocks.
|
||||
* SMTChecker: Show contract name in counterexample function call.
|
||||
* SMTChecker: Support try/catch statements.
|
||||
|
||||
Bugfixes:
|
||||
* Code Generator: Fix length check when decoding malformed error data in catch clause.
|
||||
|
@ -345,6 +345,44 @@ bool BMC::visit(ForStatement const& _node)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BMC::visit(TryStatement const& _tryStatement)
|
||||
{
|
||||
FunctionCall const* externalCall = dynamic_cast<FunctionCall const*>(&_tryStatement.externalCall());
|
||||
solAssert(externalCall && externalCall->annotation().tryCall, "");
|
||||
|
||||
externalCall->accept(*this);
|
||||
auto callExpr = expr(*externalCall);
|
||||
if (_tryStatement.successClause()->parameters())
|
||||
tryCatchAssignment(
|
||||
_tryStatement.successClause()->parameters()->parameters(), *m_context.expression(*externalCall)
|
||||
);
|
||||
|
||||
smtutil::Expression clauseId = m_context.newVariable("clause_choice_" + to_string(m_context.newUniqueId()), smtutil::SortProvider::uintSort);
|
||||
auto const& clauses = _tryStatement.clauses();
|
||||
m_context.addAssertion(clauseId >= 0 && clauseId < clauses.size());
|
||||
solAssert(clauses[0].get() == _tryStatement.successClause(), "First clause of TryStatement should be the success clause");
|
||||
vector<set<VariableDeclaration const*>> touchedVars;
|
||||
vector<pair<VariableIndices, smtutil::Expression>> clausesVisitResults;
|
||||
for (size_t i = 0; i < clauses.size(); ++i)
|
||||
{
|
||||
clausesVisitResults.push_back(visitBranch(clauses[i].get()));
|
||||
touchedVars.push_back(touchedVariables(*clauses[i]));
|
||||
}
|
||||
|
||||
// merge the information from all clauses
|
||||
smtutil::Expression pathCondition = clausesVisitResults.front().second;
|
||||
auto currentIndices = clausesVisitResults[0].first;
|
||||
for (size_t i = 1; i < clauses.size(); ++i)
|
||||
{
|
||||
mergeVariables(touchedVars[i - 1] + touchedVars[i], clauseId == i, clausesVisitResults[i].first, currentIndices);
|
||||
currentIndices = copyVariableIndices();
|
||||
pathCondition = pathCondition || clausesVisitResults[i].second;
|
||||
}
|
||||
setPathCondition(pathCondition);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void BMC::endVisit(UnaryOperation const& _op)
|
||||
{
|
||||
SMTEncoder::endVisit(_op);
|
||||
|
@ -91,6 +91,7 @@ private:
|
||||
void endVisit(UnaryOperation const& _node) override;
|
||||
void endVisit(FunctionCall const& _node) override;
|
||||
void endVisit(Return const& _node) override;
|
||||
bool visit(TryStatement const& _node) override;
|
||||
//@}
|
||||
|
||||
/// Visitor helpers.
|
||||
|
@ -541,6 +541,48 @@ void CHC::endVisit(Return const& _return)
|
||||
m_currentBlock = predicate(*returnGhost);
|
||||
}
|
||||
|
||||
bool CHC::visit(TryStatement const& _tryStatement)
|
||||
{
|
||||
FunctionCall const* externalCall = dynamic_cast<FunctionCall const*>(&_tryStatement.externalCall());
|
||||
solAssert(externalCall && externalCall->annotation().tryCall, "");
|
||||
solAssert(m_currentFunction, "");
|
||||
|
||||
auto tryHeaderBlock = createBlock(&_tryStatement, PredicateType::FunctionBlock, "try_header_");
|
||||
auto afterTryBlock = createBlock(&m_currentFunction->body(), PredicateType::FunctionBlock);
|
||||
|
||||
auto const& clauses = _tryStatement.clauses();
|
||||
solAssert(clauses[0].get() == _tryStatement.successClause(), "First clause of TryStatement should be the success clause");
|
||||
auto clauseBlocks = applyMap(clauses, [this](ASTPointer<TryCatchClause> clause) {
|
||||
return createBlock(clause.get(), PredicateType::FunctionBlock, "try_clause_" + std::to_string(clause->id()));
|
||||
});
|
||||
|
||||
connectBlocks(m_currentBlock, predicate(*tryHeaderBlock));
|
||||
setCurrentBlock(*tryHeaderBlock);
|
||||
// Visit everything, except the actual external call.
|
||||
externalCall->expression().accept(*this);
|
||||
ASTNode::listAccept(externalCall->arguments(), *this);
|
||||
// Branch directly to all catch clauses, since in these cases, any effects of the external call are reverted.
|
||||
for (size_t i = 1; i < clauseBlocks.size(); ++i)
|
||||
connectBlocks(m_currentBlock, predicate(*clauseBlocks[i]));
|
||||
// Only now visit the actual call to record its effects and connect to the success clause.
|
||||
endVisit(*externalCall);
|
||||
if (_tryStatement.successClause()->parameters())
|
||||
tryCatchAssignment(
|
||||
_tryStatement.successClause()->parameters()->parameters(), *m_context.expression(*externalCall)
|
||||
);
|
||||
connectBlocks(m_currentBlock, predicate(*clauseBlocks[0]));
|
||||
|
||||
for (size_t i = 0; i < clauses.size(); ++i)
|
||||
{
|
||||
setCurrentBlock(*clauseBlocks[i]);
|
||||
clauses[i]->accept(*this);
|
||||
connectBlocks(m_currentBlock, predicate(*afterTryBlock));
|
||||
}
|
||||
setCurrentBlock(*afterTryBlock);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void CHC::pushInlineFrame(CallableDeclaration const& _callable)
|
||||
{
|
||||
m_returnDests.push_back(createBlock(&_callable, PredicateType::FunctionBlock, "return_"));
|
||||
|
@ -84,6 +84,7 @@ private:
|
||||
void endVisit(Continue const& _node) override;
|
||||
void endVisit(IndexRangeAccess const& _node) override;
|
||||
void endVisit(Return const& _node) override;
|
||||
bool visit(TryStatement const& _node) override;
|
||||
|
||||
void pushInlineFrame(CallableDeclaration const& _callable) override;
|
||||
void popInlineFrame(CallableDeclaration const& _callable) override;
|
||||
|
@ -312,20 +312,6 @@ bool SMTEncoder::visit(InlineAssembly const& _inlineAsm)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SMTEncoder::visit(TryCatchClause const& _clause)
|
||||
{
|
||||
if (auto params = _clause.parameters())
|
||||
for (auto const& var: params->parameters())
|
||||
createVariable(*var);
|
||||
|
||||
m_errorReporter.warning(
|
||||
7645_error,
|
||||
_clause.location(),
|
||||
"Assertion checker does not support try/catch clauses."
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
void SMTEncoder::pushInlineFrame(CallableDeclaration const&)
|
||||
{
|
||||
pushPathCondition(currentPathConditions());
|
||||
@ -2121,6 +2107,26 @@ smtutil::Expression SMTEncoder::compoundAssignment(Assignment const& _assignment
|
||||
return values.first;
|
||||
}
|
||||
|
||||
void SMTEncoder::tryCatchAssignment(vector<shared_ptr<VariableDeclaration>> const& _variables, smt::SymbolicVariable const& _rhs)
|
||||
{
|
||||
if (_variables.size() > 1)
|
||||
{
|
||||
auto const* symbTuple = dynamic_cast<smt::SymbolicTupleVariable const*>(&_rhs);
|
||||
solAssert(symbTuple, "");
|
||||
auto const& symbComponents = symbTuple->components();
|
||||
solAssert(symbComponents.size() == _variables.size(), "");
|
||||
for (unsigned i = 0; i < symbComponents.size(); ++i)
|
||||
{
|
||||
auto param = _variables.at(i);
|
||||
solAssert(param, "");
|
||||
solAssert(m_context.knownVariable(*param), "");
|
||||
assignment(*param, symbTuple->component(i));
|
||||
}
|
||||
}
|
||||
else if (_variables.size() == 1)
|
||||
assignment(*_variables.front(), _rhs.currentValue());
|
||||
}
|
||||
|
||||
void SMTEncoder::assignment(VariableDeclaration const& _variable, Expression const& _value)
|
||||
{
|
||||
// In general, at this point, the SMT sorts of _variable and _value are the same,
|
||||
@ -2688,7 +2694,29 @@ vector<VariableDeclaration const*> SMTEncoder::stateVariablesIncludingInheritedA
|
||||
|
||||
vector<VariableDeclaration const*> SMTEncoder::localVariablesIncludingModifiers(FunctionDefinition const& _function, ContractDefinition const* _contract)
|
||||
{
|
||||
return _function.localVariables() + modifiersVariables(_function, _contract);
|
||||
return _function.localVariables() + tryCatchVariables(_function) + modifiersVariables(_function, _contract);
|
||||
}
|
||||
|
||||
vector<VariableDeclaration const*> SMTEncoder::tryCatchVariables(FunctionDefinition const& _function)
|
||||
{
|
||||
struct TryCatchVarsVisitor : public ASTConstVisitor
|
||||
{
|
||||
bool visit(TryCatchClause const& _catchClause) override
|
||||
{
|
||||
if (_catchClause.parameters())
|
||||
{
|
||||
auto const& params = _catchClause.parameters()->parameters();
|
||||
for (auto param: params)
|
||||
vars.push_back(param.get());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
vector<VariableDeclaration const*> vars;
|
||||
} tryCatchVarsVisitor;
|
||||
_function.accept(tryCatchVarsVisitor);
|
||||
return tryCatchVarsVisitor.vars;
|
||||
}
|
||||
|
||||
vector<VariableDeclaration const*> SMTEncoder::modifiersVariables(FunctionDefinition const& _function, ContractDefinition const* _contract)
|
||||
|
@ -76,6 +76,7 @@ public:
|
||||
|
||||
static std::vector<VariableDeclaration const*> localVariablesIncludingModifiers(FunctionDefinition const& _function, ContractDefinition const* _contract);
|
||||
static std::vector<VariableDeclaration const*> modifiersVariables(FunctionDefinition const& _function, ContractDefinition const* _contract);
|
||||
static std::vector<VariableDeclaration const*> tryCatchVariables(FunctionDefinition const& _function);
|
||||
|
||||
/// @returns the ModifierDefinition of a ModifierInvocation if possible, or nullptr.
|
||||
static ModifierDefinition const* resolveModifierInvocation(ModifierInvocation const& _invocation, ContractDefinition const* _contract);
|
||||
@ -130,7 +131,7 @@ protected:
|
||||
bool visit(InlineAssembly const& _node) override;
|
||||
void endVisit(Break const&) override {}
|
||||
void endVisit(Continue const&) override {}
|
||||
bool visit(TryCatchClause const& _node) override;
|
||||
bool visit(TryStatement const&) override { return false; }
|
||||
|
||||
virtual void pushInlineFrame(CallableDeclaration const&);
|
||||
virtual void popInlineFrame(CallableDeclaration const&);
|
||||
@ -237,6 +238,8 @@ protected:
|
||||
void tupleAssignment(Expression const& _left, Expression const& _right);
|
||||
/// Computes the right hand side of a compound assignment.
|
||||
smtutil::Expression compoundAssignment(Assignment const& _assignment);
|
||||
/// Handles assignment of the result of external call in try statement to the parameters of success clause
|
||||
void tryCatchAssignment(std::vector<std::shared_ptr<VariableDeclaration>> const& _variables, smt::SymbolicVariable const& rhs);
|
||||
|
||||
/// Maps a variable to an SSA index.
|
||||
using VariableIndices = std::unordered_map<VariableDeclaration const*, int>;
|
||||
|
@ -0,0 +1,24 @@
|
||||
pragma experimental SMTChecker;
|
||||
contract C {
|
||||
|
||||
function g() public pure {}
|
||||
|
||||
function f() public view {
|
||||
uint x = 0;
|
||||
bool success = false;
|
||||
try this.g() {
|
||||
success = true;
|
||||
x = 1;
|
||||
} catch Error (string memory /*reason*/) {
|
||||
x = 2;
|
||||
} catch (bytes memory /*reason*/) {
|
||||
x = 3;
|
||||
}
|
||||
assert(x > 0 && x < 4); // should hold
|
||||
assert(x == 0); // should fail
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// SMTEngine: bmc
|
||||
// ----
|
||||
// Warning 4661: (338-352): BMC: Assertion violation happens here.
|
@ -0,0 +1,28 @@
|
||||
pragma experimental SMTChecker;
|
||||
contract C {
|
||||
|
||||
struct S {
|
||||
uint x;
|
||||
int y;
|
||||
}
|
||||
|
||||
function g() public pure returns (bool b, S memory s) {
|
||||
b = true;
|
||||
s.x = 42;
|
||||
s.y = -1;
|
||||
}
|
||||
|
||||
function f() public view {
|
||||
bool success = false;
|
||||
try this.g() returns (bool b, S memory s) {
|
||||
success = true;
|
||||
assert(b && s.x == 42 && s.y == -1); // should hold
|
||||
} catch {
|
||||
}
|
||||
assert(success); // fails, not guaranteed that there will be no error
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// SMTEngine: bmc
|
||||
// ----
|
||||
// Warning 4661: (368-383): BMC: Assertion violation happens here.
|
@ -9,7 +9,3 @@ contract C {
|
||||
// EVMVersion: >=byzantium
|
||||
// ----
|
||||
// Warning 6321: (75-79): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable.
|
||||
// Warning 7645: (98-121): Assertion checker does not support try/catch clauses.
|
||||
// Warning 7645: (124-159): Assertion checker does not support try/catch clauses.
|
||||
// Warning 7645: (98-121): Assertion checker does not support try/catch clauses.
|
||||
// Warning 7645: (124-159): Assertion checker does not support try/catch clauses.
|
||||
|
@ -10,7 +10,3 @@ contract C {
|
||||
// ====
|
||||
// EVMVersion: >=byzantium
|
||||
// ----
|
||||
// Warning 7645: (83-85): Assertion checker does not support try/catch clauses.
|
||||
// Warning 7645: (88-122): Assertion checker does not support try/catch clauses.
|
||||
// Warning 7645: (83-85): Assertion checker does not support try/catch clauses.
|
||||
// Warning 7645: (88-122): Assertion checker does not support try/catch clauses.
|
||||
|
22
test/libsolidity/smtCheckerTests/try_catch/try_1.sol
Normal file
22
test/libsolidity/smtCheckerTests/try_catch/try_1.sol
Normal file
@ -0,0 +1,22 @@
|
||||
pragma experimental SMTChecker;
|
||||
contract C {
|
||||
int x;
|
||||
|
||||
function g() public {
|
||||
x = 42;
|
||||
}
|
||||
|
||||
function f() public {
|
||||
x = 0;
|
||||
bool success = false;
|
||||
try this.g() {
|
||||
success = true;
|
||||
} catch (bytes memory s) {
|
||||
assert(x == 0); // should hold
|
||||
}
|
||||
assert(success); // fails for now, since external call is over-approximated (both success and fail are considered possible) for now even for known code
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// Warning 5667: (195-209): Unused try/catch parameter. Remove or comment out the variable name to silence this warning.
|
||||
// Warning 6328: (253-268): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\n\n\n\nTransaction trace:\nC.constructor()\nState: x = 0\nC.f()
|
19
test/libsolidity/smtCheckerTests/try_catch/try_2.sol
Normal file
19
test/libsolidity/smtCheckerTests/try_catch/try_2.sol
Normal file
@ -0,0 +1,19 @@
|
||||
pragma experimental SMTChecker;
|
||||
contract C {
|
||||
int x;
|
||||
|
||||
function g() public {
|
||||
x = 42;
|
||||
}
|
||||
|
||||
function f() public {
|
||||
x = 0;
|
||||
try this.g() {
|
||||
assert(x == 42); // should hold
|
||||
} catch (bytes memory s) {
|
||||
assert(x == 0); // should hold
|
||||
}
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// Warning 5667: (187-201): Unused try/catch parameter. Remove or comment out the variable name to silence this warning.
|
25
test/libsolidity/smtCheckerTests/try_catch/try_3.sol
Normal file
25
test/libsolidity/smtCheckerTests/try_catch/try_3.sol
Normal file
@ -0,0 +1,25 @@
|
||||
pragma experimental SMTChecker;
|
||||
contract C {
|
||||
int x;
|
||||
|
||||
function g(int y) public pure returns (int) {
|
||||
return y;
|
||||
}
|
||||
|
||||
function postinc() internal returns (int) {
|
||||
x += 1;
|
||||
return x;
|
||||
}
|
||||
|
||||
function f() public {
|
||||
x = 0;
|
||||
try this.g(postinc()) {
|
||||
assert(x == 1); // should hold
|
||||
} catch (bytes memory s) {
|
||||
assert(x == 0); // should fail - state is reverted to the state after postinc(), but before the call to this.g()
|
||||
}
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// Warning 5667: (291-305): Unused try/catch parameter. Remove or comment out the variable name to silence this warning.
|
||||
// Warning 6328: (312-326): CHC: Assertion violation happens here.\nCounterexample:\nx = 1\n\n\n\nTransaction trace:\nC.constructor()\nState: x = 0\nC.f()
|
30
test/libsolidity/smtCheckerTests/try_catch/try_4.sol
Normal file
30
test/libsolidity/smtCheckerTests/try_catch/try_4.sol
Normal file
@ -0,0 +1,30 @@
|
||||
pragma experimental SMTChecker;
|
||||
|
||||
abstract contract D {
|
||||
function d() external virtual;
|
||||
}
|
||||
|
||||
contract C {
|
||||
|
||||
int x;
|
||||
D d;
|
||||
|
||||
function set(int n) public {
|
||||
x = n;
|
||||
}
|
||||
|
||||
function f() public {
|
||||
x = 0;
|
||||
try d.d() {
|
||||
assert(x == 0); // should fail, x can be anything here
|
||||
} catch {
|
||||
assert(x == 0); // should hold, all changes to x has been reverted
|
||||
assert(x == 1); // should fail
|
||||
}
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// SMTIgnoreCex: yes
|
||||
// ----
|
||||
// Warning 6328: (211-225): CHC: Assertion violation happens here.
|
||||
// Warning 6328: (351-365): CHC: Assertion violation happens here.
|
28
test/libsolidity/smtCheckerTests/try_catch/try_5.sol
Normal file
28
test/libsolidity/smtCheckerTests/try_catch/try_5.sol
Normal file
@ -0,0 +1,28 @@
|
||||
pragma experimental SMTChecker;
|
||||
|
||||
abstract contract D {
|
||||
function d() external virtual;
|
||||
}
|
||||
|
||||
contract C {
|
||||
|
||||
int x;
|
||||
D d;
|
||||
|
||||
function set(int n) public {
|
||||
require(n < 100);
|
||||
x = n;
|
||||
}
|
||||
|
||||
function f() public {
|
||||
x = 0;
|
||||
try d.d() {
|
||||
assert(x < 100); // should hold
|
||||
} catch {
|
||||
assert(x == 0); // should hold, all changes to x has been reverted
|
||||
assert(x == 1); // should fail
|
||||
}
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// Warning 6328: (348-362): CHC: Assertion violation happens here.\nCounterexample:\nx = 0, d = 0\n\n\n\nTransaction trace:\nC.constructor()\nState: x = 0, d = 0\nC.f()
|
14
test/libsolidity/smtCheckerTests/try_catch/try_inside_if.sol
Normal file
14
test/libsolidity/smtCheckerTests/try_catch/try_inside_if.sol
Normal file
@ -0,0 +1,14 @@
|
||||
pragma experimental SMTChecker;
|
||||
contract C {
|
||||
function g(bool b) public {}
|
||||
function f(bool b) public returns (bytes memory txt) {
|
||||
if (0==1)
|
||||
try this.g(b) {}
|
||||
catch (bytes memory s) {
|
||||
txt = s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ----
|
||||
// Warning 6838: (141-145): BMC: Condition is always false.
|
@ -0,0 +1,13 @@
|
||||
pragma experimental SMTChecker;
|
||||
contract C {
|
||||
function f() public returns (uint) {
|
||||
while(1==1)
|
||||
try this.f() returns (uint b) {
|
||||
b = 2;
|
||||
} catch {
|
||||
}
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// Warning 6321: (75-79): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable.
|
||||
// Warning 6838: (91-95): BMC: Condition is always true.
|
@ -0,0 +1,25 @@
|
||||
pragma experimental SMTChecker;
|
||||
contract C {
|
||||
int x;
|
||||
|
||||
function g() public {
|
||||
x = 42;
|
||||
}
|
||||
|
||||
function f() public {
|
||||
x = 1;
|
||||
bool success = false;
|
||||
try this.g() {
|
||||
success = true;
|
||||
assert(x == 42); // should hold
|
||||
} catch Error (string memory /*reason*/) {
|
||||
assert(x == 1); // should hold
|
||||
} catch (bytes memory /*reason*/) {
|
||||
assert(x == 1); // should hold
|
||||
}
|
||||
assert((success && x == 42) || (!success && x == 1)); // should hold
|
||||
assert(success); // can fail
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// Warning 6328: (447-462): CHC: Assertion violation happens here.\nCounterexample:\nx = 1\n\n\n\nTransaction trace:\nC.constructor()\nState: x = 0\nC.f()
|
@ -0,0 +1,22 @@
|
||||
pragma experimental SMTChecker;
|
||||
contract C {
|
||||
|
||||
function g() public pure {}
|
||||
|
||||
function f() public view {
|
||||
uint x = 0;
|
||||
bool success = false;
|
||||
try this.g() {
|
||||
success = true;
|
||||
x = 1;
|
||||
} catch Error (string memory /*reason*/) {
|
||||
x = 2;
|
||||
} catch (bytes memory /*reason*/) {
|
||||
x = 3;
|
||||
}
|
||||
assert(x > 0 && x < 4); // should hold
|
||||
assert(x == 0); // should fail
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// Warning 6328: (338-352): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nC.constructor()\nC.f()
|
@ -0,0 +1,27 @@
|
||||
pragma experimental SMTChecker;
|
||||
|
||||
abstract contract D {
|
||||
function d() external virtual returns (uint x, bool b);
|
||||
}
|
||||
|
||||
contract C {
|
||||
|
||||
int x;
|
||||
D d;
|
||||
|
||||
function f() public {
|
||||
x = 0;
|
||||
try d.d() returns (uint x, bool c) {
|
||||
assert(x == 0); // should fail, x is the local variable shadowing the state variable
|
||||
assert(!c); // should fail, c can be anything
|
||||
} catch {
|
||||
assert(x == 0); // should hold, x is the state variable
|
||||
assert(x == 1); // should fail
|
||||
}
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// Warning 2519: (197-203): This declaration shadows an existing declaration.
|
||||
// Warning 6328: (218-232): CHC: Assertion violation happens here.\nCounterexample:\nx = 0, d = 0\n\n\n\nTransaction trace:\nC.constructor()\nState: x = 0, d = 0\nC.f()
|
||||
// Warning 6328: (306-316): CHC: Assertion violation happens here.\nCounterexample:\nx = 0, d = 0\n\n\n\nTransaction trace:\nC.constructor()\nState: x = 0, d = 0\nC.f()
|
||||
// Warning 6328: (426-440): CHC: Assertion violation happens here.\nCounterexample:\nx = 0, d = 0\n\n\n\nTransaction trace:\nC.constructor()\nState: x = 0, d = 0\nC.f()
|
@ -0,0 +1,26 @@
|
||||
pragma experimental SMTChecker;
|
||||
contract C {
|
||||
|
||||
struct S {
|
||||
uint x;
|
||||
int y;
|
||||
}
|
||||
|
||||
function g() public pure returns (bool b, S memory s) {
|
||||
b = true;
|
||||
s.x = 42;
|
||||
s.y = -1;
|
||||
}
|
||||
|
||||
function f() public view {
|
||||
bool success = false;
|
||||
try this.g() returns (bool b, S memory s) {
|
||||
success = true;
|
||||
assert(b && s.x == 42 && s.y == -1);
|
||||
} catch {
|
||||
}
|
||||
assert(success); // fails, not guaranteed that there will be no error
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// Warning 6328: (353-368): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nC.constructor()\nC.f()
|
34
test/libsolidity/smtCheckerTests/try_catch/try_new.sol
Normal file
34
test/libsolidity/smtCheckerTests/try_catch/try_new.sol
Normal file
@ -0,0 +1,34 @@
|
||||
pragma experimental SMTChecker;
|
||||
|
||||
contract Reverts {
|
||||
constructor(uint) { revert("test message."); }
|
||||
}
|
||||
contract Succeeds {
|
||||
constructor(uint) { }
|
||||
}
|
||||
|
||||
contract C {
|
||||
function f() public returns (Reverts x, string memory txt) {
|
||||
uint i = 3;
|
||||
try new Reverts(i) returns (Reverts r) {
|
||||
x = r;
|
||||
txt = "success";
|
||||
} catch Error(string memory s) {
|
||||
txt = s;
|
||||
}
|
||||
}
|
||||
function g() public returns (Succeeds x, string memory txt) {
|
||||
uint i = 8;
|
||||
try new Succeeds(i) returns (Succeeds r) {
|
||||
x = r;
|
||||
txt = "success";
|
||||
} catch Error(string memory s) {
|
||||
txt = s;
|
||||
}
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// Warning 4588: (264-278): Assertion checker does not yet implement this type of function call.
|
||||
// Warning 4588: (525-540): Assertion checker does not yet implement this type of function call.
|
||||
// Warning 4588: (264-278): Assertion checker does not yet implement this type of function call.
|
||||
// Warning 4588: (525-540): Assertion checker does not yet implement this type of function call.
|
@ -0,0 +1,14 @@
|
||||
pragma experimental SMTChecker;
|
||||
contract C {
|
||||
int public x;
|
||||
|
||||
function f() public view {
|
||||
try this.x() returns (int v) {
|
||||
assert(x == v); // should hold
|
||||
} catch {
|
||||
assert(false); // this fails, because we over-approximate every external call in the way that it can both succeed and fail
|
||||
}
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// Warning 6328: (171-184): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\n\n\n\nTransaction trace:\nC.constructor()\nState: x = 0\nC.f()
|
@ -0,0 +1,23 @@
|
||||
pragma experimental SMTChecker;
|
||||
|
||||
contract C {
|
||||
mapping (uint => uint[]) public m;
|
||||
|
||||
constructor() {
|
||||
m[0].push();
|
||||
m[0].push();
|
||||
m[0][1] = 42;
|
||||
}
|
||||
|
||||
function f() public view {
|
||||
try this.m(0,1) returns (uint y) {
|
||||
assert(y == m[0][1]); // should hold
|
||||
}
|
||||
catch {
|
||||
assert(m[0][1] == 42); // should hold
|
||||
assert(m[0][1] == 1); // should fail
|
||||
}
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// Warning 6328: (313-333): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nC.constructor()\nC.f()
|
@ -0,0 +1,17 @@
|
||||
pragma experimental SMTChecker;
|
||||
contract C {
|
||||
|
||||
function g() public pure returns (bytes memory) {
|
||||
return hex"ffff";
|
||||
}
|
||||
|
||||
function f() public view {
|
||||
try this.g() returns (bytes memory b) {
|
||||
assert(b[0] == bytes1(uint8(255)) && b[1] == bytes1(uint8(255))); // should hold
|
||||
assert(b[0] == bytes1(uint8(0)) || b[1] == bytes1(uint8(0))); // should fail
|
||||
} catch {
|
||||
}
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// Warning 6328: (278-338): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nC.constructor()\nC.f()
|
@ -0,0 +1,17 @@
|
||||
pragma experimental SMTChecker;
|
||||
contract C {
|
||||
|
||||
function g() public pure returns (bytes2) {
|
||||
return hex"ffff";
|
||||
}
|
||||
|
||||
function f() public view {
|
||||
try this.g() returns (bytes2 b) {
|
||||
assert(uint8(b[0]) == 255 && uint8(b[1]) == 255); // should hold
|
||||
assert(uint8(b[0]) == 0 || uint8(b[1]) == 0); // should fail
|
||||
} catch {
|
||||
}
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// Warning 6328: (250-294): CHC: Assertion violation happens here.
|
Loading…
Reference in New Issue
Block a user