Repository navigation
Commit 24a3be5
Fix flex shrink explosion when the shrink total is a positive float residual
Summary:
Fixes react/yoga#2038
Fixes react/yoga#1651
Closes react/yoga#2039
Closes react/yoga#1855
Thanks to sebholl for the analysis and the reproduction in react/yoga#2038.
## Issue
When a flex line overflows, the first pass freezes the items that hit their minimum size and removes them from the shrink total. The second pass then divides the remaining free space by that total.
The total was computed by subtracting the frozen items flex shrink factors from the float total. When every item freezes, the total should become 0, but float subtraction leaves a small residual.
### Example
All items have `flexShrink: 1`:
```
row (width: 350, flexDirection: row)
├── A width: 100.1 minWidth: 90
├── B width: 200.3 minWidth: 190
└── C width: 300.7 minWidth: 290
```
The items need 601.1 but the row is only 350, so they must shrink by 251.1. Each item would go below its `minWidth`, so the first pass freezes all three (at 90 / 190 / 290) and removes them from the shrink total. With no items left, the total should be 0. But in float, `-601.1 + 100.1 + 200.3 + 300.7 = 0.000061`.
That is above the `1e-6` guard added in react/yoga#1974 (for react/yoga#1665), so the second pass divides the remaining free space by it: `-220 / 0.000061 ≈ -3.6 million`. Instead of staying at their min, the items grow to about **361M / 722M / 1084M** (expected **90 / 190 / 290**).
### Solution
With non-negative `flexShrink`, every scaled shrink factor is ≤ 0, so a real total is never positive. A positive total can only be this float residual, and dividing by it is what makes the items explode. The second pass now treats a positive total like the near-zero case of the `1e-6` guard from react/yoga#1974 and uses the existing fallback. Negative residuals (which the min clamp already handles) and all other layouts are unchanged. Fixing the fallback for `flexShrink < 1` (react/yoga#931) is left for a separate PR.
#### Why not `inexactEquals` (|total| < 0.0001)?
The leftover grows with the size of the total. For example, 60 chips of about 100pt each in a 1000pt row give a total of about 6000, and the leftover is then often larger than 0.0001. In a test with rows like this, about half of them still exploded with `inexactEquals`. With the sign check, none did, because a real shrink total is never positive.
X-link: react/yoga#2044
Reviewed By: javache
Differential Revision: D126038629
Pulled By: cortinico
fbshipit-source-id: dc1ec3370783e227ce4477bcd632ae59070341931 parent d63648b commit 24a3be5
1 file changed
Lines changed: 6 additions & 2 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1138 | 1138 | | |
1139 | 1139 | | |
1140 | 1140 | | |
1141 | | - | |
| 1141 | + | |
1142 | 1142 | | |
| 1143 | + | |
| 1144 | + | |
| 1145 | + | |
1143 | 1146 | | |
1144 | 1147 | | |
1145 | 1148 | | |
1146 | | - | |
| 1149 | + | |
| 1150 | + | |
1147 | 1151 | | |
1148 | 1152 | | |
1149 | 1153 | | |
| |||
0 commit comments