mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Fix function calls with named arguments for overloaded functions
This commit is contained in:
parent
b021f015f9
commit
84b68006ba
@ -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).
|
||||||
|
|
||||||
|
@ -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())
|
||||||
|
@ -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
|
||||||
|
@ -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(); }
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
@ -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});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
@ -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.
|
@ -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.
|
@ -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.
|
Loading…
Reference in New Issue
Block a user