mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
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:
commit
c4909e99c1
@ -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``.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -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())
|
||||||
);
|
);
|
||||||
|
@ -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.
|
@ -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".
|
@ -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".
|
@ -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".
|
|
@ -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".
|
|
@ -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)".
|
|
@ -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".
|
|
Loading…
Reference in New Issue
Block a user