fix: resolve module-alias-qualified type in reflection arg slot (issue 0147)
size_of(sel.Selection) and the other reflection builtins rejected a module-alias-qualified type: in argument position it parses as a .field_access expression (not the dotted .type_expr a declaration produces), and neither isStaticTypeArg nor resolveTypeArg had a .field_access arm. Add both: a pure namespace-decl scan in isStaticTypeArg, and resolution via namespaceAliasTarget + resolveNominalLeaf in the target module context in resolveTypeArg (mirroring the value-position lowerFieldAccess path). No fabricated-stub fallback. Regression: examples/0192-types-size-of-qualified-alias.sx
This commit is contained in:
@@ -243,6 +243,35 @@ pub fn isStaticTypeArg(self: *Lowering, node: *const Node) bool {
|
||||
}
|
||||
return true;
|
||||
},
|
||||
.field_access => |fa| {
|
||||
// A module-alias-qualified type name (`sel.Selection`) is a static
|
||||
// type iff `fa.object` is a namespace ALIAS (not a runtime scope var)
|
||||
// whose target module authors a TYPE named `fa.field` (issue 0147).
|
||||
// Pure predicate: scan the target's own decls — no type resolution
|
||||
// side effects (the actual TypeId is produced later by
|
||||
// `resolveTypeArg`'s matching `.field_access` arm).
|
||||
if (fa.object.data != .identifier) return false;
|
||||
const oname = fa.object.data.identifier.name;
|
||||
if (self.scope) |scope| {
|
||||
if (scope.lookup(oname) != null) return false;
|
||||
}
|
||||
const target = self.namespaceAliasTarget(oname, node.span) orelse return false;
|
||||
for (target.own_decls) |decl| {
|
||||
const dn = decl.data.declName() orelse continue;
|
||||
if (!std.mem.eql(u8, dn, fa.field)) continue;
|
||||
return switch (decl.data) {
|
||||
.struct_decl, .enum_decl, .union_decl, .error_set_decl => true,
|
||||
// A const-wrapped type definition or a type alias
|
||||
// (`Foo :: Bar;` / `Foo :: ns.Bar;`).
|
||||
.const_decl => |cd| switch (cd.value.data) {
|
||||
.struct_decl, .enum_decl, .union_decl, .error_set_decl, .identifier, .field_access => true,
|
||||
else => false,
|
||||
},
|
||||
else => false,
|
||||
};
|
||||
}
|
||||
return false;
|
||||
},
|
||||
.pack_index_type_expr,
|
||||
.pointer_type_expr,
|
||||
.many_pointer_type_expr,
|
||||
@@ -438,6 +467,36 @@ pub fn resolveTypeArg(self: *Lowering, node: *const Node) TypeId {
|
||||
.optional_type_expr,
|
||||
.function_type_expr,
|
||||
=> return self.resolveTypeWithBindings(node),
|
||||
// A module-alias-qualified type name in a type-arg slot
|
||||
// (`size_of(sel.Selection)`) parses as a field-access EXPRESSION — unlike
|
||||
// the dotted `.type_expr` a declaration annotation produces — so without
|
||||
// this arm it fell through to `else` and resolved to `.unresolved`
|
||||
// (issue 0147). Reconstruct the qualified `obj.field` name and resolve it
|
||||
// through the same alias map a declaration uses. Look it up EXPLICITLY
|
||||
// (findByName + alias map) rather than via `resolveNamed`, whose
|
||||
// empty-struct-stub fallback would silently fabricate a 0-sized type for
|
||||
// an unregistered name (the silent-default trap) — a failed lookup must
|
||||
// surface as a diagnostic + `.unresolved`.
|
||||
.field_access => |fa| {
|
||||
// Resolve the member as a TYPE in the alias's TARGET module context —
|
||||
// the same mechanism `lowerFieldAccess` uses for `alias.Type` in value
|
||||
// position (src/ir/lower/expr.zig): the alias edge authorizes the reach,
|
||||
// so set the current source to the target module and resolve the bare
|
||||
// member name through the source-aware nominal leaf.
|
||||
if (fa.object.data == .identifier) {
|
||||
if (self.namespaceAliasTarget(fa.object.data.identifier.name, node.span)) |target| {
|
||||
const saved_src = self.current_source_file;
|
||||
self.setCurrentSourceFile(target.target_module_path);
|
||||
const ty = self.resolveNominalLeaf(fa.field, false, node.span);
|
||||
self.setCurrentSourceFile(saved_src);
|
||||
if (ty != .unresolved) return ty;
|
||||
}
|
||||
}
|
||||
if (self.diagnostics) |diags| {
|
||||
diags.addFmt(.err, node.span, "unresolved qualified type in type-argument position", .{});
|
||||
}
|
||||
return .unresolved;
|
||||
},
|
||||
else => return .unresolved,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user