solidity icon indicating copy to clipboard operation
solidity copied to clipboard

Constant expression evaluation at compile time

Open axic opened this issue 6 years ago • 10 comments

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:

  1. split constant into two, constant which only allows literals and constexpr which allows more complex expressions
  2. do not allow "pure functions" above which result in a call (ecrecover, sha256, ripemd160)
  3. allow every pure function, not only the above subset
  4. evaluate constant or constexpr 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.

axic avatar Oct 30 '17 13:10 axic

I, by the way, vote for finally starting with this at least soonish...

ekpyron avatar Aug 04 '20 19:08 ekpyron

Should we consider C++-style literals and use them in expressions, such as "stringtobehashed"_keccak256 instead of the built in function?

axic avatar Aug 04 '20 19:08 axic

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.

ekpyron avatar Aug 04 '20 19:08 ekpyron

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.

chriseth avatar Aug 04 '20 20:08 chriseth

Why not?

ekpyron avatar Aug 04 '20 21:08 ekpyron

We should definitely allow compile-time evaluation of user-defined pure functions.

aarlt avatar Aug 31 '20 23:08 aarlt

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 and type(...).max

cameel avatar Apr 12 '22 11:04 cameel

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.

cameel avatar Jul 26 '22 20:07 cameel

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.

nventuro avatar Jul 26 '22 22:07 nventuro

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.

cameel avatar Jul 27 '22 21:07 cameel