Rewrote 20 issue writeups to the extern/runtime-class vocabulary (#foreign→extern, foreign_class_map→runtime_class_map, parseForeignClassDecl→parseRuntimeClassDecl, findForeignMethodInChain→findRuntimeMethodInChain, dedupeForeignSymbol→ dedupeExternSymbol, is_foreign_c_api→is_extern_c_api, stale filename refs to the renamed examples, foreign-class→runtime-class, bare foreign→extern). Renamed issues/0043-…-foreign-class-…→…-runtime-class-…. PHASE 9 COMPLETE — 9.4 GATE PASSES: zero 'foreign' across src/library/examples/ issues/docs/editors/specs/readme/CLAUDE, excluding only the SQLite API constant SQLITE_CONSTRAINT_FOREIGNKEY + vendored sqlite3.c/.h (upstream third-party). Suite green (644 corpus / 443 unit, 0 failed).
3.8 KiB
0068 — top-level value const used as a type silently yields an empty struct
RESOLVED (2026-06-02). Root cause: the A2.4 unknown-type pass (
semantic_diagnostics) inherited the issue-0064 behavior of adding EVERYconst_declname to its declared-type-name set. A value const (NotAType :: 123) thus satisfiedreportIfUnknownType, sov: NotATypewas not flagged; lowering then hitTypeResolver.resolveNamed's empty-struct-stub fallback and fabricatedNotAType{}. Fix:collectDeclaredTypeNamesandharvestScopeDeclsnow add a const name only when its value INTRODUCES a type — gated on a newconstValueIntroducesType(type declarations: struct/enum/union/error; type-expression aliases: type_expr, pointer/many-pointer/slice/optional/array/function/closure/tuple, parameterized)..identifier/.callaliases are intentionally excluded: the scan registers the type-valued ones intoProgramIndex.type_alias_map/ theTypeTable(both queried separately by the pass), so a value-RHS alias is correctly left out and flagged, while a type-RHS alias stays covered by the canonical facts. Regression test:examples/1117-diagnostics-value-const-as-type-rejected.sx(exit 1). Issue-0064 regressions 1111–1116 + the0115aliases stay green. Suite 352/0.
Symptom
A top-level value constant name is accepted in a type position and silently resolves to a fabricated empty struct.
Observed:
value = NotAType{}
Expected: a user-facing diagnostic rejecting NotAType as a type, with no
fabricated empty-struct type.
Reproduction
#import "modules/std.sx";
NotAType :: 123;
main :: () -> i32 {
v: NotAType = ---;
print("value = {}\n", v);
return 0;
}
Run:
./zig-out/bin/sx run .sx-tmp/probe-top-level-value-const-as-type.sx
The repro is standalone; the inline source above is sufficient to recreate the
scratch file under .sx-tmp/.
Investigation prompt
Fix issue 0068: a value constant name must not satisfy the unknown-type diagnostic pass or resolve as a fabricated type when used in a type position.
Suspected area:
src/ir/semantic_diagnostics.zig, especiallyUnknownTypeChecker.collectDeclaredTypeNamesandharvestScopeDecls.- The moved issue-0064 pass currently adds every
const_declname to thedeclaredset. That preserves old behavior, but it means a value const likeNotAType :: 123;suppressesreportIfUnknownType, then the later type resolver's unknown-name fallback interns an empty struct namedNotAType. - Related fallback:
TypeResolver.resolveNamed/type_bridge.resolveAstTypestill create empty struct stubs for unknown names in paths that the diagnostic pass is supposed to reject before lowering reaches codegen.
Likely fix:
- Change
collectDeclaredTypeNames/harvestScopeDeclsso only declarations that actually introduce type-position names are added: struct / enum / union / error declarations, type aliases, generic templates, protocols, extern classes, and local type declarations. - Do not add arbitrary value const names to the type-name set.
- Preserve valid type alias behavior such as
Alias :: u32;and local type-declaration behavior. - Keep the pass querying canonical facts (
ProgramIndex,TypeResolver, andTypeTable) rather than reintroducing a parallel top-level truth table.
Verification:
- Add a focused diagnostics example in the
11xxblock for the repro above, expecting exit 1 and a clear diagnostic. - Keep issue-0064 regressions green (
1111through1115) and keep existing alias/type-declaration examples green. - Run:
zig build
zig build test
bash tests/run_examples.sh
Expected result: NotAType :: 123; v: NotAType is rejected with a diagnostic,
valid aliases and type declarations still resolve, and the full suite passes.