[SMTChecker] Support named arguments in function calls

This commit is contained in:
Leonardo Alt 2020-11-09 13:09:34 +00:00
parent 8d315ee130
commit e4339b0526
12 changed files with 120 additions and 41 deletions

View File

@ -1,5 +1,8 @@
### 0.7.6 (unreleased)
Compiler Features:
* SMTChecker: Support named arguments in function calls.
### 0.7.5 (2020-11-18)

View File

@ -757,6 +757,44 @@ FunctionCallAnnotation& FunctionCall::annotation() const
return initAnnotation<FunctionCallAnnotation>();
}
vector<ASTPointer<Expression const>> FunctionCall::sortedArguments() const
{
// normal arguments
if (m_names.empty())
return arguments();
// named arguments
FunctionTypePointer functionType;
if (*annotation().kind == FunctionCallKind::StructConstructorCall)
{
auto const& type = dynamic_cast<TypeType const&>(*m_expression->annotation().type);
auto const& structType = dynamic_cast<StructType const&>(*type.actualType());
functionType = structType.constructorType();
}
else
functionType = dynamic_cast<FunctionType const*>(m_expression->annotation().type);
vector<ASTPointer<Expression const>> sorted;
for (auto const& parameterName: functionType->parameterNames())
{
bool found = false;
for (size_t j = 0; j < m_names.size() && !found; j++)
if ((found = (parameterName == *m_names.at(j))))
// we found the actual parameter position
sorted.push_back(m_arguments.at(j));
solAssert(found, "");
}
if (!functionType->takesArbitraryParameters())
{
solAssert(m_arguments.size() == functionType->parameterTypes().size(), "");
solAssert(m_arguments.size() == m_names.size(), "");
solAssert(m_arguments.size() == sorted.size(), "");
}
return sorted;
}
IdentifierAnnotation& Identifier::annotation() const
{
return initAnnotation<IdentifierAnnotation>();

View File

@ -1908,7 +1908,13 @@ public:
void accept(ASTConstVisitor& _visitor) const override;
Expression const& expression() const { return *m_expression; }
/// @returns the given arguments in the order they were written.
std::vector<ASTPointer<Expression const>> arguments() const { return {m_arguments.begin(), m_arguments.end()}; }
/// @returns the given arguments sorted by how the called function takes them.
std::vector<ASTPointer<Expression const>> sortedArguments() const;
/// @returns the list of given argument names if this is a named call,
/// in the order they were written.
/// If this is not a named call, this is empty.
std::vector<ASTPointer<ASTString>> const& names() const { return m_names; }
FunctionCallAnnotation& annotation() const override;

View File

@ -539,26 +539,8 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
functionType = dynamic_cast<FunctionType const*>(_functionCall.expression().annotation().type);
TypePointers parameterTypes = functionType->parameterTypes();
vector<ASTPointer<Expression const>> const& callArguments = _functionCall.arguments();
vector<ASTPointer<ASTString>> const& callArgumentNames = _functionCall.names();
if (!functionType->takesArbitraryParameters())
solAssert(callArguments.size() == parameterTypes.size(), "");
vector<ASTPointer<Expression const>> arguments;
if (callArgumentNames.empty())
// normal arguments
arguments = callArguments;
else
// named arguments
for (auto const& parameterName: functionType->parameterNames())
{
bool found = false;
for (size_t j = 0; j < callArgumentNames.size() && !found; j++)
if ((found = (parameterName == *callArgumentNames[j])))
// we found the actual parameter position
arguments.push_back(callArguments[j]);
solAssert(found, "");
}
vector<ASTPointer<Expression const>> const& arguments = _functionCall.sortedArguments();
if (functionCallKind == FunctionCallKind::StructConstructorCall)
{

View File

@ -846,26 +846,8 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
functionType = dynamic_cast<FunctionType const*>(_functionCall.expression().annotation().type);
TypePointers parameterTypes = functionType->parameterTypes();
vector<ASTPointer<Expression const>> const& callArguments = _functionCall.arguments();
vector<ASTPointer<ASTString>> const& callArgumentNames = _functionCall.names();
if (!functionType->takesArbitraryParameters())
solAssert(callArguments.size() == parameterTypes.size(), "");
vector<ASTPointer<Expression const>> arguments;
if (callArgumentNames.empty())
// normal arguments
arguments = callArguments;
else
// named arguments
for (auto const& parameterName: functionType->parameterNames())
{
auto const it = std::find_if(callArgumentNames.cbegin(), callArgumentNames.cend(), [&](ASTPointer<ASTString> const& _argName) {
return *_argName == parameterName;
});
solAssert(it != callArgumentNames.cend(), "");
arguments.push_back(callArguments[static_cast<size_t>(std::distance(callArgumentNames.begin(), it))]);
}
vector<ASTPointer<Expression const>> const& arguments = _functionCall.sortedArguments();
if (functionCallKind == FunctionCallKind::StructConstructorCall)
{

View File

@ -2530,8 +2530,8 @@ vector<smtutil::Expression> SMTEncoder::symbolicArguments(FunctionCall const& _f
auto const& funType = dynamic_cast<FunctionType const*>(calledExpr->annotation().type);
solAssert(funType, "");
vector<ASTPointer<Expression const>> arguments = _funCall.sortedArguments();
auto const& functionParams = function->parameters();
auto const& arguments = _funCall.arguments();
unsigned firstParam = 0;
if (funType->bound())
{

View File

@ -1,8 +1,10 @@
contract test {
function a(uint a, uint b, uint c) public returns (uint r) { r = a * 100 + b * 10 + c * 1; }
function b() public returns (uint r) { r = a({a: 1, b: 2, c: 3}); }
function c() public returns (uint r) { r = a({b: 2, c: 3, a: 1}); }
}
// ====
// compileViaYul: also
// ----
// b() -> 123
// c() -> 123

View File

@ -20,6 +20,8 @@ contract C {
return f({b: 1, a: 2});
if (num == 3)
return f({c: 1, a: 2, b: 3});
if (num == 4)
return f({b: 5, c: 1, a: 2});
return 500;
}
@ -31,4 +33,5 @@ contract C {
// call(uint256): 1 -> 1
// call(uint256): 2 -> 3
// call(uint256): 3 -> 6
// call(uint256): 4 -> 500
// call(uint256): 4 -> 8
// call(uint256): 5 -> 500

View File

@ -6,7 +6,7 @@ contract C {
S public s;
constructor() {
s = S({a: 1, x: true});
s = S({x: true, a: 1});
}
}

View File

@ -0,0 +1,34 @@
pragma experimental SMTChecker;
library L {
function l(uint x, uint y) internal pure returns (uint) {
return x + y;
}
}
contract C {
function f(uint u, uint s, bool b) internal pure returns (uint z) {
if (b)
z = u;
else
z = s;
}
using L for uint;
function call() public pure {
uint a = 2;
uint b = a.l({y: 3});
assert(b == 5);
b = L.l({x: 3, y: 3});
assert(b == 6);
b = f({b: true, u: 1, s: 2});
assert(b == 1);
b = f({b: false, u: 1, s: 2});
// Fails, should be 2.
assert(b == 6);
}
}
// ----
// Warning 8364: (360-361): Assertion checker does not yet implement type type(library L)
// Warning 6328: (507-521): CHC: Assertion violation happens here.
// Warning 8364: (360-361): Assertion checker does not yet implement type type(library L)

View File

@ -0,0 +1,14 @@
pragma experimental SMTChecker;
contract C {
function f(uint u, string memory s, bool b) internal {}
function call() public {
f({s: "abc", u: 1, b: true});
f({s: "abc", b: true, u: 1});
f({u: 1, s: "abc", b: true});
f({b: true, s: "abc", u: 1});
f({u: 1, b: true, s: "abc"});
f({b: true, u: 1, s: "abc"});
}
}
// ----

View File

@ -0,0 +1,15 @@
pragma experimental SMTChecker;
contract C {
function f(uint u, string memory s, bool b) internal {}
function f(uint u, uint s, uint b) internal {}
function call() public {
f({s: "abc", u: 1, b: true});
f({s: "abc", b: true, u: 1});
f({u: 1, s: "abc", b: true});
f({b: true, s: "abc", u: 1});
f({u: 1, b: true, s: "abc"});
f({b: true, u: 1, s: "abc"});
}
}
// ----