argotorg / argotorg/solidity

Functions taking `calldata` arguments are not assignable to function pointers of the same type

Open
#12,778 12 comments 0 reactions 0 assignees View on GitHub
bug :bug: low impact medium effort should compile without error should have
Dominant language
C++
Stars
25.7k
Forks
6.2k
Avg merge
1d 11h
Merged PRs (30d)
21

Description

## Description
The compiler seems to treat pointers to functions taking `calldata` arguments as if they had `memory` arguments instead. You cannot assign them, pass into a function of use in `abi.encodeCall()` even when the target type exactly matches the type of the function pointer.

## Steps to Reproduce
### `calldata` is not accepted
```solidity
contract C {
function f(function (string calldata) external) external {}
function g(string calldata) external {}

function main() external {
function (string calldata) external ptr = this.g;
abi.encodeCall(this.f, (this.g));
this.f(this.g);
}
}
```
```
Error: Type function (string memory) external is not implicitly convertible to expected type function (string calldata) external.
--> test.sol:6:9:
|
6 | function (string calldata) external ptr = this.g;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Error: Cannot implicitly convert component at position 0 from "function" to "function".
--> test.sol:7:32:
|
7 | abi.encodeCall(this.f, (this.g));
| ^^^^^^^^

Error: Invalid type for argument in function call. Invalid implicit conversion from function (string memory) external to function (string calldata) external requested.
--> test.sol:8:16:
|
8 | this.f(this.g);
| ^^^^^^
```

### `memory` is accepted instead
On the other hand this compiles without errors:
```solidity
contract C {
function g(bytes calldata b) external returns (bytes calldata) {
return b[2:5];
}

function main() external returns (bytes memory) {
function (bytes memory) external returns (bytes memory) ptr = this.g;
bytes memory b = "123456789";
return ptr(b);
}
}
```

I checked in Remix and it even properly slices the `memory` string argument even though we only support slicing `calldata`.

This should probably be disallowed given that you can't normally pass in `memory` arguments to functions taking `calldata`. Also, when the functions are `internal`, the compiler does produce an error:
```
Error: Type function (bytes calldata) returns (bytes calldata) is not implicitly convertible to expected type function (bytes memory) returns (bytes memory).
--> test.sol:7:9:
|
7 | function (bytes memory) internal returns (bytes memory) ptr = g;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
```

## Environment
- Compiler version: 0.8.12

Contributor guide

Open the contributing guide

Research direction

The payload names no compiler source file or regression test; the diagnostics refer to test.sol. Start by reproducing both Solidity examples with compiler version 0.8.12, then inspect the type-checking path for external function-pointer conversions and abi.encodeCall. Done means the calldata and memory behavior follows consistent function-argument rules and is covered by regression tests.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp, solidity
Domain
blockchain, compilers
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.