solidity
solidity copied to clipboard
Function references are not considered constant
Description
The language allows constant state variables of function types, but they are currently unusable, because it is impossible to initialize them. That's because a reference you get when you refer to a function is never considered constant. I think we should make those references constant. Unfortunately, this gets a bit weird in corner cases.
External function in a foreign contract
This is the most straightforward case. An external function pointer is represented with a contract address and a function selector. The latter is always constant as long as we're referring to a function directly (not via another pointer). The former can be constant if we make it so.
I think that in the following example constCPtr and literalCPtr could be allowed to compile without any complications:
contract C {
function f() external {}
}
contract D {
C varC;
C constant constC = C(address(0));
function() external constant varCPtr = varC.f; // Error: Initial value for constant variable has to be compile-time constant.
function() external constant constCPtr = constC.f; // Error: Initial value for constant variable has to be compile-time constant.
function() external constant literalCPtr = C(address(0)).f; // Error: Initial value for constant variable has to be compile-time constant.
}
External function in the current contract
This is a weird corner case. Since the address of the current contract is not known at compilation time, it can't be a compile-time constant. However, our constant state variables are actually runtime constants, i.e. are read-only, but evaluated at runtime. Allowing the code below to compiler would fit this model:
contract C {
function f() external {}
function() external constant constPtr = this.f; // Error: Initial value for constant variable has to be compile-time constant.
function() external creationPtr = constPtr;
function() external runtimePtr;
function run() public {
runtimePtr = constPtr;
}
}
Note that this would have odd consequences like creationPtr and runtimePtr having different values despite both being initialized with the same constant. That's because the address is only known after deployment and creationPtr gets initialized before that, at creation time. Despite this, it would not be a new thing. We already have this problem with string/bytes constants (see #16188), which are allocated in memory and can point at different addresses in different transactions.
Allowing this could also make things harder for us if we wanted to switch to true compile-time constant state variables (it would become a breaking change).
Internal function
This is a slightly different corner case. The value is always the same at runtime, but is still not known at compilation time. At least not during analysis, which is where it matters. In evmasm codegen they are pointers into bytecode, while in IR codegen they are global function IDs and both obviously depend on code generation.
contract D {
function f() internal {}
function() internal constant constPtr = f; // Error: Initial value for constant variable has to be compile-time constant.
}