Skip to content

Commit e99efec

Browse files
author
Leonardo
authored
Merge pull request ethereum#6652 from ethereum/smt_tuple_function
[SMTChecker] Support tuples as function calls with multiple return values
2 parents 442742e + 5440a53 commit e99efec

File tree

8 files changed

+96
-32
lines changed

8 files changed

+96
-32
lines changed

Changelog.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Language Features:
66

77
Compiler Features:
88
* SMTChecker: Support inherited state variables.
9-
* SMTChecker: Support tuple assignments.
9+
* SMTChecker: Support tuple assignments and function calls with multiple return values.
1010

1111

1212
Bugfixes:

libsolidity/formal/SMTChecker.cpp

+37-27
Original file line numberDiff line numberDiff line change
@@ -705,8 +705,8 @@ void SMTChecker::visitGasLeft(FunctionCall const& _funCall)
705705

706706
void SMTChecker::inlineFunctionCall(FunctionCall const& _funCall)
707707
{
708-
FunctionDefinition const* _funDef = inlinedFunctionCallToDefinition(_funCall);
709-
if (!_funDef)
708+
FunctionDefinition const* funDef = inlinedFunctionCallToDefinition(_funCall);
709+
if (!funDef)
710710
{
711711
m_errorReporter.warning(
712712
_funCall.location(),
@@ -715,39 +715,48 @@ void SMTChecker::inlineFunctionCall(FunctionCall const& _funCall)
715715
return;
716716
}
717717

718-
if (visitedFunction(_funDef))
718+
if (visitedFunction(funDef))
719719
m_errorReporter.warning(
720720
_funCall.location(),
721721
"Assertion checker does not support recursive function calls.",
722-
SecondarySourceLocation().append("Starting from function:", _funDef->location())
722+
SecondarySourceLocation().append("Starting from function:", funDef->location())
723723
);
724724
else
725725
{
726726
vector<smt::Expression> funArgs;
727727
Expression const* calledExpr = &_funCall.expression();
728728
auto const& funType = dynamic_cast<FunctionType const*>(calledExpr->annotation().type);
729729
solAssert(funType, "");
730+
730731
if (funType->bound())
731732
{
732733
auto const& boundFunction = dynamic_cast<MemberAccess const*>(calledExpr);
733734
solAssert(boundFunction, "");
734735
funArgs.push_back(expr(boundFunction->expression()));
735736
}
737+
736738
for (auto arg: _funCall.arguments())
737739
funArgs.push_back(expr(*arg));
738-
initializeFunctionCallParameters(*_funDef, funArgs);
739-
_funDef->accept(*this);
740-
auto const& returnParams = _funDef->returnParameters();
741-
if (_funDef->returnParameters().size())
740+
initializeFunctionCallParameters(*funDef, funArgs);
741+
742+
funDef->accept(*this);
743+
744+
createExpr(_funCall);
745+
auto const& returnParams = funDef->returnParameters();
746+
if (returnParams.size() > 1)
742747
{
743-
if (returnParams.size() > 1)
744-
m_errorReporter.warning(
745-
_funCall.location(),
746-
"Assertion checker does not yet support calls to functions that return more than one value."
747-
);
748-
else
749-
defineExpr(_funCall, currentValue(*returnParams[0]));
748+
vector<shared_ptr<SymbolicVariable>> components;
749+
for (auto param: returnParams)
750+
{
751+
solAssert(m_variables[param.get()], "");
752+
components.push_back(m_variables[param.get()]);
753+
}
754+
auto const& symbTuple = dynamic_pointer_cast<SymbolicTupleVariable>(m_expressions[&_funCall]);
755+
solAssert(symbTuple, "");
756+
symbTuple->setComponents(move(components));
750757
}
758+
else if (returnParams.size() == 1)
759+
defineExpr(_funCall, currentValue(*returnParams.front()));
751760
}
752761
}
753762

@@ -829,15 +838,11 @@ void SMTChecker::visitTypeConversion(FunctionCall const& _funCall)
829838
void SMTChecker::visitFunctionIdentifier(Identifier const& _identifier)
830839
{
831840
auto const& fType = dynamic_cast<FunctionType const&>(*_identifier.annotation().type);
832-
if (fType.returnParameterTypes().size() > 1)
841+
if (fType.returnParameterTypes().size() == 1)
833842
{
834-
m_errorReporter.warning(
835-
_identifier.location(),
836-
"Assertion checker does not yet support functions with more than one return parameter."
837-
);
843+
defineGlobalFunction(fType.richIdentifier(), _identifier);
844+
m_expressions.emplace(&_identifier, m_globalContext.at(fType.richIdentifier()));
838845
}
839-
defineGlobalFunction(fType.richIdentifier(), _identifier);
840-
m_expressions.emplace(&_identifier, m_globalContext.at(fType.richIdentifier()));
841846
}
842847

843848
void SMTChecker::endVisit(Literal const& _literal)
@@ -873,12 +878,17 @@ void SMTChecker::endVisit(Return const& _return)
873878
{
874879
auto returnParams = m_functionPath.back()->returnParameters();
875880
if (returnParams.size() > 1)
876-
m_errorReporter.warning(
877-
_return.location(),
878-
"Assertion checker does not yet support more than one return value."
879-
);
881+
{
882+
auto tuple = dynamic_cast<TupleExpression const*>(_return.expression());
883+
solAssert(tuple, "");
884+
auto const& components = tuple->components();
885+
solAssert(components.size() == returnParams.size(), "");
886+
for (unsigned i = 0; i < returnParams.size(); ++i)
887+
if (components.at(i))
888+
m_interface->addAssertion(expr(*components.at(i)) == newValue(*returnParams.at(i)));
889+
}
880890
else if (returnParams.size() == 1)
881-
m_interface->addAssertion(expr(*_return.expression()) == newValue(*returnParams[0]));
891+
m_interface->addAssertion(expr(*_return.expression()) == newValue(*returnParams.front()));
882892
}
883893
}
884894

test/libsolidity/smtCheckerTests/types/tuple_assignment.sol

+1
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ contract C
1010
assert(y == 4);
1111
}
1212
}
13+
// ----
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
pragma experimental SMTChecker;
2+
3+
contract C
4+
{
5+
function f() internal pure returns (uint, uint) {
6+
return (2, 3);
7+
}
8+
function g() public pure {
9+
uint x;
10+
uint y;
11+
(x,y) = f();
12+
assert(x == 1);
13+
assert(y == 4);
14+
}
15+
}
16+
// ----
17+
// Warning: (182-196): Assertion violation happens here
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
pragma experimental SMTChecker;
2+
3+
contract C
4+
{
5+
function f() internal pure returns (uint, uint) {
6+
return (2, 3);
7+
}
8+
function g() public pure {
9+
uint x;
10+
uint y;
11+
(x,) = f();
12+
assert(x == 2);
13+
assert(y == 4);
14+
}
15+
}
16+
// ----
17+
// Warning: (199-213): Assertion violation happens here
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
pragma experimental SMTChecker;
2+
3+
contract C
4+
{
5+
function f() internal pure returns (uint, bool, uint) {
6+
return (2, false, 3);
7+
}
8+
function g() public pure {
9+
uint x;
10+
uint y;
11+
bool b;
12+
(,b,) = f();
13+
assert(x == 2);
14+
assert(y == 4);
15+
assert(!b);
16+
}
17+
}
18+
// ----
19+
// Warning: (205-219): Assertion violation happens here

test/libsolidity/smtCheckerTestsJSON/multi.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
{
44
"smtlib2responses":
55
{
6-
"0x47a038dd9021ecb218726ea6bf1f75c215a50b1981bae4341e89c9f2b7ac5db7": "sat\n((|EVALEXPR_0| 1))\n",
7-
"0xf057b272f2ceb99a2f714cb132960babdeedfb84ff8ffb96106a58bc0c2060cb": "sat\n((|EVALEXPR_0| 0))\n",
8-
"0xf49b9d0eb7b6d2f2ac9e1604288e52ee1a08cda57058e26d7843ed109ca6d7c9": "unsat\n"
6+
"0x092d52dc5c2b54c1909592f7b3c8efedfd87afc0223ce421a24a1cc7905006b4": "sat\n((|EVALEXPR_0| 1))\n",
7+
"0x8faacfc008b6f2278b5927ff22d76832956dfb46b3c21a64fab96583c241b88f": "unsat\n",
8+
"0xa66d08de30c873ca7d0e7e9e426f278640e0ee463a1aed2e4e80baee916b6869": "sat\n((|EVALEXPR_0| 0))\n"
99
}
1010
}
1111
}

test/libsolidity/smtCheckerTestsJSON/simple.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
{
44
"smtlib2responses":
55
{
6-
"0xf057b272f2ceb99a2f714cb132960babdeedfb84ff8ffb96106a58bc0c2060cb": "sat\n((|EVALEXPR_0| 0))\n"
6+
"0xa66d08de30c873ca7d0e7e9e426f278640e0ee463a1aed2e4e80baee916b6869": "sat\n((|EVALEXPR_0| 0))\n"
77
}
88
}
99
}

0 commit comments

Comments
 (0)