Dart AOT Gets Math Wrong: Debug Outputs 0.125, Release Outputs 2299087609772638200
Flutter release builds run under AOT, so any app that mixes numeric types from dynamic sources like JSON can silently crash or return corrupted numbers on ARM64. This is a concrete reliability trap for production Dart code.
Dart's AOT compiler can miscompile mathematical code that mixes int and double values. In a Dart VM JIT, `min(0.965, jsonDecode('{"value":0}'))` returns 0 and `0.035 + 0` gives 0.035. Under AOT, the same code triggers SIGSEGV on ARM64 because the compiler assumes the return value is a Double, treats the Smi-tagged integer zero as an object reference, and tries to read a float from address 0x7.
The second example is worse: `max(0, 0.25) * 0.5` computes 0.125 in JIT, but AOT returns 2299087609772638200.0. The root cause is that `min` and `max` are generic with `T extends num`, so arguments can be int or double. When the value comes from a JSON-decoded `Map` with static type `dynamic`, the compiler's `InlineMathMinMax` optimization can't guarantee both operands are doubles, but its ARM64 backend still emits code that unboxes the result as a Double. The result is a wrong machine-code path that reads a valid small integer as a pointer.
The bug also appears in Flutter 3.44.9 via register-argument passing, where a default 1.0 value is treated as an address. The issue is tracked in dart-lang/sdk#64878, but the exact compiler boundary that causes it remains unclear.
The error is not a rounding issue but a type-unsafe machine-code path: the compiler assumes a value is a Double reference when it is actually a Smi integer.
AOT's aggressive optimization can produce silent numeric corruption, not just crashes, which is harder to detect in production.
Dynamic typing from `jsonDecode` combined with Dart's generic `num` creates a type-inference gap that the compiler doesn't close before emitting assembly.
The discussion turns on Dart's type design and the recurrence of debug/release divergences. One view holds that Dart's num design is ugly, implying the mixed int/double overloads are at fault. Another notes that debug/release mismatches have happened several times before, suggesting a pattern of AOT reliability problems.
Dart's num design feels ugly.
It seems like this kind of thing—debug fine, release broken—has happened several times already.