Officially introduce "comptime" and "runtime constant" as language concepts and adjust `pure`
- Dominant language
- C++
- Stars
- 25.7k
- Forks
- 6.2k
- Avg merge
- 1d 11h
- Merged PRs (30d)
- 21
Description
## Abstract
There are various de-facto levels of purity and constness in the language while officially we only recognize and document pure. Their application is also inconsistent, as many constructs could be easily promoted to a stricter level with various nice benefits. I described the target state in [Comptime and runtime constants vs `pure` in Classic Solidity](https://notes.argot.org/@solidity-comptime-and-runtime-constants-vs-pure-in-classic-solidity). This issue lists changes that are required to get to that state.
## Motivation
Aside from making things more consistent, implementing this would have practical benefits:
- Zero-cost evaluation of comptime constants. Currently they are not always successfully reduced to a single value in the optimizer since that depends on our inlining heuristics (#15358).
- More complex constants in inline assembly (#12479). The current approach limits us to ones whose initializer is a literal expression. No references to other constants or type conversions allowed.
- No more unintuitive termination of comptime evaluation on constants, type conversions or certain operators. E.g. if you can calculate something like `uint48((C == D ? 0 : 1) * CONST * uint48(uint(keccak256("abc"))))` to determine storage layout base, there's no reason not to do that just because it was assigned to a variable in runtime context.
- `assert()` being automatically checked at compilation time as long as arguments are comptime (not requiring a separate builtin proposed in #8146)
- Compile-time evaluation of comparison and logical operators using our existing comptime infrastructure instead of leaving that to the optimizer. E.g.: `if (fPtr == C.foo)`, `type(MyUDVT).min < 0` or `require(0 < CONST_VALUE && CONST_VALUE < 10, "error")`.
## Specification
- Officially document comptime and runtime constant as distinct purity levels
- Make the following builtins comptime:
- `addmod()`, `mulmod()`
- `.selector`, `.address`
- `type()`
- `type().min`, `type().max`
- `type().interfaceId`
- `.length`
- `assert()`
- Make the following expressions comptime:
- Logical and comparison operators (`||`, `&&`, `!`, `==`, `!=`, `<`, `<=`, `>`, `>=`)
- Ternary operator (` ? : `
- Indexing operator (`[]`)
- Enum members (e.g. `E.A`)
- Tuples of comptime expressions (e.g. `(123, true)`)
- Inline arrays of comptime expressions (e.g. `[[12, 34], [56, 78]]`)
- Type conversion (#16420)
- Types (contracts, interfaces, libraries, structs, enums, UDVTs, errors, events, modules, built-in types)
- External function references
- Modifier references
- Make the following expressions runtime constant:
- Internal function references
- Always evaluate comptime expressions at compilation time, even in runtime context
- Adjust annotations in the AST:
- Add `comptimeValue` (optionally might be accompanied with `isComptime`)
- Add `isRuntimeConstant`
- Change `isPure` to be set on expressions that are pure rather than runtime constant
- Add `initialValue` on variable declarations
- Deprecate `constant` (redundant with `mutability`)
- Deprecate `isConstant` (redundant with `mutability` of `referencedDeclaration`)
- Deprecate `value` (equivalent to the new `comptimeValue` or `initialValue`, depending on node type)
## Backwards Compatibility
Mostly backwards-compatible, as we only make things stricter.
The change in the meaning of `isPure` annotation could be potentially interpreted as breaking - the alternative would be to introduce a new annotation and only swap it for `isPure` in a breaking version.
Contributor guide
Assessment
This issue has not been assessed yet.