fix: reject non-type expression in type position instead of fabricating {} (issue 0189)
Two type-resolution paths silently resolved a non-type AST node in type
position to a zero-field `{}` struct that reached codegen with no
diagnostic:
- a dotted `type_expr` / field-access (`g.a`, `g` a runtime value) whose
prefix is not a namespace alias
- an `error_type_expr` (`!Name`) whose `Name` is not a declared error set
Now both reject loudly:
- `resolveTypeWithBindings` (lower.zig): "expected a type, found a value
'<name>' in type position" + `.unresolved`
- `checkTypeNodeForUnknown` (semantic_diagnostics.zig): validates a named
`!E` against the declared error-set names — "unknown error set
'<name>'" / "expected an error set after '!', found type '<name>'".
A bare `!` (void channel) and a declared `!E` in return position stay
valid; namespace-qualified types (`pkg.Type`) are unaffected.
Regression: examples/diagnostics/1195-diagnostics-non-type-in-type-position.
This commit is contained in:
@@ -1068,6 +1068,29 @@ pub const Lowering = struct {
|
||||
self.setCurrentSourceFile(saved);
|
||||
return ty;
|
||||
}
|
||||
// A dotted `type_expr` whose prefix is NOT a namespace alias
|
||||
// is the only remaining qualified form — and sx has no
|
||||
// `Type.NestedType` access, so this is a VALUE field access
|
||||
// (`g.a` where `g` is a value) sitting in a type position.
|
||||
// Without this guard `resolveNominalLeaf("g.a")` would
|
||||
// fabricate a zero-field empty-struct stub (`{}`) and ship it
|
||||
// to codegen as a real type (issue 0189) — a silent-default
|
||||
// miscompile. Reject loudly and poison with `.unresolved`.
|
||||
// A genuinely registered dotted type (none today, but a
|
||||
// forward-declared stub could exist) is still honored before
|
||||
// we reject, so we never reject a name that resolves to a
|
||||
// real type.
|
||||
if (!self.aliasDeclaredAnywhere(te.name[0..dot])) {
|
||||
const sid = self.module.types.internString(te.name);
|
||||
if (self.module.types.findByName(sid)) |tid| {
|
||||
const info = self.module.types.get(tid);
|
||||
const is_empty_stub = info == .@"struct" and info.@"struct".fields.len == 0;
|
||||
if (!is_empty_stub) return tid;
|
||||
}
|
||||
if (self.diagnostics) |d|
|
||||
d.addFmt(.err, node.span, "expected a type, found a value '{s}' in type position", .{te.name});
|
||||
return .unresolved;
|
||||
}
|
||||
}
|
||||
return self.resolveNominalLeaf(te.name, te.is_raw, node.span);
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user