fix(stdlib/S2.1c): preseed top-level UFCS aliases [additive]
This commit is contained in:
@@ -334,6 +334,7 @@ pub fn resolve(
|
||||
.ufcs_aliases = std.StringHashMap([]const u8).init(alloc),
|
||||
};
|
||||
defer pass.ufcs_aliases.deinit();
|
||||
pass.seedTopLevelUfcsAliases(root);
|
||||
pass.visit(root, .{ .source = main_file, .scope = null });
|
||||
return pass.out;
|
||||
}
|
||||
@@ -352,6 +353,10 @@ const Frame = struct {
|
||||
const Ctx = struct {
|
||||
source: []const u8,
|
||||
scope: ?*const Frame,
|
||||
/// True only while visiting declarations that were already covered by the
|
||||
/// top-level UFCS alias pre-scan. Their decl nodes still get recorded into
|
||||
/// `ufcs_refs`, but the alias map keeps the scanDecls-style final state.
|
||||
preseeded_decl: bool = false,
|
||||
};
|
||||
|
||||
/// A resolved generic-param reference: the matched param (its address is its
|
||||
@@ -467,7 +472,11 @@ const ResolvePass = struct {
|
||||
/// names must resolve in their DEFINING module) overrides the ambient source
|
||||
/// for this subtree; an unstamped node inherits its parent's.
|
||||
fn visit(self: *ResolvePass, node: *const ast.Node, ctx: Ctx) void {
|
||||
const here = Ctx{ .source = node.source_file orelse ctx.source, .scope = ctx.scope };
|
||||
const here = Ctx{
|
||||
.source = node.source_file orelse ctx.source,
|
||||
.scope = ctx.scope,
|
||||
.preseeded_decl = ctx.preseeded_decl,
|
||||
};
|
||||
switch (node.data) {
|
||||
// ── declarations that open a generic-param scope ──
|
||||
.fn_decl => |*fd| {
|
||||
@@ -585,7 +594,8 @@ const ResolvePass = struct {
|
||||
// ── structural recursion (no classification of their own) ──
|
||||
.root => |*r| {
|
||||
// each top-level decl carries its own ambient source stamp.
|
||||
self.visitAll(r.decls, here);
|
||||
const decl_ctx = Ctx{ .source = here.source, .scope = here.scope, .preseeded_decl = true };
|
||||
self.visitAll(r.decls, decl_ctx);
|
||||
},
|
||||
.block => |*b| self.visitAll(b.stmts, here),
|
||||
.binary_op => |*b| {
|
||||
@@ -746,12 +756,37 @@ const ResolvePass = struct {
|
||||
// name so its rewrite call sites resolve to the same target. The map is
|
||||
// global / traversal-ordered, mirroring lowering's flat `ufcs_alias_map`.
|
||||
.ufcs_alias => |ua| {
|
||||
self.ufcs_aliases.put(ua.name, ua.target) catch @panic("resolve: OOM");
|
||||
if (!here.preseeded_decl) {
|
||||
self.ufcs_aliases.put(ua.name, ua.target) catch @panic("resolve: OOM");
|
||||
}
|
||||
self.recordAuthorsInto(&self.out.ufcs_refs, node, ua.target, here.source);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Mirror lowering's declaration pre-scan for UFCS aliases: top-level roots
|
||||
/// and namespace declaration lists are scanned before function bodies are
|
||||
/// visited, so a call can resolve through an alias declared later in the file.
|
||||
/// Function/lambda/block bodies are intentionally not entered here; local
|
||||
/// aliases keep normal statement-order behavior on the owning walk.
|
||||
fn seedTopLevelUfcsAliases(self: *ResolvePass, node: *const ast.Node) void {
|
||||
switch (node.data) {
|
||||
.root => |*r| self.seedTopLevelUfcsAliasDecls(r.decls),
|
||||
.namespace_decl => |*ns| self.seedTopLevelUfcsAliasDecls(ns.decls),
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
|
||||
fn seedTopLevelUfcsAliasDecls(self: *ResolvePass, decls: []const *ast.Node) void {
|
||||
for (decls) |decl| switch (decl.data) {
|
||||
.ufcs_alias => |ua| {
|
||||
self.ufcs_aliases.put(ua.name, ua.target) catch @panic("resolve: OOM");
|
||||
},
|
||||
.namespace_decl => self.seedTopLevelUfcsAliases(decl),
|
||||
else => {},
|
||||
};
|
||||
}
|
||||
|
||||
fn visitAll(self: *ResolvePass, nodes: anytype, ctx: Ctx) void {
|
||||
for (nodes) |n| self.visit(n, ctx);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user