fix(ir): validate const-expression typed module-const initializers [F0.7]
Attempt 1 rejected only LITERAL initializers that mismatch a typed module
const's annotation; a const-EXPRESSION initializer escaped, so the same
issue-0088 root remained for `M :: 2; N : string : M + 2` — accepted at exit 0,
folding `[N]s64` to 4 and printing N as an integer.
Root cause: `registerTypedModuleConst` validated only the enumerated literal
node kinds; any other kind fell through to `else => {}`, and pass 0
pre-registers binary_op/unary_op consts as a `.s64` placeholder that was never
reconciled with the annotation.
Fix — validate by TYPE, not by node kind:
- lower.zig: `registerTypedModuleConst` now covers literals AND const-expressions
(binary_op/unary_op) through one path. `typedConstInitFits` keeps the literal
arms and routes any non-literal through the new `constExprInitFits`, which
compares the initializer's INFERRED type (`inferExprType`, the existing
type-inference facility — no second const evaluator) to the annotation with the
same integer/float compatibility. A mismatch emits the `type mismatch` diagnostic
(a const-expression is described by its inferred type, e.g. "an integer
expression") and evicts the pass-0 placeholder; a match registers the const at
its resolved annotation type (the same `put` the literal path always did), so a
const-expression folds and emits at its declared type.
- `literalKindName` → `initializerDescription` (+ `constExprDescription`) so the
message is accurate for both a literal and a const-expression initializer.
Regression:
- examples/1143: extended with `E : string : M + 2` and `V : string : -M`
(const-expr mismatches → exit 1, pinned diagnostics).
- examples/0162: extended with `KE : s64 : M + 2` (used as a count + printed) and
`WE : f32 : M + 2` (over-rejection guard — valid const-exprs still work).
- program_index.test.zig: count-gate test extended with a binary_op value node
declared `string` (must not fold as a count).
Docs: specs.md §3 + readme.md generalized from "initializer literal" to cover
constant expressions; issues/0088 RESOLVED banner updated.
This commit is contained in:
@@ -11,31 +11,45 @@
|
||||
> folded the const into an integer COUNT by inspecting the `int_literal` node
|
||||
> alone, ignoring `ModuleConstInfo.ty` (so `[N]s64` folded to 4).
|
||||
>
|
||||
> Both LITERAL initializers (`N : string : 4`) and const-EXPRESSION initializers
|
||||
> (`M :: 2; N : string : M + 2`, `V : string : -M`) are rejected — the validation
|
||||
> is type-based, so a non-literal node kind can no longer escape it (attempt 2).
|
||||
>
|
||||
> **Fix per file.**
|
||||
> - `src/ir/lower.zig` — `registerTypedModuleConst` now validates the
|
||||
> initializer against the resolved annotation via the new `typedConstInitFits`
|
||||
> (arms mirror `emitModuleConst`'s faithful-emit precondition: int → int/float,
|
||||
> float → float, bool → bool, string → string, null → pointer/optional,
|
||||
> `---` → any). A mismatch emits `type mismatch: constant '<n>' is declared
|
||||
> '<ty>' but its initializer is <kind>` at the initializer span and does NOT
|
||||
> register the const (it also evicts the pass-0 placeholder so a count use
|
||||
> can't still fold it). `literalKindName` names the literal kind for the
|
||||
> message.
|
||||
> - `src/ir/program_index.zig` — `moduleConstInt` / `moduleConstIntFramed` now
|
||||
> take the `TypeTable` and gate the fold on `isCountableConstType(ci.ty)`
|
||||
> (integer of any width, or a float), so a non-numeric typed const can never be
|
||||
> folded into a count off its initializer node. Callers in `lower.zig` and
|
||||
> `type_bridge.zig` updated.
|
||||
> - `src/ir/lower.zig` — `registerTypedModuleConst` validates the initializer
|
||||
> against the resolved annotation BY TYPE, covering literals AND
|
||||
> const-expressions (binary_op / unary_op) uniformly. `typedConstInitFits`
|
||||
> keeps the literal arms (int → int/float, float → float, bool → bool,
|
||||
> string → string, null → pointer/optional, `---` → any) and routes any
|
||||
> non-literal through `constExprInitFits`, which compares the initializer's
|
||||
> INFERRED type (`inferExprType`, the existing type-inference facility — no
|
||||
> second const evaluator) to the annotation with the same integer/float
|
||||
> compatibility. A mismatch emits `type mismatch: constant '<n>' is declared
|
||||
> '<ty>' but its initializer is <desc>` at the initializer span (a literal
|
||||
> names its kind; a const-expression is described by its inferred type, e.g.
|
||||
> "an integer expression"), and does NOT register the const — it evicts the
|
||||
> pass-0 placeholder so a count use can't still fold it. On a MATCH the const is
|
||||
> registered at its resolved annotation type (the same `put` the literal path
|
||||
> always did), so a const-expression folds and emits at its declared type.
|
||||
> - `src/ir/program_index.zig` — `moduleConstInt` / `moduleConstIntFramed` take
|
||||
> the `TypeTable` and gate the fold on `isCountableConstType(ci.ty)` (integer
|
||||
> of any width, or a float), so a non-numeric typed const can never be folded
|
||||
> into a count off its initializer node — whether that node is a literal or a
|
||||
> foldable integer expression. Callers in `lower.zig` and `type_bridge.zig`
|
||||
> updated.
|
||||
>
|
||||
> **Regression tests.**
|
||||
> - `examples/1143-diagnostics-typed-module-const-mismatch.sx` — negative: four
|
||||
> mismatch shapes (`int→string`, `string→s64`, `bool→s64`, `float→s64`) each
|
||||
> emit a `type mismatch` diagnostic, exit 1.
|
||||
> - `examples/0162-types-typed-module-const-roundtrip.sx` — positive: valid
|
||||
> typed consts (`s64` as count + printed, `f32` from int, `f32` float,
|
||||
> `string`, `*void` null) compile, fold, and print correctly.
|
||||
> - `examples/1143-diagnostics-typed-module-const-mismatch.sx` — negative: six
|
||||
> mismatch shapes — four literal (`int→string`, `string→s64`, `bool→s64`,
|
||||
> `float→s64`) and two const-expression (`M + 2 → string`, `-M → string`) —
|
||||
> each emit a `type mismatch` diagnostic, exit 1.
|
||||
> - `examples/0162-types-typed-module-const-roundtrip.sx` — positive: valid typed
|
||||
> consts (`s64` as count + printed, `f32` from int, `f32` float, `string`,
|
||||
> `*void` null, plus const-expression `s64 : M + 2` used as a count + printed
|
||||
> and `f32 : M + 2`) compile, fold, and print correctly.
|
||||
> - `src/ir/program_index.test.zig` — `moduleConstInt gates the fold on the
|
||||
> declared type, not the initializer node`.
|
||||
> declared type, not the initializer node` (covers both a literal and a
|
||||
> binary_op value node declared with a non-numeric type).
|
||||
|
||||
# 0088 — Typed module const annotation mismatch is accepted
|
||||
|
||||
|
||||
Reference in New Issue
Block a user