// Numeric-limit accessor vs. a raw value binding that shadows a builtin type // name. A backtick raw identifier (F0.6) can legitimately bind a value whose // spelling is a reserved numeric type name (`` `f64 ``, `` `i32 ``, `` `u8 ``). // Field access on such a value is an ORDINARY field read — the numeric-limit // intercept (NL.1 integer `.min`/`.max`, NL.2 float `.epsilon`/… ) must NOT // hijack it. An adjacent BARE `f64.epsilon` / `i32.max` / `u8.max` — which the // parser classifies as a type receiver, not the raw value — STILL folds to the // numeric limit. Both behaviors coexist: the raw receiver reads the value, the // bare receiver folds the limit. // // A raw value binding can reach the intercept through THREE sources, exactly // mirroring the ordinary identifier field-access path (scope / globals / module // consts). This example exercises all three: a GLOBAL `` `f32 ``, a MODULE-CONST // `` `i16 ``, and LOCAL `` `f64 ``/`` `i32 ``/`` `u8 `` — each reads its field, // and the bare spelling of each STILL folds. // // Regression (issues 0092 local, 0093 global + module-const): the intercept // previously treated any identifier whose text matched a builtin numeric type // name as a TYPE receiver, silently shadowing the in-scope value binding // (`` `f64.epsilon `` folded to 2^-52, `` `i32.max `` folded to 2147483647 — // a silent wrong value). The attempt-3 fix guarded only lexical scope, so // GLOBAL and MODULE-CONST raw bindings still folded (issue 0093). #import "modules/std.sx"; FBox :: struct { epsilon: i64; max: i64; min_positive: i64; } IBox :: struct { max: i64; min: i64; } UBox :: struct { max: i64; } // GLOBAL raw value binding whose spelling shadows the builtin `f32`. Reachable // via `program_index.global_names`, not lexical scope (issue 0093). `f32 := FBox.{ epsilon = 44, max = 55, min_positive = 66 }; // MODULE-CONST raw value binding whose spelling shadows the builtin `i16`. // Reachable via `program_index.module_const_map` (issue 0093, const variant). `i16 :: IBox.{ max = 99, min = -99 }; main :: () -> i32 { // LOCAL raw value bindings whose spelling shadows a builtin numeric type name. `f64 := FBox.{ epsilon = 11, max = 22, min_positive = 33 }; `i32 := IBox.{ max = 78, min = -78 }; `u8 := UBox.{ max = 7 }; // Raw receiver → ordinary field READ (the value), never the numeric limit. print("local f64: epsilon={} max={} min_positive={}\n", `f64.epsilon, `f64.max, `f64.min_positive); // 11 22 33 print("local i32: max={} min={}\n", `i32.max, `i32.min); // 78 -78 print("local u8: max={}\n", `u8.max); // 7 // GLOBAL raw receiver → ordinary field READ (issue 0093). print("global f32: epsilon={} max={} min_positive={}\n", `f32.epsilon, `f32.max, `f32.min_positive); // 44 55 66 // MODULE-CONST raw receiver → ordinary field READ (issue 0093). print("const i16: max={} min={}\n", `i16.max, `i16.min); // 99 -99 // The value-field read carries the field type (i64 here): round-trips // through a typed binding, so a mistyped/boxed read would not type-check. e : i64 = `f64.epsilon; print("typed val e={}\n", e); // 11 // Bare receiver (a type receiver, NOT the raw value) → STILL folds to the // numeric limit, even though a LOCAL (`i32`/`u8`/`f64`), GLOBAL (`f32`), or // MODULE-CONST (`i16`) value of the same spelling is bound. The bare receiver // is never blocked by any of the three value sources. print("lim i32.max={} i32.min={}\n", i32.max, i32.min); // 2147483647 -2147483648 print("lim u8.max={}\n", u8.max); // 255 print("lim i16.max={} i16.min={}\n", i16.max, i16.min); // 32767 -32768 // Bare float accessors still fold; the formatter is crude (issue 0090), so // pin the values by their defining properties rather than by printing. print("lim (1.0+f64.epsilon)!=1.0: {}\n", (1.0 + f64.epsilon) != 1.0); // true print("lim f64.inf > f64.max: {}\n", f64.inf > f64.max); // true print("lim f64.min == -f64.max: {}\n", f64.min == -f64.max); // true print("lim f32.inf > f32.max: {}\n", f32.inf > f32.max); // true return 0; }