Description
Right now, the body in
export function test1(x: f32): bool {
return 5.0 < x
}
infers as someF64 < f64(someF32)
(sees 5.0
, eagerly picks f64
), compiling to
local.get $0
f64.promote_f32
f64.const 5
f64.gt
while inverting the operation to
export function test1_inv(x: f32): bool {
return x >= 5.0;
}
compiles to
local.get $0
f32.const 5
f32.ge
Similarly,
export function test2(x: f32): bool {
return 5 < x
}
infers as someI32 < someF32
(sees 5
, picks i32
), then refuses to compile with
ERROR TS2365: Operator '<' cannot be applied to types 'i32' and 'f32'.
while inverting the operation to
export function test2_inv(x: f32): bool {
return x >= 5;
}
compiles to
local.get $0
f32.const 5
f32.gt
In other places, like the max<T>
builtin, what we instead do is to infer based on the RHS if the LHS is a numeric literal, which can be done because the numeric literal on the LHS has no side-effects anyway, so the RHS can be compiled prior. The mechanism could be extended to binary operations like the above, but then is a breaking change. There are arguments to be made that right now, the rules are at least straight-forward, while after, the rules would become somewhat magical, including potential unexpected precision loss. The latter can be mitigated by a) consequently warning when a literal does not fit into the designated respectively inferred type or b) by upgrading, say, an f32
to an f64
literal if the value otherwise wouldn't fit. Option b) highlights other magical properties that might be unexpected.