Files
sx/examples/0759-modules-undeclared-type-in-import.sx
agra 4072689afe fix(lower): genuinely-undeclared type → diagnostic + .unresolved (no silent stub) [stdlib E3]
Phase E3: remove the silent empty-struct fall-throughs in type resolution for
genuinely-undeclared names, replacing them with a real "unknown type" diagnostic
+ the dedicated `.unresolved` sentinel (already present, with the sizeOf @panic
tripwire) — the REJECTED-PATTERN this project bans.

Split `TypeHeadResolution.undeclared` into `.forward` (a real author not interned
yet — self/forward/mutual/foreign reference, adopted on registration via
internNamedTypeDecl) vs `.undeclared` (NO author anywhere). `resolveNominalLeaf`:
- `.pending` / `.forward` keep the empty-struct stub the type adopts on register.
- `.undeclared` in a NON-main (imported/library) module — which the
  UnknownTypeChecker trusts and never walks — emits "unknown type 'X'" + poisons
  with `.unresolved`. In the MAIN file the checker owns the diagnostic (and a
  valid unbound generic leaf legitimately lands here), so the leaf keeps the
  legacy stub and does not double-report.

Also convert the `parameterized_type_expr` constructor-head fallback
(resolveParameterizedWithBindings): an unresolvable base now emits + returns
`.unresolved` (mirroring the `.call`-node sibling) instead of a 0-field stub
that mis-sizes `b.field` / `b.len`. Threads the reference span through both
callers.

Triage of the other empty-struct sites (all load-bearing on the green suite or
unable to distinguish forward from undeclared — KEPT): resolveNamed's legacy
namer (forward/generic/Self/foreign-opaque: R/Self/Object/Array), the
foreign-class struct + JNI Self placeholders, the shadow-slot reservation, the
type_bridge stateless pack/generic namer, and the struct-literal inference
fallback (front-run by the leaf; 0 suite hits).

Regression: examples/0759-modules-undeclared-type-in-import — an undeclared type
in an imported module now errors (exit 1) instead of silently compiling (the
pre-fix code printed `thing.x = 42`, exit 0).

Gate: zig build; zig build test (423/423 + LSP corpus sweep); run_examples 497
passed / 0 failed (prior 496 byte-identical); m3te ios-sim build exit 0.
2026-06-08 08:10:42 +03:00

23 lines
1.0 KiB
Plaintext

// A genuinely-undeclared type name used in an IMPORTED (non-main) module must
// emit a clean "unknown type" diagnostic, not silently compile.
//
// The `UnknownTypeChecker` only walks MAIN-file decls — imported / library
// modules are trusted and never checked. So an undeclared type name in an
// imported module used to fall through the type leaf's empty-struct stub and
// silently fabricate a 0-field struct: `make_thing()` below compiled and ran
// (printing `thing.x = 42`) even though `lib.sx` references the non-existent
// type `Coordnate`. The source-aware nominal leaf now poisons a genuinely-
// undeclared name with the `.unresolved` sentinel and emits the diagnostic at
// the reference, so the typo surfaces instead of mis-sizing `Thing` downstream.
//
// Expected: `error: unknown type 'Coordnate'` pointing into lib.sx; exit 1.
// Regression (stdlib E3).
#import "modules/std.sx";
#import "0759-modules-undeclared-type-in-import/lib.sx";
main :: () -> s32 {
print("thing.x = {}\n", make_thing());
return 0;
}