Solidity v0.8.15 fixes two important bugs, improves inlining heuristics and adds a .selector member for errors and events.
The first one is an optimizer bug that can lead to memory write operations in inline assembly being removed if the result of such an operation is not read back from within the same assembly block. The bug can be triggered only when using the default legacy compiler pipeline (the new compilation pipeline via IR is not affected) and happens only in assembly blocks that never access Solidity variables defined in the surrounding code. The bug affects only versions 0.8.13 and 0.8.14 and we strongly encourage users of those versions to update their compiler. While we deem the problem unlikely to occur and go undetected in practical applications, which significantly lowers the overall severity, when it does happen, it may lead to serious issues. See Optimizer Bug Regarding Memory Side Effects of Inline Assembly for more information.
The second bug may result in a .push() operation on a bytes array in storage appending bytes that are not properly zero-initialized if the array was originally copied from memory or calldata. This, again, can be triggered only when using the legacy code generation pipeline. See Bug when Copying Dirty Bytes Arrays to Storage for more information.
Notable New Features
Improved Inlining Heuristics in Yul Optimizer
The compiler used to be very conservative in deciding whether to inline a function or not. This was necessary due to the fact that inlining may easily increase stack pressure and lead to the dreaded "Stack too deep" error.
Now that the mechanism for moving variables from stack to memory is available for the IR pipeline, we were able to relax the conditions necessary for inlining. The optimizer will now inline slightly bigger functions as long as nothing prevents the mechanism from being used. If you want to benefit from it, make sure that your inline assembly blocks are memory safe.
Our benchmarks show that the change significantly decreases the bytecode size (which impacts the deployment cost) while the effect on the runtime gas usage is smaller. The following table shows the overall differences between 0.8.14 and 0.8.15 that we observed when using the IR-based pipeline to run test suites of several real-life projects in our CI:
|project||Bytecode size||Deployment gas||Runtime gas|
|ENS||-4.85% ✅||-0.29% ✅|
|Euler||-2.43% ✅||-1.68% ✅||-2.08% ✅|
|Gnosis Safe||-3.87% ✅||-4.31% ✅||-0.02% ✅|
|Gnosis Protocol v2||-3.95% ✅||-2.55% ✅||-0.07% ✅|
|Perpetual Pools||-4.55% ✅||-2.64% ✅||-1% ✅|
|Pool Together||-7.09% ✅||-5.59% ✅||-0.37% ✅|
|PRBMath||-3.56% ✅||-3.49% ✅|
|Trident||-9.84% ✅||-7.98% ✅||-6.26% ✅|
|Uniswap v3||-4.32% ✅||-4.53% ✅||-1.24% ✅|
|Yield Liquidator||-7.13% ✅||-6.08% ✅||-0.5% ✅|
|OpenZeppelin||-7.83% ✅||-6.01% ✅||-0.28% ✅|
Error and Event Selectors
Note that for events the selector represents the topic 0, which, unlike error and function selectors, is not truncated to 4 bytes.
New or Improved Documentation
We have recently received vulnerability reports about cases where normal use of language features may leave the user with a dangling reference and lead to data being overwritten in storage. Unfortunately, this is a natural consequence of the current design of the language and not a bug that can be simply fixed. Solidity strongly restricts the use of references to value types, but the possibility to store references to elements of arrays of arrays in local variables together with the ability to resize them has always meant that the target of a reference may disappear. What the newly reported cases uncovered is that there are some unusual patterns which may produce dangling references in situations where they may not be expected. To make this more transpared and raise awareness we prepared a new documentation section that goes into more detail about the topic: Dangling References to Storage Array Elements. Thanks go to Kuroi with Binance Smart Contract Security Team and Red Team for reporting this!
- Code Generation: Avoid writing dirty bytes to storage when copying bytes arrays.
- Yul Optimizer: Keep all memory side-effects of inline assembly blocks.
- Add E.selector for a non-anonymous event E to access the 32-byte selector topic.
- LSP: Add rudimentary support for semantic highlighting.
- Type Checker: Warn about assignments involving multiple pushes to storage bytes that may invalidate references.
- Yul Optimizer: Improve inlining heuristics for via IR code generation and pure Yul compilation.
- ABI Encoder: When encoding an empty string coming from storage do not add a superfluous empty slot for data.
- Common Subexpression Eliminator: Process assembly items in chunks with maximum size of 2000. It helps to avoid extremely time-consuming searches during code optimization.
- Yul Optimizer: Do not remove returndatacopy in cases in which it might perform out-of-bounds reads that unconditionally revert as out-of-gas. Previously, any returndatacopy that wrote to memory that was never read from was removed without accounting for the out-of-bounds condition.
A big thank you to all contributors who helped make this release possible!
Download the new version of Solidity here.