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.
7.0 KiB
RESOLVED (F0.7) — A typed module-level constant whose initializer does not match its annotation is now rejected at the declaration with a clear
type mismatchdiagnostic, killing both symptoms (theprint(N)segfault and the[N]i64→ 4 fold).Root cause.
registerTypedModuleConst(src/ir/lower.zig) stored the annotation type on the const but never checked the initializer literal against it, soN : string : 4registered as{value = int 4, ty = string}.emitModuleConstthen stamped theint_literalwith thestringtype (a bogus pointer → segfault at the use site), andprogram_index.moduleConstIntfolded the const into an integer COUNT by inspecting theint_literalnode alone, ignoringModuleConstInfo.ty(so[N]i64folded 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).Mixed-numeric escape closed at the type-system root (attempt 3). The type-based validation reuses
inferExprType, which inferred a non-comparison binary op from its LHS alone — soBAD : i64 : M + 0.5(i64 + f64) inferredi64and was accepted+truncated, while0.5 + Minferredf64and was rejected: operand-order-dependent. The fix is in the binary-op arm ofExprTyper.inferType(src/ir/expr_typer.zig): arithmetic / bitwise / shift ops now infer the PROMOTED result of(lhs, rhs)viaLowering.arithResultType— the same int×float → float rulelowerBinaryOpalready applied for the value (extracted from its inline block into a shared helper, so the two can't diverge).M + 0.5now infersf64in either operand order, so the typed-const validation rejects it against ani64annotation with no special-casing in the validation logic itself. This was a pre-existing inference bug broader than typed consts (it also mis-formattedprint("{}", M + 0.5)as a truncated int); the typed-LOCALy : i64 = 1.5→ 1 narrowing is a SEPARATE assignment-coercion bug tracked as issue 0095.Fix per file.
src/ir/lower.zig—registerTypedModuleConstvalidates the initializer against the resolved annotation BY TYPE, covering literals AND const-expressions (binary_op / unary_op) uniformly.typedConstInitFitskeeps the literal arms (int → int/float, float → float, bool → bool, string → string, null → pointer/optional,---→ any) and routes any non-literal throughconstExprInitFits, 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 emitstype 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 sameputthe literal path always did), so a const-expression folds and emits at its declared type.src/ir/program_index.zig—moduleConstInt/moduleConstIntFramedtake theTypeTableand gate the fold onisCountableConstType(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 inlower.zigandtype_bridge.zigupdated.Regression tests.
examples/1143-diagnostics-typed-module-const-mismatch.sx— negative: eight mismatch shapes — four literal (int→string,string→i64,bool→i64,float→i64), two const-expression (M + 2 → string,-M → string), and two mixed-numeric (i64 : M + 0.5andi64 : 0.5 + M, rejected in BOTH operand orders) — each emit atype mismatchdiagnostic, exit 1.examples/0162-types-typed-module-const-roundtrip.sx— positive: valid typed consts (i64as count + printed,f32from int,f32float,string,*voidnull, const-expressioni64 : M + 2used as a count + printed,f32 : M + 2, plus mixed-numericf64 : M + 0.5andf64 : 0.5 + Mfolding to 2.5 in both orders) compile, fold, and print correctly.examples/0163-types-mixed-numeric-promotion.sx— positive: pins the inferExprType promotion DIRECTLY in a value context (print("{}", n + 0.5)formats as the float2.5, both operand orders, across+ - * /and an f32 operand; a pure-int expression stays an integer).src/ir/program_index.test.zig—moduleConstInt gates the fold on the declared type, not the initializer node(covers both a literal and a binary_op value node declared with a non-numeric type).src/ir/expr_typer.test.zig—arithResultType promotes int×float to the float regardless of operand order(the shared promotion helper).
0088 — Typed module const annotation mismatch is accepted
Symptom
A module-level typed constant whose initializer does not match its annotation is
accepted. Observed: N : string : 4 compiles; printing N segfaults, and using
N as an array dimension folds it as 4. Expected: the const declaration emits
a type-mismatch diagnostic and no downstream use treats it as a valid string or
integer count.
Reproduction
#import "modules/std.sx";
N : string : 4;
main :: () {
print("N={}\n", N);
}
Related count-surface manifestation:
#import "modules/std.sx";
N : string : 4;
main :: () {
a : [N]i64 = ---;
print("{}\n", a.len);
}
Observed on flow/sx-foundation/F0.4 attempt 10: the first repro segfaults in
the generated program; the second prints 4.
Investigation prompt
Fix issue 0088: typed module constants must validate/coerce their initializer
against the explicit annotation before being registered or used. Suspected area:
src/ir/lower.zig, especially registerTypedModuleConst, lowerExpr's
module-const identifier path, and any const-declaration lowering that stores
ProgramIndex.module_const_map entries. src/ir/program_index.zig's
moduleConstInt currently folds by inspecting the initializer node and ignores
ModuleConstInfo.ty; after the declaration is diagnosed or represented
correctly, a non-integer typed const such as N : string : 4 must not become a
valid count. Likely fix: add a typed-const validation path that emits a clear
diagnostic for incompatible initializer/annotation pairs, and ensure the
module-const count lookup only accepts constants whose declared/inferred type is
numeric and integral-compatible. Verify by running the two repros above: expect
a non-zero compile with a type-mismatch diagnostic for N : string : 4, no
runtime segfault, and no [N] length of 4.