# 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 1111–1116 + 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: ```text value = NotAType{} ``` Expected: a user-facing diagnostic rejecting `NotAType` as a type, with no fabricated empty-struct type. ## Reproduction ```sx #import "modules/std.sx"; NotAType :: 123; main :: () -> s32 { v: NotAType = ---; print("value = {}\n", v); return 0; } ``` Run: ```sh ./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, foreign 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: ```sh 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.