Skip to content

Commit c4909e9

Browse files
authored
Merge pull request #12750 from nishant-sachdeva/abi_encodecall_should_properly_convert_function_type_to_externally_called_function
typeCheckAbiEncodeCallFunction should type check the arguments on functionPointerType->asExternallyCallableFunction instead of the plain function type
2 parents b08190c + 4c6066b commit c4909e9

9 files changed

+90
-73
lines changed

Changelog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ Compiler Features:
1010

1111
Bugfixes:
1212
* Assembly-Json: Fix assembly json export to store jump types of operations in `jumpType` field instead of `value`.
13+
* TypeChecker: Convert parameters of function type to how they would be called for ``abi.encodeCall``.
1314

1415

1516

libsolidity/analysis/TypeChecker.cpp

Lines changed: 27 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2111,57 +2111,60 @@ void TypeChecker::typeCheckABIEncodeCallFunction(FunctionCall const& _functionCa
21112111
return;
21122112
}
21132113

2114-
auto const functionPointerType = dynamic_cast<FunctionTypePointer>(type(*arguments.front()));
2115-
2116-
if (!functionPointerType)
2114+
FunctionType const* externalFunctionType = nullptr;
2115+
if (auto const functionPointerType = dynamic_cast<FunctionTypePointer>(type(*arguments.front())))
2116+
{
2117+
// this cannot be a library function, that is checked below
2118+
externalFunctionType = functionPointerType->asExternallyCallableFunction(false);
2119+
solAssert(externalFunctionType->kind() == functionPointerType->kind());
2120+
}
2121+
else
21172122
{
21182123
m_errorReporter.typeError(
21192124
5511_error,
21202125
arguments.front()->location(),
21212126
"Expected first argument to be a function pointer, not \"" +
2122-
type(*arguments.front())->canonicalName() +
2127+
type(*arguments.front())->toString() +
21232128
"\"."
21242129
);
21252130
return;
21262131
}
21272132

21282133
if (
2129-
functionPointerType->kind() != FunctionType::Kind::External &&
2130-
functionPointerType->kind() != FunctionType::Kind::Declaration
2134+
externalFunctionType->kind() != FunctionType::Kind::External &&
2135+
externalFunctionType->kind() != FunctionType::Kind::Declaration
21312136
)
21322137
{
21332138
string msg = "Expected regular external function type, or external view on public function.";
2134-
if (functionPointerType->kind() == FunctionType::Kind::Internal)
2139+
if (externalFunctionType->kind() == FunctionType::Kind::Internal)
21352140
msg += " Provided internal function.";
2136-
else if (functionPointerType->kind() == FunctionType::Kind::DelegateCall)
2141+
else if (externalFunctionType->kind() == FunctionType::Kind::DelegateCall)
21372142
msg += " Cannot use library functions for abi.encodeCall.";
2138-
else if (functionPointerType->kind() == FunctionType::Kind::Creation)
2143+
else if (externalFunctionType->kind() == FunctionType::Kind::Creation)
21392144
msg += " Provided creation function.";
21402145
else
21412146
msg += " Cannot use special function.";
21422147
SecondarySourceLocation ssl{};
21432148

2144-
if (functionPointerType->hasDeclaration())
2149+
if (externalFunctionType->hasDeclaration())
21452150
{
2146-
ssl.append("Function is declared here:", functionPointerType->declaration().location());
2151+
ssl.append("Function is declared here:", externalFunctionType->declaration().location());
21472152
if (
2148-
functionPointerType->declaration().visibility() == Visibility::Public &&
2149-
functionPointerType->declaration().scope() == m_currentContract
2153+
externalFunctionType->declaration().visibility() == Visibility::Public &&
2154+
externalFunctionType->declaration().scope() == m_currentContract
21502155
)
21512156
msg += " Did you forget to prefix \"this.\"?";
21522157
else if (util::contains(
21532158
m_currentContract->annotation().linearizedBaseContracts,
2154-
functionPointerType->declaration().scope()
2155-
) && functionPointerType->declaration().scope() != m_currentContract)
2159+
externalFunctionType->declaration().scope()
2160+
) && externalFunctionType->declaration().scope() != m_currentContract)
21562161
msg += " Functions from base contracts have to be external.";
21572162
}
21582163

21592164
m_errorReporter.typeError(3509_error, arguments[0]->location(), ssl, msg);
21602165
return;
21612166
}
2162-
2163-
solAssert(!functionPointerType->takesArbitraryParameters(), "Function must have fixed parameters.");
2164-
2167+
solAssert(!externalFunctionType->takesArbitraryParameters(), "Function must have fixed parameters.");
21652168
// Tuples with only one component become that component
21662169
vector<ASTPointer<Expression const>> callArguments;
21672170

@@ -2174,14 +2177,14 @@ void TypeChecker::typeCheckABIEncodeCallFunction(FunctionCall const& _functionCa
21742177
else
21752178
callArguments.push_back(arguments[1]);
21762179

2177-
if (functionPointerType->parameterTypes().size() != callArguments.size())
2180+
if (externalFunctionType->parameterTypes().size() != callArguments.size())
21782181
{
21792182
if (tupleType)
21802183
m_errorReporter.typeError(
21812184
7788_error,
21822185
_functionCall.location(),
21832186
"Expected " +
2184-
to_string(functionPointerType->parameterTypes().size()) +
2187+
to_string(externalFunctionType->parameterTypes().size()) +
21852188
" instead of " +
21862189
to_string(callArguments.size()) +
21872190
" components for the tuple parameter."
@@ -2191,18 +2194,18 @@ void TypeChecker::typeCheckABIEncodeCallFunction(FunctionCall const& _functionCa
21912194
7515_error,
21922195
_functionCall.location(),
21932196
"Expected a tuple with " +
2194-
to_string(functionPointerType->parameterTypes().size()) +
2197+
to_string(externalFunctionType->parameterTypes().size()) +
21952198
" components instead of a single non-tuple parameter."
21962199
);
21972200
}
21982201

21992202
// Use min() to check as much as we can before failing fatally
2200-
size_t const numParameters = min(callArguments.size(), functionPointerType->parameterTypes().size());
2203+
size_t const numParameters = min(callArguments.size(), externalFunctionType->parameterTypes().size());
22012204

22022205
for (size_t i = 0; i < numParameters; i++)
22032206
{
22042207
Type const& argType = *type(*callArguments[i]);
2205-
BoolResult result = argType.isImplicitlyConvertibleTo(*functionPointerType->parameterTypes()[i]);
2208+
BoolResult result = argType.isImplicitlyConvertibleTo(*externalFunctionType->parameterTypes()[i]);
22062209
if (!result)
22072210
m_errorReporter.typeError(
22082211
5407_error,
@@ -2212,7 +2215,7 @@ void TypeChecker::typeCheckABIEncodeCallFunction(FunctionCall const& _functionCa
22122215
" from \"" +
22132216
argType.toString() +
22142217
"\" to \"" +
2215-
functionPointerType->parameterTypes()[i]->toString() +
2218+
externalFunctionType->parameterTypes()[i]->toString() +
22162219
"\"" +
22172220
(result.message().empty() ? "." : ": " + result.message())
22182221
);
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
interface testInterface {
2+
function C(function (string memory) external) external;
3+
function D(string calldata) external;
4+
function E(string memory) external;
5+
function F(address) external;
6+
}
7+
8+
contract testContract {
9+
function g(string calldata) external {}
10+
function h(string memory) external {}
11+
function i(string calldata str) external {
12+
this.h(str);
13+
this.g(str);
14+
}
15+
function j(string memory str) external {
16+
this.h(str);
17+
this.g(str);
18+
}
19+
function k(string memory str) external pure {
20+
abi.encodeCall(testInterface.D, (str));
21+
}
22+
string s;
23+
24+
function main() external view {
25+
abi.encodeCall(testInterface.C, (this.g));
26+
abi.encodeCall(testInterface.C, (this.h));
27+
abi.encodeCall(testInterface.D, (s));
28+
abi.encodeCall(testInterface.E, (s));
29+
abi.encodeCall(testInterface.F, (payable(address(0))));
30+
abi.encodeCall(this.i, (s));
31+
abi.encodeCall(this.j, (s));
32+
}
33+
}
34+
// ----
35+
// Warning 6133: (860-914): Statement has no effect.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
interface testInterface {
2+
function A(address payable) external;
3+
}
4+
5+
contract testContract {
6+
function main() external view {
7+
abi.encodeCall(testInterface.A, (address(0)));
8+
}
9+
}
10+
// ----
11+
// TypeError 5407: (171-183): Cannot implicitly convert component at position 0 from "address" to "address payable".
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
interface testInterface {
2+
function B(function (string calldata) external) external;
3+
}
4+
5+
contract testContract {
6+
function g(string calldata) external {}
7+
function h(string memory) external {}
8+
9+
function main() external view {
10+
abi.encodeCall(testInterface.B, (this.g));
11+
abi.encodeCall(testInterface.B, (this.h));
12+
}
13+
}
14+
// ----
15+
// TypeError 5407: (278-286): Cannot implicitly convert component at position 0 from "function (string memory) external" to "function (string calldata) external".
16+
// TypeError 5407: (329-337): Cannot implicitly convert component at position 0 from "function (string memory) external" to "function (string calldata) external".

test/libsolidity/syntaxTests/abiEncoder/abi_encode_convert_address_to_address_payable.sol

Lines changed: 0 additions & 11 deletions
This file was deleted.

test/libsolidity/syntaxTests/abiEncoder/abi_encode_convert_function_pointer_to_different_function_pointer.sol

Lines changed: 0 additions & 13 deletions
This file was deleted.

test/libsolidity/syntaxTests/abiEncoder/abi_encode_convert_function_to_different_function.sol

Lines changed: 0 additions & 13 deletions
This file was deleted.

test/libsolidity/syntaxTests/abiEncoder/abi_encode_convert_string_to_different_string.sol

Lines changed: 0 additions & 12 deletions
This file was deleted.

0 commit comments

Comments
 (0)