The compiler folds 1 << 1024 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 SHIFT: int = 1024;
const CONST_SHIFTED: int = 1 << SHIFT;
@noinline
fun runtime_shift(x: int, s: int): int { return x << s; }
fun onInternalMessage(s: int) {
return (CONST_SHIFTED, runtime_shift(1, s));
}
Compiles to:
runtime_shift() PROC:<{ // x s
LSHIFT
}>
onInternalMessage() PROC:<{ // s
PUSHNAN // const path: silently returns NaN
1 PUSHINT
ROT
runtime_shift() CALLDICT // runtime path: throws range_chk for s=1024
}>
TVM's LSHIFT throws range_chk (exit code 5) for shift counts outside 0–1023. The constant-folded version bypasses this check entirely and returns NaN instead.
For s = 1024, the runtime path throws while the const path silently yields NaN. The compiler should reject out-of-range constant shifts at compile time.