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
This commit is contained in:
chriseth 2022-03-24 15:16:55 +01:00 committed by GitHub
commit c4909e99c1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 90 additions and 73 deletions

View File

@ -10,6 +10,7 @@ Compiler Features:
Bugfixes: Bugfixes:
* Assembly-Json: Fix assembly json export to store jump types of operations in `jumpType` field instead of `value`. * Assembly-Json: Fix assembly json export to store jump types of operations in `jumpType` field instead of `value`.
* TypeChecker: Convert parameters of function type to how they would be called for ``abi.encodeCall``.

View File

@ -2111,57 +2111,60 @@ void TypeChecker::typeCheckABIEncodeCallFunction(FunctionCall const& _functionCa
return; return;
} }
auto const functionPointerType = dynamic_cast<FunctionTypePointer>(type(*arguments.front())); FunctionType const* externalFunctionType = nullptr;
if (auto const functionPointerType = dynamic_cast<FunctionTypePointer>(type(*arguments.front())))
if (!functionPointerType) {
// this cannot be a library function, that is checked below
externalFunctionType = functionPointerType->asExternallyCallableFunction(false);
solAssert(externalFunctionType->kind() == functionPointerType->kind());
}
else
{ {
m_errorReporter.typeError( m_errorReporter.typeError(
5511_error, 5511_error,
arguments.front()->location(), arguments.front()->location(),
"Expected first argument to be a function pointer, not \"" + "Expected first argument to be a function pointer, not \"" +
type(*arguments.front())->canonicalName() + type(*arguments.front())->toString() +
"\"." "\"."
); );
return; return;
} }
if ( if (
functionPointerType->kind() != FunctionType::Kind::External && externalFunctionType->kind() != FunctionType::Kind::External &&
functionPointerType->kind() != FunctionType::Kind::Declaration externalFunctionType->kind() != FunctionType::Kind::Declaration
) )
{ {
string msg = "Expected regular external function type, or external view on public function."; string msg = "Expected regular external function type, or external view on public function.";
if (functionPointerType->kind() == FunctionType::Kind::Internal) if (externalFunctionType->kind() == FunctionType::Kind::Internal)
msg += " Provided internal function."; msg += " Provided internal function.";
else if (functionPointerType->kind() == FunctionType::Kind::DelegateCall) else if (externalFunctionType->kind() == FunctionType::Kind::DelegateCall)
msg += " Cannot use library functions for abi.encodeCall."; msg += " Cannot use library functions for abi.encodeCall.";
else if (functionPointerType->kind() == FunctionType::Kind::Creation) else if (externalFunctionType->kind() == FunctionType::Kind::Creation)
msg += " Provided creation function."; msg += " Provided creation function.";
else else
msg += " Cannot use special function."; msg += " Cannot use special function.";
SecondarySourceLocation ssl{}; SecondarySourceLocation ssl{};
if (functionPointerType->hasDeclaration()) if (externalFunctionType->hasDeclaration())
{ {
ssl.append("Function is declared here:", functionPointerType->declaration().location()); ssl.append("Function is declared here:", externalFunctionType->declaration().location());
if ( if (
functionPointerType->declaration().visibility() == Visibility::Public && externalFunctionType->declaration().visibility() == Visibility::Public &&
functionPointerType->declaration().scope() == m_currentContract externalFunctionType->declaration().scope() == m_currentContract
) )
msg += " Did you forget to prefix \"this.\"?"; msg += " Did you forget to prefix \"this.\"?";
else if (util::contains( else if (util::contains(
m_currentContract->annotation().linearizedBaseContracts, m_currentContract->annotation().linearizedBaseContracts,
functionPointerType->declaration().scope() externalFunctionType->declaration().scope()
) && functionPointerType->declaration().scope() != m_currentContract) ) && externalFunctionType->declaration().scope() != m_currentContract)
msg += " Functions from base contracts have to be external."; msg += " Functions from base contracts have to be external.";
} }
m_errorReporter.typeError(3509_error, arguments[0]->location(), ssl, msg); m_errorReporter.typeError(3509_error, arguments[0]->location(), ssl, msg);
return; return;
} }
solAssert(!externalFunctionType->takesArbitraryParameters(), "Function must have fixed parameters.");
solAssert(!functionPointerType->takesArbitraryParameters(), "Function must have fixed parameters.");
// Tuples with only one component become that component // Tuples with only one component become that component
vector<ASTPointer<Expression const>> callArguments; vector<ASTPointer<Expression const>> callArguments;
@ -2174,14 +2177,14 @@ void TypeChecker::typeCheckABIEncodeCallFunction(FunctionCall const& _functionCa
else else
callArguments.push_back(arguments[1]); callArguments.push_back(arguments[1]);
if (functionPointerType->parameterTypes().size() != callArguments.size()) if (externalFunctionType->parameterTypes().size() != callArguments.size())
{ {
if (tupleType) if (tupleType)
m_errorReporter.typeError( m_errorReporter.typeError(
7788_error, 7788_error,
_functionCall.location(), _functionCall.location(),
"Expected " + "Expected " +
to_string(functionPointerType->parameterTypes().size()) + to_string(externalFunctionType->parameterTypes().size()) +
" instead of " + " instead of " +
to_string(callArguments.size()) + to_string(callArguments.size()) +
" components for the tuple parameter." " components for the tuple parameter."
@ -2191,18 +2194,18 @@ void TypeChecker::typeCheckABIEncodeCallFunction(FunctionCall const& _functionCa
7515_error, 7515_error,
_functionCall.location(), _functionCall.location(),
"Expected a tuple with " + "Expected a tuple with " +
to_string(functionPointerType->parameterTypes().size()) + to_string(externalFunctionType->parameterTypes().size()) +
" components instead of a single non-tuple parameter." " components instead of a single non-tuple parameter."
); );
} }
// Use min() to check as much as we can before failing fatally // Use min() to check as much as we can before failing fatally
size_t const numParameters = min(callArguments.size(), functionPointerType->parameterTypes().size()); size_t const numParameters = min(callArguments.size(), externalFunctionType->parameterTypes().size());
for (size_t i = 0; i < numParameters; i++) for (size_t i = 0; i < numParameters; i++)
{ {
Type const& argType = *type(*callArguments[i]); Type const& argType = *type(*callArguments[i]);
BoolResult result = argType.isImplicitlyConvertibleTo(*functionPointerType->parameterTypes()[i]); BoolResult result = argType.isImplicitlyConvertibleTo(*externalFunctionType->parameterTypes()[i]);
if (!result) if (!result)
m_errorReporter.typeError( m_errorReporter.typeError(
5407_error, 5407_error,
@ -2212,7 +2215,7 @@ void TypeChecker::typeCheckABIEncodeCallFunction(FunctionCall const& _functionCa
" from \"" + " from \"" +
argType.toString() + argType.toString() +
"\" to \"" + "\" to \"" +
functionPointerType->parameterTypes()[i]->toString() + externalFunctionType->parameterTypes()[i]->toString() +
"\"" + "\"" +
(result.message().empty() ? "." : ": " + result.message()) (result.message().empty() ? "." : ": " + result.message())
); );

View File

@ -0,0 +1,35 @@
interface testInterface {
function C(function (string memory) external) external;
function D(string calldata) external;
function E(string memory) external;
function F(address) external;
}
contract testContract {
function g(string calldata) external {}
function h(string memory) external {}
function i(string calldata str) external {
this.h(str);
this.g(str);
}
function j(string memory str) external {
this.h(str);
this.g(str);
}
function k(string memory str) external pure {
abi.encodeCall(testInterface.D, (str));
}
string s;
function main() external view {
abi.encodeCall(testInterface.C, (this.g));
abi.encodeCall(testInterface.C, (this.h));
abi.encodeCall(testInterface.D, (s));
abi.encodeCall(testInterface.E, (s));
abi.encodeCall(testInterface.F, (payable(address(0))));
abi.encodeCall(this.i, (s));
abi.encodeCall(this.j, (s));
}
}
// ----
// Warning 6133: (860-914): Statement has no effect.

View File

@ -0,0 +1,11 @@
interface testInterface {
function A(address payable) external;
}
contract testContract {
function main() external view {
abi.encodeCall(testInterface.A, (address(0)));
}
}
// ----
// TypeError 5407: (171-183): Cannot implicitly convert component at position 0 from "address" to "address payable".

View File

@ -0,0 +1,16 @@
interface testInterface {
function B(function (string calldata) external) external;
}
contract testContract {
function g(string calldata) external {}
function h(string memory) external {}
function main() external view {
abi.encodeCall(testInterface.B, (this.g));
abi.encodeCall(testInterface.B, (this.h));
}
}
// ----
// TypeError 5407: (278-286): Cannot implicitly convert component at position 0 from "function (string memory) external" to "function (string calldata) external".
// TypeError 5407: (329-337): Cannot implicitly convert component at position 0 from "function (string memory) external" to "function (string calldata) external".

View File

@ -1,11 +0,0 @@
interface I {
function f(address payable) external;
}
contract C {
function main() external view {
abi.encodeCall(I.f, (address(0)));
}
}
// ----
// TypeError 5407: (136-148): Cannot implicitly convert component at position 0 from "address" to "address payable".

View File

@ -1,13 +0,0 @@
interface I {
function f(function (string calldata) external) external;
}
contract C {
function g(string calldata) external {}
function main() external view {
abi.encodeCall(I.f, (this.g));
}
}
// ----
// TypeError 5407: (201-209): Cannot implicitly convert component at position 0 from "function (string memory) external" to "function (string calldata) external".

View File

@ -1,13 +0,0 @@
interface I {
function f(function (string calldata) external view returns (uint)) external;
}
contract C {
function g(string memory) external {}
function main() external view {
abi.encodeCall(I.f, (this.g));
}
}
// ----
// TypeError 5407: (219-227): Cannot implicitly convert component at position 0 from "function (string memory) external" to "function (string calldata) view external returns (uint256)".

View File

@ -1,12 +0,0 @@
interface I {
function f(string calldata) external;
}
contract C {
string s;
function main() external view {
abi.encodeCall(I.f, (s));
}
}
// ----
// TypeError 5407: (150-153): Cannot implicitly convert component at position 0 from "string storage ref" to "string calldata".