The issue-0092 fix guarded the numeric-limit accessor intercept against
raw value shadowing using only lexical Scope.lookup. The ordinary
identifier field-access path resolves a value through THREE sources
(scope / program_index.global_names / program_index.module_const_map),
so a backtick raw identifier bound at module scope — a global
`` `f64 := Box.{…} `` or a module constant `` `f64 :: Box.{…} `` — still
folded `` `f64.epsilon `` to the numeric limit instead of reading the
value's field (issue 0093, plus the module-const variant: same root
cause, same fix).
Fix: a single shared helper Lowering.identifierBindsValue(name) that
returns true when the name resolves through scope OR global_names OR
module_const_map. Used in BOTH lowerNumericLimit (lower.zig) and the
numeric-limit inference arm (expr_typer.zig) so the two resolvers can't
desync (issue-0083 class). A bare `f64.epsilon` / `s32.max` (a
.type_expr receiver) still folds even when a raw value of the same
spelling is bound — the bare receiver is never value-shadowed.
- examples/0161: extended to exercise all three binding kinds — a
GLOBAL `` `f32 ``, a MODULE-CONST `` `s16 ``, and LOCAL
`` `f64 ``/`` `s32 ``/`` `u8 `` — each reading its field while the
bare spelling still folds.
- src/ir/expr_typer.test.zig: unit test pinning the global +
module-const sources of the shared guard.
- issues/0093: RESOLVED banner (3-source root cause + fix, module-const
variant folded in).
- specs.md / readme.md: numeric-limit shadow note now source-agnostic
(local / global / module-const).
14 lines
384 B
Plaintext
14 lines
384 B
Plaintext
local f64: epsilon=11 max=22 min_positive=33
|
|
local s32: max=78 min=-78
|
|
local u8: max=7
|
|
global f32: epsilon=44 max=55 min_positive=66
|
|
const s16: max=99 min=-99
|
|
typed val e=11
|
|
lim s32.max=2147483647 s32.min=-2147483648
|
|
lim u8.max=255
|
|
lim s16.max=32767 s16.min=-32768
|
|
lim (1.0+f64.epsilon)!=1.0: true
|
|
lim f64.inf > f64.max: true
|
|
lim f64.min == -f64.max: true
|
|
lim f32.inf > f32.max: true
|