Surface rename of the signed integer family: s1..s64 become i1..i64
(u1..u64, usize, isize unchanged). 'string' keeps the s-prefix arm in
name classification; width parsing moves to the i-prefix arm next to
isize.
Internal TypeId tags follow the surface (.s8/.s16/.s32/.s64 ->
.i8/.i16/.i32/.i64), as do mono-key mangle fragments (ptr_i64,
tu_i64_bool) and all display/diagnostic formatting (i{d}).
Migrated in the same sweep: stdlib + examples + issue repros + FFI C
companions (shared symbol names like ffi_id_i64), expected
stdout/stderr/ir snapshots, specs.md, readme.md, CLAUDE.md/AGENTS.md,
implementation_plan.md, docs/, issue writeups. Vendored stb_image and
historical flow state left untouched.
zig build test: 426/426; examples suite: 595/595.
77 lines
4.2 KiB
Plaintext
77 lines
4.2 KiB
Plaintext
// 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;
|
|
}
|