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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user