The compiler folds 1 << -1 into PUSHNAN at compile time, but the equivalent runtime operation throws range_chk (exit code 5). This changes observable behavior based on whether the shift amount is a constant.
const BAD_SHIFT: int = 1 << -1;
@noinline
fun const_val(): int {
return BAD_SHIFT;
}
@noinline
fun runtime_val(shift: int): int {
return 1 << shift;
}
fun onInternalMessage(): (int, int) {
return (const_val(), runtime_val(-1));
}
Compiles to:
const_val() PROC:<{
PUSHNAN // const path: silently returns NaN
}>
runtime_val() PROC:<{ // shift
POW2 // runtime path: throws for shift=-1
}>
onInternalMessage() PROC:<{
const_val() CALLDICT
-1 PUSHINT
runtime_val() CALLDICT
}>
The runtime path throws exit code 5 (integer out of range) because POW2 range-checks its argument. The constant-folded version bypasses this and silently produces NaN.
Same input, different behavior. The compiler should reject 1 << -1 at compile time rather than folding it to NaN.