fix(ir): value const used as a type must not satisfy unknown-type check (issue 0068)
The A2.4 unknown-type pass (semantic_diagnostics) added 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{}` (the program ran, printing it).
Fix: collectDeclaredTypeNames and harvestScopeDecls now gate the const-name-add
on a new constValueIntroducesType — true only when the value introduces a 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: examples/1117-diagnostics-value-const-as-type-rejected.sx (exit 1).
Issue-0064 regressions 1111-1116 and the 0115 aliases stay green. Gate: zig
build, zig build test, run_examples 352/0.
This commit is contained in:
@@ -65,7 +65,11 @@ pub const UnknownTypeChecker = struct {
|
||||
for (decls) |decl| {
|
||||
switch (decl.data) {
|
||||
.const_decl => |cd| {
|
||||
out.put(cd.name, {}) catch {};
|
||||
// Only a const whose VALUE introduces a type (a type decl or
|
||||
// type-expression alias) declares a type name. A value const
|
||||
// like `NotAType :: 123` must NOT satisfy the unknown-type
|
||||
// check (issue 0068).
|
||||
if (constValueIntroducesType(cd.value)) out.put(cd.name, {}) catch {};
|
||||
if (cd.value.data == .fn_decl) self.harvestScopeDecls(cd.value.data.fn_decl.body, out);
|
||||
},
|
||||
.struct_decl => |sd| out.put(sd.name, {}) catch {},
|
||||
@@ -127,7 +131,11 @@ pub const UnknownTypeChecker = struct {
|
||||
.destructure_decl => |dd| self.harvestScopeDecls(dd.value, out),
|
||||
.var_decl => |vd| if (vd.value) |v| self.harvestScopeDecls(v, out),
|
||||
.const_decl => |cd| {
|
||||
out.put(cd.name, {}) catch {};
|
||||
// Local type decl (`T :: struct/enum/union/error/alias`) — add
|
||||
// its name; a local VALUE const (`x :: 5`) does not declare a
|
||||
// type (issue 0068). Recurse regardless, to harvest nested decls
|
||||
// (e.g. type decls inside a `f :: () { ... }` body).
|
||||
if (constValueIntroducesType(cd.value)) out.put(cd.name, {}) catch {};
|
||||
self.harvestScopeDecls(cd.value, out);
|
||||
},
|
||||
.struct_decl => |sd| out.put(sd.name, {}) catch {},
|
||||
@@ -428,6 +436,35 @@ fn isBuiltinTypeName(name: []const u8) bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
/// True when a `const_decl`'s value introduces a TYPE name — a type declaration
|
||||
/// (`struct`/`enum`/`union`/`error`) or a type-expression alias (`Alias :: u32`,
|
||||
/// `Ptr :: *u8`, `Cb :: (s32) -> s32`, …). Only these belong in the declared-
|
||||
/// type-name set; a value const (`NotAType :: 123`) does NOT declare a type and
|
||||
/// must stay subject to the unknown-type check (issue 0068).
|
||||
///
|
||||
/// `.identifier` / `.call` aliases (`B :: A`, `Vec3 :: Vec(3, f32)`) are
|
||||
/// deliberately NOT matched here: the scan registers the type-valued ones into
|
||||
/// `ProgramIndex.type_alias_map` / the `TypeTable` (both queried separately), so
|
||||
/// a value-RHS alias is correctly left out and flagged, while a type-RHS alias
|
||||
/// is still covered by the canonical facts.
|
||||
fn constValueIntroducesType(value: *const Node) bool {
|
||||
return switch (value.data) {
|
||||
.struct_decl, .enum_decl, .union_decl, .error_set_decl => true,
|
||||
.type_expr,
|
||||
.pointer_type_expr,
|
||||
.many_pointer_type_expr,
|
||||
.slice_type_expr,
|
||||
.optional_type_expr,
|
||||
.array_type_expr,
|
||||
.function_type_expr,
|
||||
.closure_type_expr,
|
||||
.tuple_type_expr,
|
||||
.parameterized_type_expr,
|
||||
=> true,
|
||||
else => false,
|
||||
};
|
||||
}
|
||||
|
||||
fn isIdentLike(name: []const u8) bool {
|
||||
if (name.len == 0) return false;
|
||||
if (!(std.ascii.isAlphabetic(name[0]) or name[0] == '_')) return false;
|
||||
|
||||
Reference in New Issue
Block a user