Files
sx/issues/0068-value-const-used-as-type-suppresses-unknown-type.md
agra b9cfe2554f refactor(ffi-linkage): Phase 9.3/9.4 — purge 'foreign' from issues/*.md; GATE PASS
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).
2026-06-15 11:18:35 +03:00

3.8 KiB
Raw Permalink Blame History

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 EVERY const_decl name to its declared-type-name set. A value const (NotAType :: 123) thus satisfied reportIfUnknownType, so v: NotAType was not flagged; lowering then hit TypeResolver.resolveNamed's empty-struct-stub fallback and fabricated NotAType{}. Fix: collectDeclaredTypeNames and harvestScopeDecls now add a const name only when its value INTRODUCES a type — gated on a new constValueIntroducesType (type declarations: struct/enum/union/error; type-expression aliases: type_expr, pointer/many-pointer/slice/optional/array/function/closure/tuple, parameterized). .identifier / .call aliases are intentionally excluded: the scan registers the type-valued ones into ProgramIndex.type_alias_map / the TypeTable (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 11111116 + the 0115 aliases 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, especially UnknownTypeChecker.collectDeclaredTypeNames and harvestScopeDecls.
  • The moved issue-0064 pass currently adds every const_decl name to the declared set. That preserves old behavior, but it means a value const like NotAType :: 123; suppresses reportIfUnknownType, then the later type resolver's unknown-name fallback interns an empty struct named NotAType.
  • Related fallback: TypeResolver.resolveNamed / type_bridge.resolveAstType still 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 / harvestScopeDecls so 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, and TypeTable) rather than reintroducing a parallel top-level truth table.

Verification:

  • Add a focused diagnostics example in the 11xx block for the repro above, expecting exit 1 and a clear diagnostic.
  • Keep issue-0064 regressions green (1111 through 1115) 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.