Fix function calls with named arguments for overloaded functions

This commit is contained in:
Mathias Baumann 2019-03-19 17:12:21 +01:00
parent b021f015f9
commit 84b68006ba
13 changed files with 163 additions and 25 deletions

View File

@ -4,6 +4,7 @@ Language Features:
Compiler Features: Compiler Features:
* Function calls with named arguments now work with overloaded functions.
* Inline Assembly: Issue error when using ``callvalue()`` inside nonpayable function (in the same way that ``msg.value`` already does). * Inline Assembly: Issue error when using ``callvalue()`` inside nonpayable function (in the same way that ``msg.value`` already does).
* Yul Optimizer: Enable stack allocation optimization by default if yul optimizer is active (disable in yulDetails). * Yul Optimizer: Enable stack allocation optimization by default if yul optimizer is active (disable in yulDetails).

View File

@ -1807,13 +1807,16 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
argumentsArePure = false; argumentsArePure = false;
} }
// For positional calls only, store argument types // Store argument types - and names if given - for overload resolution
if (_functionCall.names().empty())
{ {
shared_ptr<TypePointers> argumentTypes = make_shared<TypePointers>(); FuncCallArguments funcCallArgs;
funcCallArgs.names = _functionCall.names();
for (ASTPointer<Expression const> const& argument: arguments) for (ASTPointer<Expression const> const& argument: arguments)
argumentTypes->push_back(type(*argument)); funcCallArgs.types.push_back(type(*argument));
_functionCall.expression().annotation().argumentTypes = move(argumentTypes);
_functionCall.expression().annotation().arguments = std::move(funcCallArgs);
} }
_functionCall.expression().accept(*this); _functionCall.expression().accept(*this);
@ -2010,16 +2013,16 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
ASTString const& memberName = _memberAccess.memberName(); ASTString const& memberName = _memberAccess.memberName();
// Retrieve the types of the arguments if this is used to call a function. // Retrieve the types of the arguments if this is used to call a function.
auto const& argumentTypes = _memberAccess.annotation().argumentTypes; auto const& arguments = _memberAccess.annotation().arguments;
MemberList::MemberMap possibleMembers = exprType->members(m_scope).membersByName(memberName); MemberList::MemberMap possibleMembers = exprType->members(m_scope).membersByName(memberName);
size_t const initialMemberCount = possibleMembers.size(); size_t const initialMemberCount = possibleMembers.size();
if (initialMemberCount > 1 && argumentTypes) if (initialMemberCount > 1 && arguments)
{ {
// do overload resolution // do overload resolution
for (auto it = possibleMembers.begin(); it != possibleMembers.end();) for (auto it = possibleMembers.begin(); it != possibleMembers.end();)
if ( if (
it->type->category() == Type::Category::Function && it->type->category() == Type::Category::Function &&
!dynamic_cast<FunctionType const&>(*it->type).canTakeArguments(*argumentTypes, exprType) !dynamic_cast<FunctionType const&>(*it->type).canTakeArguments(*arguments, exprType)
) )
it = possibleMembers.erase(it); it = possibleMembers.erase(it);
else else
@ -2274,7 +2277,7 @@ bool TypeChecker::visit(Identifier const& _identifier)
IdentifierAnnotation& annotation = _identifier.annotation(); IdentifierAnnotation& annotation = _identifier.annotation();
if (!annotation.referencedDeclaration) if (!annotation.referencedDeclaration)
{ {
if (!annotation.argumentTypes) if (!annotation.arguments)
{ {
// The identifier should be a public state variable shadowing other functions // The identifier should be a public state variable shadowing other functions
vector<Declaration const*> candidates; vector<Declaration const*> candidates;
@ -2303,7 +2306,7 @@ bool TypeChecker::visit(Identifier const& _identifier)
{ {
FunctionTypePointer functionType = declaration->functionType(true); FunctionTypePointer functionType = declaration->functionType(true);
solAssert(!!functionType, "Requested type not present."); solAssert(!!functionType, "Requested type not present.");
if (functionType->canTakeArguments(*annotation.argumentTypes)) if (functionType->canTakeArguments(*annotation.arguments))
candidates.push_back(declaration); candidates.push_back(declaration);
} }
if (candidates.empty()) if (candidates.empty())

View File

@ -23,8 +23,11 @@
#pragma once #pragma once
#include <libsolidity/ast/ASTForward.h> #include <libsolidity/ast/ASTForward.h>
#include <libsolidity/ast/ASTEnums.h>
#include <libsolidity/ast/ExperimentalFeatures.h> #include <libsolidity/ast/ExperimentalFeatures.h>
#include <boost/optional.hpp>
#include <map> #include <map>
#include <memory> #include <memory>
#include <set> #include <set>
@ -176,9 +179,10 @@ struct ExpressionAnnotation: ASTAnnotation
bool isLValue = false; bool isLValue = false;
/// Whether the expression is used in a context where the LValue is actually required. /// Whether the expression is used in a context where the LValue is actually required.
bool lValueRequested = false; bool lValueRequested = false;
/// Types of arguments if the expression is a function that is called - used
/// for overload resolution. /// Types and - if given - names of arguments if the expr. is a function
std::shared_ptr<std::vector<TypePointer>> argumentTypes; /// that is called, used for overload resoultion
boost::optional<FuncCallArguments> arguments;
}; };
struct IdentifierAnnotation: ExpressionAnnotation struct IdentifierAnnotation: ExpressionAnnotation

View File

@ -22,6 +22,7 @@
#pragma once #pragma once
#include <liblangutil/Exceptions.h> #include <liblangutil/Exceptions.h>
#include <libsolidity/ast/ASTForward.h>
#include <string> #include <string>
@ -50,5 +51,20 @@ inline std::string stateMutabilityToString(StateMutability const& _stateMutabili
} }
} }
class Type;
/// Container for function call parameter types & names
struct FuncCallArguments
{
/// Types of arguments
std::vector<std::shared_ptr<Type const>> types;
/// Names of the arguments if given, otherwise unset
std::vector<ASTPointer<ASTString>> names;
size_t numArguments() const { return types.size(); }
size_t numNames() const { return names.size(); }
bool hasNamedArguments() const { return !names.empty(); }
};
} }
} }

View File

@ -144,12 +144,12 @@ Json::Value ASTJsonConverter::typePointerToJson(TypePointer _tp, bool _short)
return typeDescriptions; return typeDescriptions;
} }
Json::Value ASTJsonConverter::typePointerToJson(std::shared_ptr<std::vector<TypePointer>> _tps) Json::Value ASTJsonConverter::typePointerToJson(boost::optional<FuncCallArguments> const& _tps)
{ {
if (_tps) if (_tps)
{ {
Json::Value arguments(Json::arrayValue); Json::Value arguments(Json::arrayValue);
for (auto const& tp: *_tps) for (auto const& tp: _tps->types)
appendMove(arguments, typePointerToJson(tp)); appendMove(arguments, typePointerToJson(tp));
return arguments; return arguments;
} }
@ -168,7 +168,7 @@ void ASTJsonConverter::appendExpressionAttributes(
make_pair("isPure", _annotation.isPure), make_pair("isPure", _annotation.isPure),
make_pair("isLValue", _annotation.isLValue), make_pair("isLValue", _annotation.isLValue),
make_pair("lValueRequested", _annotation.lValueRequested), make_pair("lValueRequested", _annotation.lValueRequested),
make_pair("argumentTypes", typePointerToJson(_annotation.argumentTypes)) make_pair("argumentTypes", typePointerToJson(_annotation.arguments))
}; };
_attributes += exprAttributes; _attributes += exprAttributes;
} }
@ -701,7 +701,7 @@ bool ASTJsonConverter::visit(Identifier const& _node)
make_pair("referencedDeclaration", idOrNull(_node.annotation().referencedDeclaration)), make_pair("referencedDeclaration", idOrNull(_node.annotation().referencedDeclaration)),
make_pair("overloadedDeclarations", overloads), make_pair("overloadedDeclarations", overloads),
make_pair("typeDescriptions", typePointerToJson(_node.annotation().type)), make_pair("typeDescriptions", typePointerToJson(_node.annotation().type)),
make_pair("argumentTypes", typePointerToJson(_node.annotation().argumentTypes)) make_pair("argumentTypes", typePointerToJson(_node.annotation().arguments))
}); });
return false; return false;
} }

View File

@ -159,7 +159,7 @@ private:
return tmp; return tmp;
} }
static Json::Value typePointerToJson(TypePointer _tp, bool _short = false); static Json::Value typePointerToJson(TypePointer _tp, bool _short = false);
static Json::Value typePointerToJson(std::shared_ptr<std::vector<TypePointer>> _tps); static Json::Value typePointerToJson(boost::optional<FuncCallArguments> const& _tps);
void appendExpressionAttributes( void appendExpressionAttributes(
std::vector<std::pair<std::string, Json::Value>> &_attributes, std::vector<std::pair<std::string, Json::Value>> &_attributes,
ExpressionAnnotation const& _annotation ExpressionAnnotation const& _annotation

View File

@ -2986,26 +2986,53 @@ TypePointer FunctionType::interfaceType(bool /*_inLibrary*/) const
return TypePointer(); return TypePointer();
} }
bool FunctionType::canTakeArguments(TypePointers const& _argumentTypes, TypePointer const& _selfType) const bool FunctionType::canTakeArguments(
FuncCallArguments const& _arguments,
TypePointer const& _selfType
) const
{ {
solAssert(!bound() || _selfType, ""); solAssert(!bound() || _selfType, "");
if (bound() && !_selfType->isImplicitlyConvertibleTo(*selfType())) if (bound() && !_selfType->isImplicitlyConvertibleTo(*selfType()))
return false; return false;
TypePointers paramTypes = parameterTypes(); TypePointers paramTypes = parameterTypes();
std::vector<std::string> const paramNames = parameterNames();
if (takesArbitraryParameters()) if (takesArbitraryParameters())
return true; return true;
else if (_argumentTypes.size() != paramTypes.size()) else if (_arguments.numArguments() != paramTypes.size())
return false; return false;
else else if (!_arguments.hasNamedArguments())
return equal( return equal(
_argumentTypes.cbegin(), _arguments.types.cbegin(),
_argumentTypes.cend(), _arguments.types.cend(),
paramTypes.cbegin(), paramTypes.cbegin(),
[](TypePointer const& argumentType, TypePointer const& parameterType) [](TypePointer const& argumentType, TypePointer const& parameterType)
{ {
return argumentType->isImplicitlyConvertibleTo(*parameterType); return argumentType->isImplicitlyConvertibleTo(*parameterType);
} }
); );
else if (paramNames.size() != _arguments.numNames())
return false;
else
{
solAssert(_arguments.numArguments() == _arguments.numNames(), "Expected equal sized type & name vectors");
size_t matchedNames = 0;
for (auto const& argName: _arguments.names)
for (size_t i = 0; i < paramNames.size(); i++)
if (*argName == paramNames[i])
{
matchedNames++;
if (!_arguments.types[i]->isImplicitlyConvertibleTo(*paramTypes[i]))
return false;
}
if (matchedNames == _arguments.numNames())
return true;
return false;
}
} }
bool FunctionType::hasEqualParameterTypes(FunctionType const& _other) const bool FunctionType::hasEqualParameterTypes(FunctionType const& _other) const

View File

@ -1107,11 +1107,15 @@ public:
/// external type. /// external type.
FunctionTypePointer interfaceFunctionType() const; FunctionTypePointer interfaceFunctionType() const;
/// @returns true if this function can take the given argument types (possibly /// @returns true if this function can take the given arguments (possibly
/// after implicit conversion). /// after implicit conversion).
/// @param _selfType if the function is bound, this has to be supplied and is the type of the /// @param _selfType if the function is bound, this has to be supplied and is the type of the
/// expression the function is called on. /// expression the function is called on.
bool canTakeArguments(TypePointers const& _arguments, TypePointer const& _selfType = TypePointer()) const; bool canTakeArguments(
FuncCallArguments const& _arguments,
TypePointer const& _selfType = TypePointer()
) const;
/// @returns true if the types of parameters are equal (does not check return parameter types) /// @returns true if the types of parameters are equal (does not check return parameter types)
bool hasEqualParameterTypes(FunctionType const& _other) const; bool hasEqualParameterTypes(FunctionType const& _other) const;
/// @returns true iff the return types are equal (does not check parameter types) /// @returns true iff the return types are equal (does not check parameter types)

View File

@ -0,0 +1,32 @@
contract C {
function f() public returns (uint) {
return 0;
}
function f(uint a) public returns (uint) {
return a;
}
function f(uint a, uint b) public returns (uint) {
return a+b;
}
function f(uint a, uint b, uint c) public returns (uint) {
return a+b+c;
}
function call(uint num) public returns (uint256) {
if (num == 0)
return f();
if (num == 1)
return f({a: 1});
if (num == 2)
return f({b: 1, a: 2});
if (num == 3)
return f({c: 1, a: 2, b: 3});
return 500;
}
}
// ----
// call(uint256): 0 -> 0
// call(uint256): 1 -> 1
// call(uint256): 2 -> 3
// call(uint256): 3 -> 6
// call(uint256): 4 -> 500

View File

@ -0,0 +1,14 @@
contract C {
function f(uint x) internal { }
function f(uint x, uint y) internal { }
function f(uint x, uint y, uint z) internal { }
function call() internal {
f({x: 1});
f({x: 1, y: 2});
f({y: 2, x: 1});
f({x: 1, y: 2, z: 3});
f({z: 3, x: 1, y: 2});
f({y: 2, z: 3, x: 1});
}
}
// ----

View File

@ -0,0 +1,14 @@
contract C {
function f(uint x) internal { }
function f(uint x, uint y) internal { }
function f(uint x, uint y, uint z) internal { }
function call() internal {
f(1, 2);
f(1);
f({x: 1, y: 2});
f({y: 2});
}
}
// ----
// TypeError: (241-242): No matching declaration found after argument-dependent lookup.

View File

@ -0,0 +1,12 @@
contract C {
function f(uint x) internal { }
function f(uint x, uint y) internal { }
function f(uint x, uint y, uint z) internal { }
function call() internal {
f({x:1, y: 2});
f({x:1, z: 3});
}
}
// ----
// TypeError: (209-210): No matching declaration found after argument-dependent lookup.

View File

@ -0,0 +1,11 @@
contract C {
function f(uint x) internal { }
function f(uint x, uint y) internal { }
function f(uint x, uint y, uint z) internal { }
function call() internal {
f({x:1, y: 2, z: 3});
f({y:2, v: 10, z: 3});
}
}
// ----
// TypeError: (214-215): No matching declaration found after argument-dependent lookup.