Stop the war on for loops
- Dominant language
- C++
- Stars
- 25.7k
- Forks
- 6.2k
- Avg merge
- 1d 11h
- Merged PRs (30d)
- 21
Description
## Abstract
Add a specific overflow check optimization that will make many for-loop optimizations unnecessary.
## Motivation
Too many people are saying this is bad:
```solidity
for (uint256 mintCounter = 0; mintCounter < quantity; mintCounter++) {
_mint(msg.sender, _assembleTokenID(dropID, mintCounter));
}
```
compared to this:
```solidity
for (uint256 mintCounter = 0; mintCounter < quantity;) {
_mint(msg.sender, _assembleTokenID(dropID, mintCounter));
unchecked{
mintCounter++
}
}
```
## Specification
### Note
For reference, a for-loop like this:
```
for (A; B; C) {D}
```
is implemented like this:
```basic
10 A
100 if !B GOTO 999
110 D
120 C
130 GOTO 100
999 EXIT
```
### New optimization
If inside a single code unit, if:
1. a variable has bit width X,
2. the variable is compared to be less than some quantity with bit width <=X,
3. the variable is not set until step 4 here, then
4. the variable is incremented;
then that incrementation in step 4 need not be safety checked. Or in $\LaTeX$:
$$x < a <= w \implies x+1 <= w$$
### Documentation
People like being fancy, so even if this is implemented, they will still use the `unchecked` "optimization" until it can be clearly explained that it is unnecessary.
So the documentation should be updated to specify that the gas cost of this code:
```solidity
contract C {
event E;
constructor(uint256 count) {
for (uint256 i = 0; i < count; i++) {
emit E(i);
}
}
}
```
shall not exceed that of this code:
```solidity
contract CUnchecked {
event E;
constructor(uint256 count) {
for (uint256 i = 0; i < count;) {
unchecked {
emit E(i);
i++;
}
}
}
}
```
And this can be checked with a test case.
There is precedent in this with JavaScript where the specification requires that implementations implement tail-end recursion optimization.
## Backwards Compatibility
n/a
---
Inspired by the discussion with @FrankNFT-labs at https://github.com/LightArtists/light-smart-contracts/issues/2
Contributor guide
Research direction
Start by locating the compiler's handling of Solidity for-loop increments and the documentation section covering loop gas costs. Use the two constructor examples in the issue as the behavioral comparison, then add a test case showing that the checked and unchecked forms have no greater gas-cost difference after the optimization.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp, solidity
- Domain
- compilers, documentation, testing
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 30/100