Problem: is_within_range<I>(F value) — where I is an integer and F is a
floating-point type — is unexpectedly too permissive in some cases:
a. Values that are 1 past the integer range unexpectedly pass; e.g.,
is_within_range<int>(2147483648.0f) returns true — even though
2147483648 is INT_MAX + 1.
b. Fractional values whose magnitude exceeds the destination max
unexpectedly pass; e.g., is_within_range<unsigned>(4294967295.5)
returns true — even though 4294967295.5 > UINT_MAX.
c. Fractional values within the destination’s numeric range unexpectedly
pass (e.g., is_within_range<int>(2.5) returns true) — even though
they aren’t exactly representable as the destination type.
Cause: TypeBoundsChecker integer-bounds specializations compare against
NumericLimits<Destination>::max() and ::min() directly. When a caller’s
value is a float, the integer max/min get implicitly converted to a
float for the comparison. For Destination/Source pairs with the integer
extreme not exactly representable in the float, that conversion rounds
up to the next power-of-two boundary — so “value <= F(max)” accepts
values that are actually out of range by one (case a). And the
comparison itself doesn’t reject fractional values (cases b and c).
Fix: When Source is a floating-point type:
1. First gate (case a) — Compare against 2^digits; exactly representable
in any IEEE float, and equals max + 1 for unsigned / -min for two’s-
complement signed integers.
2. Second gate (cases b and c) – Round-trip check: cast value to
Destination, then cast back — and require equality. Only integer-
valued floats whose truncation matches the original pass.
Fixes https://github.com/LadybirdBrowser/ladybird/issues/6212
We could never hit the #else branches for some of these methods, because
we already relied on having __builtin_*_overflow() readily available in
earlier methods.
multiplication_would_overflow() with three arguments was only used in a
test, so let's get rid of that as well.
Before, adding an overflow'n `Checked<T>` to another `Checked<T>` would
cause a verification faliure when instead it should propogate m_overflow
and allow the user to handle the overflow.
The following command was used to clang-format these files:
clang-format-18 -i $(find . \
-not \( -path "./\.*" -prune \) \
-not \( -path "./Base/*" -prune \) \
-not \( -path "./Build/*" -prune \) \
-not \( -path "./Toolchain/*" -prune \) \
-not \( -path "./Ports/*" -prune \) \
-type f -name "*.cpp" -o -name "*.mm" -o -name "*.h")
There are a couple of weird cases where clang-format now thinks that a
pointer access in an initializer list, e.g. `m_member(ptr->foo)`, is a
lambda return statement, and it puts spaces around the `->`.