fix(ir): reject typed module const whose initializer mismatches annotation [F0.7]

A typed module-level constant whose initializer did not match its
annotation was silently accepted: `N : string : 4` compiled, then
`print(N)` segfaulted (an integer emitted as a `string` const → a bogus
pointer) and `[N]s64` folded `N` to 4 as an integer count. Issue 0088.

Root cause: `registerTypedModuleConst` stored the annotation type but never
validated the initializer literal against it, and
`program_index.moduleConstInt` folded a const into a count by inspecting
the initializer node alone, ignoring `ModuleConstInfo.ty`.

Fix at the declaration (kills both symptoms):
- lower.zig: `registerTypedModuleConst` now validates the initializer via
  `typedConstInitFits` (arms mirror `emitModuleConst`'s faithful-emit
  precondition: int→int/float, float→float, bool→bool, string→string,
  null→pointer/optional, `---`→any). A mismatch emits a `type mismatch`
  diagnostic at the initializer span and does not register the const (also
  evicting the pass-0 placeholder). Not routed through
  `coercionResolver().classify`: that runtime-coercion planner is unsound
  here (null's natural type is void → false-rejects `*T`; bool is 1 bit →
  false-accepts s64).
- program_index.zig: `moduleConstInt` now takes the `TypeTable` and gates
  the fold on `isCountableConstType(ci.ty)` (integer of any width, or a
  float), so a non-numeric typed const can never fold into a count off its
  initializer node. Callers in lower.zig and type_bridge.zig updated.

Regression:
- examples/1143-diagnostics-typed-module-const-mismatch.sx (negative, exit 1)
- examples/0162-types-typed-module-const-roundtrip.sx (positive)
- program_index.test.zig: gate-on-declared-type unit test

Docs: specs.md §3 Constant Binding + readme.md note the compatibility rule.
This commit is contained in:
agra
2026-06-05 07:17:20 +03:00
parent 3edb60762d
commit 156edf8e28
15 changed files with 326 additions and 16 deletions

View File

@@ -114,6 +114,11 @@ y : s32 = 0; // explicit type
z : s32 = ---; // uninitialized
```
A typed constant's initializer must be compatible with its annotation — an
integer literal fits any integer or float, a float a float type, a string
`string`, `null` a pointer/optional. A mismatch like `N : string : 4` is a
compile-time `type mismatch` error, not a silently-accepted constant.
Builtin type names (`s2`, `u8`, `bool`, `string`, …) are reserved and a *bare*
spelling can't be used as an identifier at a **value-binding or declaration-name**
site — a value binding (`:=` / typed local / parameter), a `::` constant or