fix: visibility-aware type-name resolution at registration time

Enum payloads, union fields, inline struct/enum/union field types, and
named error-set references now resolve through the visibility-aware
`inner` recursion hook (the same seam `resolveCompound` uses) instead of
the flat `findByName`. A bare type name in any of these positions now
selects the querying module's OWN author over a same-name namespaced
import -- the own-wins rule already applied to top-level named references
and struct fields.

- buildEnumInfo / buildUnionInfo / resolveInlineEnum / resolveInlineStruct
  / resolveInlineUnion / resolveErrorType take the `inner: anytype` seam;
  registerEnumDecl / registerUnionDecl and the struct-const annotation
  pass `self` (visibility-aware); resolveAstType passes the stateless `si`.
- resolveTypeWithBindings routes inline type decls and named error refs
  through `self` instead of delegating to flat resolveAstType.

Regression tests: examples/0781 (top-level enum payload over a namespaced
import), examples/0784 (inline struct field). Addresses issue 0132's
broader latent class; the protocol-return case (0132 primary) is a
separate registerProtocolDecl fix and stays open. The error-set reference
path is in place but dormant pending error-set per-decl nominal identity
(issue 0134).
This commit is contained in:
agra
2026-06-13 13:41:11 +03:00
parent ab3c9202ff
commit f13f4abfb1
11 changed files with 148 additions and 30 deletions

View File

@@ -703,7 +703,7 @@ pub fn registerStructDecl(self: *Lowering, sd: *const ast.StructDecl, source_fil
if (const_node.data == .const_decl) {
const cd = const_node.data.const_decl;
const qualified = std.fmt.allocPrint(self.alloc, "{s}.{s}", .{ sd.name, cd.name }) catch continue;
const ty: ?TypeId = if (cd.type_annotation) |ta| type_bridge.resolveAstType(ta, table, &self.program_index.type_alias_map, &self.program_index.module_const_map) else null;
const ty: ?TypeId = if (cd.type_annotation) |ta| self.resolveType(ta) else null;
self.struct_const_map.put(qualified, .{ .value = cd.value, .ty = ty }) catch {};
}
}
@@ -725,7 +725,12 @@ pub fn registerEnumDecl(self: *Lowering, ed: *const ast.EnumDecl) void {
const name_id = table.internString(ed.name);
const decl_key: *const anyopaque = @ptrCast(ed);
const nominal_id: u32 = if (table.type_decl_tids.get(decl_key)) |id| nominalIdOf(table.get(id)) else self.shadowNominalId(name_id);
const info = type_bridge.buildEnumInfo(ed, table, &self.program_index.type_alias_map, &self.program_index.module_const_map);
// Pass `self` (the visibility-aware `Lowering` resolver) as the `inner`
// recursion hook — the same seam `resolveCompound` uses — so a payload type
// NAME resolves in the enum's OWN module visibility context (own author wins
// over a namespaced same-name import), not via a global `findByName`
// first-match (issue 0132's class).
const info = type_bridge.buildEnumInfo(ed, table, self);
_ = self.internNamedTypeDecl(decl_key, name_id, info, nominal_id);
}
@@ -736,7 +741,8 @@ pub fn registerUnionDecl(self: *Lowering, ud: *const ast.UnionDecl) void {
const name_id = table.internString(ud.name);
const decl_key: *const anyopaque = @ptrCast(ud);
const nominal_id: u32 = if (table.type_decl_tids.get(decl_key)) |id| nominalIdOf(table.get(id)) else self.shadowNominalId(name_id);
const info = type_bridge.buildUnionInfo(ud, table, &self.program_index.type_alias_map, &self.program_index.module_const_map);
// `self` as the visibility-aware `inner` hook — see `registerEnumDecl`.
const info = type_bridge.buildUnionInfo(ud, table, self);
_ = self.internNamedTypeDecl(decl_key, name_id, info, nominal_id);
}