Closes the main-file carveout left by attempt-1 (4072689): a genuinely- undeclared type used as a field type inside a MAIN-file GENERIC struct still fell through the type leaf's empty-struct stub and silently compiled — `Box :: struct($T: Type) { good: T; bad: MissingType; }` with `b : Box(s64)` exited 0 and printed a value instead of reporting `unknown type 'MissingType'`. Root cause: `UnknownTypeChecker` is the main-file diagnostic authority (the type leaf defers to it for `.undeclared` names there), but `checkStructFieldTypes` SKIPPED every generic struct outright ("its fields reference `$T`, resolved at instantiation"), so the undeclared name was never examined. The sibling `walkBodyTypes` `.struct_decl` arm skipped body-local generic structs the same way. Fix (semantic_diagnostics.zig, checker only — no leaf change): - `checkStructFieldTypes`: stop skipping generic structs; walk the field types with the struct's OWN type params (`$T`, `$N`, `..$Ts`) passed as the in-scope set. A param name resolves; any OTHER bare name that is neither declared nor a generic param is reported. Value-param positions (a `Vector` lane count, a `$N: u32` arg) are still skipped inside `checkTypeNodeForUnknown` / `isValueParamPosition`. - `walkBodyTypes` `.struct_decl`: same close for body-local structs — the local struct's own type params join the enclosing scope's in-scope params (so it can name both the outer fn's `$T` and its own), any other bare field type is still flagged. The `..$Ts` pack field `(..$Ts)` parses to a `spread_expr` inside the tuple, which hits `checkTypeNodeForUnknown`'s `else` arm — never walked — so the pack examples (0538-0543, 0414) stay green. The checker walks only MAIN-file decls, so library generic structs (List, Map) are untouched. Regression: examples/0171-types-undeclared-type-in-generic-struct-field — the reviewer's exact shape; `unknown type 'MissingType'` at the field, exit 1. Fail-before on4072689(prints 7, exit 0), pass-after. Gate: zig build; zig build test (423/423 + LSP corpus sweep 514); run_examples 498 passed / 0 failed (prior 497 byte-identical); m3te ios-sim build exit 0.
30 lines
1.2 KiB
Plaintext
30 lines
1.2 KiB
Plaintext
// A genuinely-undeclared type name used as a field type inside a MAIN-file
|
|
// GENERIC struct must emit a clean "unknown type" diagnostic, not silently
|
|
// compile.
|
|
//
|
|
// The `UnknownTypeChecker` used to SKIP generic structs entirely ("their field
|
|
// types reference the struct's own `$T`, resolved at instantiation"). That skip
|
|
// was too broad: a field type like `bad: MissingType` — which is NOT a type
|
|
// param and names no declared type — fell through the type leaf's empty-struct
|
|
// stub and the struct silently compiled, mis-sizing every downstream load.
|
|
//
|
|
// The checker now walks generic-struct fields with the struct's own type params
|
|
// (`$T`) in scope: `good: T` resolves (it IS a param) while `bad: MissingType`
|
|
// is reported. A value-param position (a `Vector` lane count, a `$N: u32` arg)
|
|
// is still skipped, so a valid generic struct keeps compiling unchanged.
|
|
//
|
|
// Expected: `error: unknown type 'MissingType'` pointing at the field; exit 1.
|
|
// Regression (stdlib E3).
|
|
#import "modules/std.sx";
|
|
|
|
Box :: struct($T: Type) {
|
|
good: T;
|
|
bad: MissingType;
|
|
}
|
|
|
|
main :: () -> s32 {
|
|
b : Box(s64) = .{ good = 7, bad = 0 };
|
|
print("{}\n", b.good);
|
|
return 0;
|
|
}
|