We are excited to announce the release of the Solidity Compiler v0.8.27.
This newest version of the compiler brings support for custom errors in require to the legacy pipeline, optimizer improvements such as caching of optimized IR that will speed up compilation via IR, several bugfixes, and more!
Notable Features
Legacy Support for require with Custom Errors
Custom errors in Solidity provide a convenient and gas-efficient way to explain to the user why an operation failed. Support for using errors with the require function has been a highly anticipated feature and was finally implemented in the previous release of the compiler. The release introduced a new overload — require(bool, <error>), which reverts with the signature of the error rather than Error(string) used by the other variant.
However, using custom errors with require was only supported by the IR pipeline, i.e. compilation via Yul. With the release of 0.8.27, you can use the feature in the legacy pipeline as well.
To learn more about the feature and how to use it, check out the example from our previous release announcement.
Caching of optimized IR
While investigating the performance bottlenecks of the IR pipeline, we realized that there's one aspect of the design that has much bigger performance impact on real-life projects than we envisioned when the pipeline was introduced.
For some background, when a contract uses a feature that requires knowledge of the bytecode of another contract at compilation time, the accessed bytecode has to be embedded inside the accessing contract as a dependency. This is what happens, for example, when a contract needs to be deployed using new or its .runtimeCode or .creationCode is accessed.
In the IR pipeline this embedding happens very early, already during Yul code generation. The source of the accessed contract is included as a Yul subobject, which means that its bytecode can simply be accessed using language primitives. This ensures that the output of the code generator is self-contained and makes the pipeline very simple. There are no external artifacts that have to be linked in and everything about the source can be known at every stage.
The downside of this design, however, is that the whole pipeline has to run on the dependency again each time it is included. In particular, the whole optimization process is repeated even though the contract has already been optimized in isolation.
The new feature preserves the simplicity of the IR pipeline by introducing a cache at the Yul object level. Since every Yul object is optimized independently, the result can be reused when compiling a bytecode dependency. Below you can see our benchmarks on a few popular projects:
Project | 0.8.26 | 0.8.27 | Speedup |
---|---|---|---|
openzeppelin | 37 s | 37 s | 0% |
uniswap-v4 | 225 s | 147 s | 42% |
eigenlayer | 1211 s | 674 s | 44% |
Note that some projects remained unaffected while for others the difference is quite dramatic. We observed that the production code that gets deployed in each case actually has very few bytecode dependencies and compiles relatively quickly, which confirms our initial assumptions. However, in some projects the test suite makes extensive use of new, resulting in the large test contracts undergoing the same optimization several times. In this use case the cost of new is irrelevant, while using it makes it possible to keep the whole test written in Solidity, so it has become a common pattern in frameworks such as Foundry. Thanks to the cache, the compiler can now handle this pattern efficiently.
Additional Notes
Transient Storage
This release introduces support for transient storage variables into the parser. To clarify, the feature as a whole is not yet complete.
The compiler will now accept the syntax for marking some variables as transient and let users generate transient storage layout. However, it is still not possible to generate bytecode for contracts using such variables.
We plan to introduce high-level language support for transient storage gradually, over the next few releases. In particular, the next release will provide full support for transient state variables of value types. The support will be extended from there to cover more complex use cases.
--strict-assembly vs. --yul
This release drops the deprecated typed Yul dialect that was only accessible via --yul option in the CLI. Users should not mistake this for deprecation of support for Yul.
Please note that this change does not impact Yul compilation in any way. It is the --strict-assembly option, and not --yul, that has always been used to select the only commonly used dialect of Yul. The latter was only used for an experimental typed dialect that is now deprecated.
Full Changelog
Language Features
- Accept declarations of state variables with transient data location (parser support only, no code generation yet).
- Make require(bool, Error) available when using the legacy pipeline.
- Yul: Parsing rules for source location comments have been relaxed: Whitespace between the location components as well as single-quoted code snippets are now allowed.
Compiler Features
- Commandline Interface: Add --transient-storage-layout output.
- Commandline Interface: Allow the use of --asm-json output option in assembler mode to export EVM assembly of the contracts in JSON format.
- Commandline Interface: Do not perform IR optimization when only unoptimized IR is requested.
- Constant Optimizer: Uses PUSH0 if supported by the selected evm version.
- Error Reporting: Unimplemented features are now properly reported as errors instead of being handled as if they were bugs.
- EVM: Support for the EVM version "Prague".
- Peephole Optimizer: PUSH0, when supported, is duplicated explicitly instead of using DUP1.
- Peephole Optimizer: Remove identical code snippets that terminate the control flow if they occur one after another.
- SMTChecker: Add CHC engine check for underflow and overflow in unary minus operation.
- SMTChecker: Replace CVC4 as a possible BMC backend with cvc5.
- Standard JSON Interface: Add transientStorageLayout output.
- Standard JSON Interface: Do not perform IR optimization when only unoptimized IR is requested.
- Yul: Drop the deprecated typed Yul dialect that was only accessible via --yul in the CLI.
- Yul: The presence of types in untyped Yul dialects is now a parser error.
- Yul Optimizer: Caching of optimized IR to speed up optimization of contracts with bytecode dependencies.
- Yul Optimizer: The optimizer now treats some previously unrecognized identical literals as identical.
Bugfixes
- Assembler: Fix ICE caused by imprecise calculation of required size of tags in bytecode when code size is above 255.
- Parser: Fix spuriously emitted parser error for unary plus operations when used as binary operator in some cases.
- SMTChecker: Fix error that reports invalid number of verified checks for BMC and CHC engines.
- SMTChecker: Fix formatting of unary minus expressions in invariants.
- SMTChecker: Fix internal compiler error when reporting proved targets for BMC engine.
- SMTChecker: Fix SMT logic error when assigning to an array of contracts or functions.
- Standard JSON Interface: For Yul input, properly produce output artifacts in case of warnings.
- TypeChecker: Fix segfault when assigning nested tuple to tuple.
- Yul IR Code Generation: Deterministic order of Yul subobjects.
- Yul Optimizer: Fix Yul source locations always referring to unoptimized source, even in optimized outputs.
- Yul Optimizer: Fix warnings being generated twice when there are no errors.
- Yul Optimizer: Name simplification could lead to forbidden identifiers with a leading and/or trailing dot, e.g., x._ would get simplified into x..
- Yul Parser: Fix segfault when parsing very long location comments.
Build System
- Change build system to use git submodules for some dependencies (nlohmann-json, fmtlib & range-v3).
How to Install/Upgrade?
To upgrade to the latest version of the Solidity Compiler, please follow the installation instructions available in our documentation. You can download the new version of Solidity here: v0.8.27.
If you want to build from the source code, do not use the source archives generated automatically by GitHub.
And last but not least, we would like to give a big thank you to all the contributors who helped make this release possible!