solidity
solidity copied to clipboard
Constant expression evaluation at compile time
Variables can be declared constant
which allows:
- literals
- (arithmetic) expressions
- and function calls to "pure" functions, which is a hardcoded list of:
keccak256
,ecrecover
,sha256
,ripemd160
,addmod
,mulmod
,new
(for creating objects only, such as arrays)
Currently whenever a constant expression is used, the entire expression is placed into the output. This is of course reduced/optimised, but that is mostly restricted to operations on literals and some edge cases.
We should consider one or more of the following:
- split
constant
into two,constant
which only allows literals andconstexpr
which allows more complex expressions - do not allow "pure functions" above which result in a call (
ecrecover
,sha256
,ripemd160
) - allow every pure function, not only the above subset
- evaluate
constant
orconstexpr
in a VM during compilation time and use the result only (this VM could be a subset of the EVM, since many features are not required)
Obviously 2 and 3 contradict eachother. I would opt for 2. unless 4. is implemented and then 3. could be allowed.
Somewhat relevant is #715.
I, by the way, vote for finally starting with this at least soonish...
Should we consider C++-style literals and use them in expressions, such as "stringtobehashed"_keccak256
instead of the built in function?
Going forward I'd very much like for this to work even for user-defined pure functions, e.g.
function weirdStuff(uint256 x, uint256 y) public pure returns(uint256) { return 2**4 - 3 * x / y; }
uint256[weirdStuff(1,2)] x;
Or even stuff like
function weirdStuff(bytes memory x) public pure returns(uint256 s) { for (uint256 i = 0; i < x.length; ++i) s += x[i]; }
uint256[weirdStuff(hex"12345678")] x;
Not in a first step for sure, but eventually... So given that, I'd personally not see much need to distinguish that much between compile time constant string literal things and function calls.
Is it in any way possible to compile this via yul and run it through an interpreter? Then we would at least not have to re-implement everything.
Why not?
We should definitely allow compile-time evaluation of user-defined pure functions.
Some things that we might want to explicitly add to the list of stuff that should be evaluated a compilation time:
- Constant expressions involving type conversions.
- Ternary operator with constant expressions as arguments.
- Indexing an array literal with a constant expression.
-
type(...).min
andtype(...).max
Another case that might be of practical interest, brought up by @nventuro:
-
keccak256(type(ERC20).creationCode)
-
keccak256(abi.encodePacked(type(ERC20).creationCode, "hello how are you"))
This is useful in CREATE2
address calculation, when constructor args are known constants. Currently this is done by copying the whole bytecode to memory, which is very inefficient.
This is useful in CREATE2 address calculation, when constructor args are known constants. Currently this is done by copying the whole bytecode to memory, which is very inefficient.
Note that mine was just a toy example - a real use case would have non-constant constructor arguments. I'm not sure there's many use cases for the one you mention.
ok, right. We should still have things like this handled as a part of this mechanism but I guess you're right, constant arguments for this are likely not very common.