What happens when an integer overflows in Solidity, and how did version 0.8 change the rules?
Before 0.8, integers silently wrapped around (a uint8 at 255 plus 1 becomes 0); from Solidity 0.8 all arithmetic is checked by default and reverts on overflow, with unchecked { } as an explicit opt-out.
* The same expression, two fates: 0.8+ checked arithmetic reverts; pre-0.8 (or unchecked { }) silently wraps to 0 *
uint8 x = 255;
x = x + 1; // 0.8+: the transaction reverts (checked arithmetic)
unchecked { x = x + 1; } // explicit opt-out: wraps to 0, saves a little gas
A uint8 holds 0–255, so adding 1 to 255 cannot be represented. Older Solidity (before 0.8) handled this the way raw CPUs do: the value wrapped around to 0 without any warning — the cause of real money-losing bugs, which is why contracts routinely imported the SafeMath library just to turn every overflow into a revert. Solidity 0.8 built that protection into the language: plain +, -, * now spend a few extra gas on the check and revert the transaction on overflow, making SafeMath unnecessary. Writing unchecked { ... } restores the old wrapping behaviour for places where you have proven overflow is impossible and want the gas back.
Go deeper:
Solidity docs: checked or unchecked arithmetic — the exact semantics of the 0.8 default and the
uncheckedblock.