check: narrow untyped float literals to f32 in f32 context (#120)
An untyped float literal defaults to f64, so in an f32 context it was materialized as f64 then bit-truncated by a raw MOVSS (low-32 reinterpret) rather than narrowed -- e.g. `let x: f32 = 2.0f32; x * 3.0` multiplied by 0.0f. Twelve byte-id-gate-blind both-wrong miscompiles, all this one cause (compare, binop, call-arg, struct-field, array-elem against an untyped literal). Broaden coerce_floatlit to stamp the untyped fconst type_=f32 across the f32-context sites (assign rhs, call-arg, struct-field, array-elem) and to descend the implicit-cast shapes (peel unary +/-/cast, recurse binop operands AND the binop node, recurse arrlit elems), mirroring harec's lower_implicit_cast. The existing CVTSD2SS gate then fires; cgen is unchanged. f64 contexts are untouched -- the stamp is gated on TY_F32. Surfaced by the float codegen sub-hunt (= the deferred #120). Pinned by test/lang/f32_untyped_narrow_test.ww (22 value-asserting rows incl. f64 controls; reddens on revert).
This commit is contained in:
138
test/lang/f32_untyped_narrow_test.ww
Normal file
138
test/lang/f32_untyped_narrow_test.ww
Normal file
@@ -0,0 +1,138 @@
|
||||
// f32_untyped_narrow_test — #120: an UN-suffixed float literal (defaults to
|
||||
// untyped_float/f64) used in an f32 context must narrow to single precision,
|
||||
// exactly as the f32-suffixed literal already does (#104 fold-1). Pre-#120 the
|
||||
// untyped literal stayed untyped_float through the checker, so cgen's CVTSD2SS
|
||||
// narrow (gated on the node's f32 stamp) never fired: the literal materialised
|
||||
// as a 64-bit double in X0 and the downstream f32 consumer MOVSS-read the low 4
|
||||
// bytes (0.0f for clean values; garbage for others). The fix stamps the literal
|
||||
// (and, for an arith binop, its operands AND result node) f32 in the checker at
|
||||
// every harec lower_implicit_cast site — binop operands, comparison sibling,
|
||||
// unary ±, assign rhs, param-typed call-arg, struct-field init, array element.
|
||||
//
|
||||
// Byte-id is BLIND to this class (both stages were wrong-but-identical), so the
|
||||
// asserts below are VALUE-asserting: each redden on pre-fix HEAD (0.0f / false);
|
||||
// the cstage `ww test` run is the live net, the T2 byte-id gate rides along.
|
||||
//
|
||||
// Test-design: ARITHMETIC rows use exactly-representable values (2/3/4/5/6) so a
|
||||
// rounded product can't false-redden; pure COERCION round-trips use a rounding
|
||||
// witness (0.1/0.2/1.3, whose f32 and f64 roundings differ) to also catch a
|
||||
// low-32 reinterpret and a wrong-rounding regression.
|
||||
|
||||
package f32_untyped_narrow_test;
|
||||
|
||||
type S = struct { f: f32 };
|
||||
|
||||
fn ca(a: f32) f32 = { return a; };
|
||||
|
||||
// --- binop ×4 (exact) + operand-order ---
|
||||
@test fn binop_mul() void = {
|
||||
let x: f32 = 2.0f32;
|
||||
assert(x * 3.0 == 6.0f32);
|
||||
};
|
||||
@test fn binop_add() void = {
|
||||
let x: f32 = 2.0f32;
|
||||
assert(x + 4.0 == 6.0f32);
|
||||
};
|
||||
@test fn binop_sub() void = {
|
||||
let x: f32 = 5.0f32;
|
||||
assert(x - 1.0 == 4.0f32);
|
||||
};
|
||||
@test fn binop_div() void = {
|
||||
let x: f32 = 8.0f32;
|
||||
assert(x / 2.0 == 4.0f32);
|
||||
};
|
||||
@test fn binop_lit_left() void = {
|
||||
let x: f32 = 2.0f32;
|
||||
assert(3.0 * x == 6.0f32);
|
||||
};
|
||||
|
||||
// --- compare ×6 (exact); the !=, >, >= rows assert a FALSE result (their
|
||||
// pre-fix low-32 garbage flips it true), so they need the negation. ---
|
||||
@test fn cmp_eq() void = {
|
||||
let y: f32 = 4.0f32;
|
||||
assert(y == 4.0);
|
||||
};
|
||||
@test fn cmp_ne() void = {
|
||||
let y: f32 = 4.0f32;
|
||||
assert(!(y != 4.0));
|
||||
};
|
||||
@test fn cmp_lt() void = {
|
||||
let y: f32 = 4.0f32;
|
||||
assert(y < 5.0);
|
||||
};
|
||||
@test fn cmp_gt() void = {
|
||||
let y: f32 = 4.0f32;
|
||||
assert(!(y > 5.0));
|
||||
};
|
||||
@test fn cmp_le() void = {
|
||||
let y: f32 = 4.0f32;
|
||||
assert(y <= 4.0);
|
||||
};
|
||||
@test fn cmp_ge() void = {
|
||||
let y: f32 = 4.0f32;
|
||||
assert(!(y >= 5.0));
|
||||
};
|
||||
|
||||
// --- unary neg (exact) ---
|
||||
@test fn unary_neg() void = {
|
||||
let z: f32 = -2.0;
|
||||
assert(z == -2.0f32);
|
||||
};
|
||||
|
||||
// --- assign rhs (witness) ---
|
||||
@test fn assign_rhs() void = {
|
||||
let w: f32 = 0.0f32;
|
||||
w = 0.1;
|
||||
assert(w == 0.1f32);
|
||||
};
|
||||
|
||||
// --- param-typed call-arg (witness) ---
|
||||
@test fn call_arg() void = {
|
||||
assert(ca(0.1) == 0.1f32);
|
||||
};
|
||||
|
||||
// --- struct-field init (witness) ---
|
||||
@test fn struct_field() void = {
|
||||
let s: S = S { f = 1.3 };
|
||||
assert(s.f == 1.3f32);
|
||||
};
|
||||
|
||||
// --- array element (witness, multi-leaf: each untyped leaf coerces alone) ---
|
||||
@test fn array_elem() void = {
|
||||
let a: [2]f32 = [0.1, 0.2];
|
||||
assert(a[0] == 0.1f32);
|
||||
assert(a[1] == 0.2f32);
|
||||
};
|
||||
|
||||
// --- let-store (witness) ---
|
||||
@test fn let_store() void = {
|
||||
let v: f32 = 1.3;
|
||||
assert(v == 1.3f32);
|
||||
};
|
||||
|
||||
// --- literal-on-BOTH-sides under an f32 target (exact, must-green): the
|
||||
// recursion narrows per-leaf AND stamps the binop result f32, so it computes
|
||||
// 5.0f directly (no f64 intermediate). reddens with garbage if either half
|
||||
// of the narrow fails. ---
|
||||
@test fn both_sides() void = {
|
||||
let q: f32 = 2.0 + 3.0;
|
||||
assert(q == 5.0f32);
|
||||
};
|
||||
|
||||
// --- CONTROL rows: green pre AND post-fix (no regression / no over-stamp). ---
|
||||
@test fn ctrl_suffixed() void = {
|
||||
let s: f32 = 0.1f32;
|
||||
assert(s == 0.1f32);
|
||||
};
|
||||
@test fn ctrl_explicit_cast() void = {
|
||||
assert((4.0: f32) == 4.0f32);
|
||||
};
|
||||
@test fn ctrl_no_f32_context() void = {
|
||||
// no f32 anywhere → both literals default to f64; stays f64, false.
|
||||
let b: bool = (4.0 == 5.0);
|
||||
assert(b == false);
|
||||
};
|
||||
@test fn ctrl_f64_untouched() void = {
|
||||
let d: f64 = 1.3;
|
||||
assert(d == 1.3f64);
|
||||
};
|
||||
Reference in New Issue
Block a user