lang: generic struct head aliases bind the template (fix 0120) — alias-follow from each author's source in head selection; loud unknown-type on the .call type tail

BoxAlias :: Box; / Box :: r.Box; now resolve instantiation, methods,
annotations, and chains through the aliased template, and re-export one
flat-import level as ordinary own decls (the facade shape the std.sx
restructure needs). selectGenericStructHead consults aliasedStructTemplate
(nominal.zig) before the global template map — own-wins/single-flat alias
author, each hop pinned to the alias author's source, ns.X RHS through
namespaceAliasVerdictFrom, depth-capped. resolveTypeCallWithBindings'
silent .unresolved tail (panicked in LLVM emission) now diagnoses
"unknown type". Also aligns the stale pre-existing calls.test.zig UFCS
plan test with the opt-in model (a47ea14). Regression: examples/0211
(+rich/+facade). Gates: zig build test 426/426, suite 587/587.
This commit is contained in:
agra
2026-06-11 18:09:01 +03:00
parent 51194a26d8
commit f2db8ecc53
13 changed files with 382 additions and 5 deletions

View File

@@ -394,6 +394,77 @@ pub fn qualifiedMemberMissing(self: *Lowering, alias: []const u8, member: []cons
return true;
}
/// The `*ConstDecl` a raw author wraps when it is a const ALIAS of another
/// name — `BoxAlias :: Box;` (identifier RHS) or `Box :: r.Box;` (namespace-
/// member RHS). Null for every other shape, including const-wrapped struct /
/// fn DEFINITIONS, which are authors in their own right.
fn constAliasOfRaw(ref: resolver_mod.RawDeclRef) ?*const ast.ConstDecl {
return switch (ref) {
.const_decl => |cd| switch (cd.value.data) {
.identifier, .field_access => cd,
else => null,
},
else => null,
};
}
/// The single author of `name` as seen from `from` — own wins, else exactly
/// one flat-import author. Null when absent or when ≥2 flat authors compete
/// (the use site then diagnoses the unresolved head; no silent pick).
fn singleVisibleAuthor(self: *Lowering, name: []const u8, from: []const u8) ?resolver_mod.RawAuthor {
var res = self.resolver();
const set = res.collectVisibleAuthors(name, from, .user_bare_flat);
defer if (set.flat.len > 0) self.alloc.free(set.flat);
if (set.own) |o| return o;
if (set.flat.len == 1) return set.flat[0];
return null;
}
/// Resolve `name`, as seen from `from`, to a generic-struct template by
/// following const ALIAS declarations (issue 0120). Entry for the head
/// selector's bare tail: the FIRST hop must be alias-shaped — a direct
/// struct author is the template map's business, never this path's. Each
/// hop resolves from the ALIAS AUTHOR's source, so visibility is the
/// author's, not the use site's (a consumer one flat hop from a facade
/// reaches the facade's `Box :: r.Box;` without seeing `r` itself).
pub fn aliasedStructTemplate(self: *Lowering, name: []const u8, from: []const u8) ?StructTemplate {
const author = singleVisibleAuthor(self, name, from) orelse return null;
if (constAliasOfRaw(author.raw) == null) return null;
return followToTemplate(self, author, 8);
}
/// One alias hop: a generic-struct author terminates the chain with its
/// rebuilt source-pinned template; an alias author recurses on its RHS —
/// bare identifier from the author's own source, `ns.X` through the
/// author's namespace edge into the target module's own member. The depth
/// cap breaks alias cycles (`A :: B; B :: A;`).
fn followToTemplate(self: *Lowering, author: resolver_mod.RawAuthor, depth: u8) ?StructTemplate {
if (depth == 0) return null;
if (structDeclOfRaw(author.raw)) |sd| {
if (sd.type_params.len == 0) return null;
return self.buildGenericStructTemplate(sd, author.source);
}
const cd = constAliasOfRaw(author.raw) orelse return null;
switch (cd.value.data) {
.identifier => |id| {
const next = singleVisibleAuthor(self, id.name, author.source) orelse return null;
return followToTemplate(self, next, depth - 1);
},
.field_access => |fa| {
if (fa.object.data != .identifier) return null;
const target = switch (self.namespaceAliasVerdictFrom(fa.object.data.identifier.name, author.source)) {
.target => |t| t,
.none, .ambiguous => return null,
};
var res = self.resolver();
const member_set = res.collectNamespaceAuthors(target, fa.field);
const member = member_set.own orelse return null;
return followToTemplate(self, member, depth - 1);
},
else => return null,
}
}
/// The bare-VISIBLE single generic-struct author of `name` (its `StructDecl` +
/// defining source) when that author is NOT the one the global last-wins
/// `struct_template_map` already holds — the E4 non-transitive selection for a