feat(resolver): route plain bare-call author through Phase B collector via SelectedFunc [stdlib C]
Phase C of the unified resolver (R5 §C, §#3). Re-base the plain bare-name
call author onto the Phase B collector behind one shared SelectedFunc, so
every call-path consumer reads ONE author and they can no longer disagree
(fix-0102 F2). Behavior-preserving: 0722-0735 byte-identical, run_examples
stays at 475.
- SelectedFunc {decl, source, materialized?} replaces ResolvedAuthor in
BareCallee.func; CallPlan.Target gains a `selected` arm (calls.zig).
- selectPlainCallableAuthor: resolveBareCallee's body verbatim over
resolver.collectVisibleAuthors (.user_bare_flat) — the ONE graph-walk.
fnDeclOfRaw mirrors imports.fnDeclOf so the collector's all-domain authors
reproduce module_fns' fn-only view; every byte of the negative space is
preserved (own==winner → .none; non-plain-free → .none; filter-before-count;
≥2 distinct → .ambiguous). No eager materialization.
- selectedFuncId materializes the FuncId on demand (shadow-only), caching into
materialized — null until a site needs it (0102d: a shadow taken as a value
never lowers the winner).
- Six consumers route through the one selector: lowerCall variadic packing,
free-fn UFCS, fn-value, closure(fn), resolveCallParamTypes, and
expandCallDefaults (decl-only, no materialization). plan() produces the
SelectedFunc as `.selected`. Generic/comptime/foreign/builtin stay legacy.
- lower.test.zig: wire module_decls; selectPlainCallableAuthor verdicts
(own-winner → .none; ≥2 flat → .ambiguous; own-shadow → decl+source, fid
round-trips, materialized null).
Gate: zig build + zig build test (412 ok) + run_examples (475, byte-identical)
+ m3te ios-sim build exit 0.
This commit is contained in:
@@ -72,6 +72,13 @@ pub const CallPlan = struct {
|
||||
/// A callee carried by name — reflection builtin, generic / lazy fn,
|
||||
/// closure / fn-pointer binding, or a not-yet-lowered namespace fn.
|
||||
named: []const u8,
|
||||
/// The single bare-call author `selectPlainCallableAuthor` selected for a
|
||||
/// genuine flat same-name collision (R5 §#3). Carries the resolved
|
||||
/// `*FnDecl` + source so `plan` and the lowering call-path read ONE
|
||||
/// author and can no longer disagree (fix-0102 F2); the FuncId is
|
||||
/// materialized on demand. Only set when the bare name reroutes away from
|
||||
/// the first-wins winner; the common path still uses `func` / `named`.
|
||||
selected: Lowering.SelectedFunc,
|
||||
/// Protocol method, by index in the protocol's method table.
|
||||
protocol_method: u32,
|
||||
/// Foreign-class method (Obj-C / JNI), with its static-ness.
|
||||
@@ -149,6 +156,28 @@ pub const CallResolver = struct {
|
||||
if (std.mem.eql(u8, bare_name, "type_is_unsigned")) return refl(bare_name, .bool);
|
||||
if (std.mem.eql(u8, bare_name, "type_of")) return refl(bare_name, .any);
|
||||
if (std.mem.eql(u8, bare_name, "field_value")) return refl(bare_name, .any);
|
||||
// Plain bare same-name flat collision (R5 §C): route through the ONE
|
||||
// selector so `plan` reads the SAME author the lowering call-path
|
||||
// binds — they can no longer disagree (fix-0102 F2). The gate mirrors
|
||||
// `lowerCall`'s: a plain top-level identifier with no scope-mangle /
|
||||
// local shadow. A generic / foreign / builtin author is not plain-free
|
||||
// so the selector returns `.none`; `.ambiguous` / `.none` fall through
|
||||
// to the first-wins path below, byte-for-byte.
|
||||
if (std.mem.eql(u8, name, bare_name) and
|
||||
(if (self.l.scope) |scope| scope.lookup(bare_name) == null else true))
|
||||
{
|
||||
if (self.l.current_source_file) |caller_file| {
|
||||
switch (self.l.selectPlainCallableAuthor(bare_name, caller_file)) {
|
||||
.func => |sf| return .{
|
||||
.kind = .direct_fn,
|
||||
.return_type = if (sf.decl.return_type) |rt| self.l.resolveType(rt) else .void,
|
||||
.target = .{ .selected = sf },
|
||||
.expands_defaults = defaultsFor(sf.decl, c.args.len),
|
||||
},
|
||||
.ambiguous, .none => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
// Generic function — infer return type via type bindings.
|
||||
if (self.l.program_index.fn_ast_map.get(name)) |fd| {
|
||||
if (fd.type_params.len > 0) {
|
||||
|
||||
@@ -1361,6 +1361,10 @@ test "lower: shadowed same-name author gets its own FuncId + real body (fix-0102
|
||||
var module_fns = imports.ModuleFns.init(alloc);
|
||||
try imports.buildModuleFns(alloc, main_path, mod, &cache, &module_fns);
|
||||
|
||||
// Phase A raw facts: `selectPlainCallableAuthor` (Phase C) collects authors
|
||||
// over `module_decls`, not `module_fns`. Wired exactly as `core.zig` does.
|
||||
var facts = try imports.buildImportFacts(alloc, main_path, mod, &cache);
|
||||
|
||||
const resolved_root = try alloc.create(Node);
|
||||
resolved_root.* = .{ .span = root.span, .data = .{ .root = .{ .decls = mod.decls } } };
|
||||
|
||||
@@ -1375,6 +1379,7 @@ test "lower: shadowed same-name author gets its own FuncId + real body (fix-0102
|
||||
lowering.program_index.import_graph = &import_graph;
|
||||
lowering.program_index.flat_import_graph = &flat_import_graph;
|
||||
lowering.program_index.module_fns = &module_fns;
|
||||
lowering.program_index.module_decls = &facts.decls;
|
||||
|
||||
lowering.lowerRoot(resolved_root);
|
||||
try std.testing.expect(!diagnostics.hasErrors());
|
||||
@@ -1428,19 +1433,27 @@ test "lower: shadowed same-name author gets its own FuncId + real body (fix-0102
|
||||
try std.testing.expect(shadow_fid != null);
|
||||
try std.testing.expect(shadow_fid.? != winner_fid.?);
|
||||
|
||||
// fix-0102c: THE bare-name resolver routes per caller file. `main` flat-
|
||||
// imports two `greet` authors and is its own author of neither → a bare
|
||||
// `greet()` from `main` is ambiguous. a.sx authors the WINNER, so its bare
|
||||
// `greet` resolves through the existing path (`.none`). b.sx authors the
|
||||
// SHADOW, so own-author-wins binds b.sx's distinct FuncId — not first-wins.
|
||||
// fix-0102c / Phase C: THE bare-name selector routes per caller file over the
|
||||
// Phase A author collector. `main` flat-imports two `greet` authors and is its
|
||||
// own author of neither → a bare `greet()` from `main` is ambiguous. a.sx
|
||||
// authors the WINNER, so its bare `greet` resolves through the existing path
|
||||
// (`.none`). b.sx authors the SHADOW, so own-author-wins selects b.sx's
|
||||
// author — its `*FnDecl` + source, NOT first-wins. The selector does NOT
|
||||
// eagerly materialize: it returns the decl, and the FuncId still round-trips
|
||||
// to the shadow slot via the identity map (`fn_decl_fids`).
|
||||
const a_path = try std.fmt.allocPrint(alloc, "{s}/a.sx", .{absdir});
|
||||
const b_path = try std.fmt.allocPrint(alloc, "{s}/b.sx", .{absdir});
|
||||
try std.testing.expect(lowering.resolveBareCallee("greet", main_path) == .ambiguous);
|
||||
try std.testing.expect(lowering.resolveBareCallee("greet", a_path) == .none);
|
||||
switch (lowering.resolveBareCallee("greet", b_path)) {
|
||||
.func => |resolved| try std.testing.expectEqual(shadow_fid.?, resolved.fid),
|
||||
try std.testing.expect(lowering.selectPlainCallableAuthor("greet", main_path) == .ambiguous);
|
||||
try std.testing.expect(lowering.selectPlainCallableAuthor("greet", a_path) == .none);
|
||||
switch (lowering.selectPlainCallableAuthor("greet", b_path)) {
|
||||
.func => |sf| {
|
||||
try std.testing.expectEqual(shadow_fd.?, sf.decl);
|
||||
try std.testing.expectEqualStrings(b_path, sf.source);
|
||||
try std.testing.expect(sf.materialized == null);
|
||||
try std.testing.expectEqual(shadow_fid.?, lowering.fn_decl_fids.get(sf.decl).?);
|
||||
},
|
||||
else => return error.TestUnexpectedResult,
|
||||
}
|
||||
// A name no module authors (and no flat import provides) never routes.
|
||||
try std.testing.expect(lowering.resolveBareCallee("nonexistent", b_path) == .none);
|
||||
try std.testing.expect(lowering.selectPlainCallableAuthor("nonexistent", b_path) == .none);
|
||||
}
|
||||
|
||||
223
src/ir/lower.zig
223
src/ir/lower.zig
@@ -1461,7 +1461,7 @@ pub const Lowering = struct {
|
||||
// A `#run` body lowers in its OWN module's source context (fix-0102d
|
||||
// site 4): `NAME :: #run f()` written in an imported module must
|
||||
// resolve a bare `f` from that module's flat imports, not the main
|
||||
// file's. Without this, `resolveBareCallee` runs with the main
|
||||
// file's. Without this, `selectPlainCallableAuthor` runs with the main
|
||||
// file's perspective and reports a genuine per-source author as
|
||||
// ambiguous. Mirrors `scanDecls` / `lowerDecls`, which already set
|
||||
// the source file per decl.
|
||||
@@ -1552,14 +1552,16 @@ pub const Lowering = struct {
|
||||
}
|
||||
}
|
||||
|
||||
/// Result of bare-call disambiguation (fix-0102c).
|
||||
/// Result of bare-call disambiguation (fix-0102c, now over the Phase B
|
||||
/// author collector).
|
||||
pub const BareCallee = union(enum) {
|
||||
/// Bind the call to this specific author — its identity-addressable
|
||||
/// FuncId (fix-0102b's `bareAuthorFuncId`) AND its `*FnDecl`. The decl
|
||||
/// travels with the FuncId so every callee-signature decision in the
|
||||
/// call path (variadic packing, …) reads the RESOLVED author, never a
|
||||
/// first-wins re-lookup by name (fix-0102c F1).
|
||||
func: ResolvedAuthor,
|
||||
/// Bind the call to this specific author, carried as the shared
|
||||
/// `SelectedFunc` (R5 §#3): its `*FnDecl` + authoring source, FuncId
|
||||
/// materialized on demand. Every callee-signature decision in the call
|
||||
/// path (variadic packing, param typing, default expansion) reads the
|
||||
/// RESOLVED author from this one object — never a first-wins re-lookup
|
||||
/// by name (fix-0102c F1).
|
||||
func: SelectedFunc,
|
||||
/// ≥2 distinct flat authors are reachable from the caller and none is
|
||||
/// the caller's own — the bare call can't pick one; require a qualifier.
|
||||
ambiguous,
|
||||
@@ -1568,78 +1570,122 @@ pub const Lowering = struct {
|
||||
none,
|
||||
};
|
||||
|
||||
/// A resolved bare-call author: its FuncId and the `*FnDecl` that defined
|
||||
/// it, kept together so the call path has ONE source of truth for the
|
||||
/// callee (no re-fetch by name after resolution).
|
||||
pub const ResolvedAuthor = struct { fid: FuncId, decl: *const ast.FnDecl };
|
||||
/// The single bare-call author object (R5 §#3): the `*FnDecl` that defines
|
||||
/// the call and the SOURCE file that authors it, kept together so the call
|
||||
/// path has ONE source of truth for the callee. `materialized` holds the
|
||||
/// author's FuncId once a site needs it; it is filled on demand by
|
||||
/// `selectedFuncId` (→ `bareAuthorFuncId`), NOT during selection — so a
|
||||
/// selection that only needs the decl (default-arg expansion), or a shadow
|
||||
/// taken purely as a value, never lowers the first-wins winner (0102d).
|
||||
pub const SelectedFunc = struct {
|
||||
decl: *const ast.FnDecl,
|
||||
source: []const u8,
|
||||
materialized: ?FuncId = null,
|
||||
};
|
||||
|
||||
/// THE bare-name call resolver (fix-0102c). One canonical traversal over
|
||||
/// fix-0102a's `module_fns` + `flat_import_graph` that routes a bare
|
||||
/// identifier call `name` from `caller_file` to the right same-name author
|
||||
/// when flat imports introduce a genuine collision. Every single-author /
|
||||
/// local / parameter / std / qualified name resolves through the EXISTING
|
||||
/// path unchanged: the resolver returns `.none` whenever the outcome would
|
||||
/// match first-wins, so nothing on the common path is perturbed.
|
||||
/// THE plain bare-name call selector (fix-0102c, R5 §C). `resolveBareCallee`'s
|
||||
/// body verbatim, now over the Phase B author collector
|
||||
/// (`resolver.collectVisibleAuthors` — the ONE graph-walk) instead of a direct
|
||||
/// `module_fns` + `flat_import_graph` traversal. Routes a bare identifier call
|
||||
/// `name` from `caller_file` to the right same-name author when flat imports
|
||||
/// introduce a genuine collision. Every single-author / local / parameter /
|
||||
/// std / qualified name resolves through the EXISTING path unchanged: the
|
||||
/// selector returns `.none` whenever the outcome would match first-wins, so
|
||||
/// nothing on the common path is perturbed.
|
||||
///
|
||||
/// - **own-author wins**: if `caller_file` authors `name` and the bare-name
|
||||
/// first-wins winner is a DIFFERENT author, bind the caller's own author.
|
||||
/// (When the winner already IS the caller's own — the single-author and
|
||||
/// first-importer cases — `.none` lets the existing path bind it.)
|
||||
/// - else collect the authors reachable via `caller_file`'s FLAT import
|
||||
/// The collector returns RAW authors across ALL decl domains; this selector
|
||||
/// reproduces `module_fns`' fn-only view by filtering each author through
|
||||
/// `fnDeclOfRaw` (a `const`-wrapped fn unwraps to its inner fn — the exact
|
||||
/// `*FnDecl` `module_fns` stored; every other domain drops out), preserving
|
||||
/// resolveBareCallee's negative space byte-for-byte.
|
||||
///
|
||||
/// - **own-author wins**: if `caller_file` authors `name` as a fn and the
|
||||
/// bare-name first-wins winner is a DIFFERENT author, select the caller's
|
||||
/// own author. (When the winner already IS the caller's own — the
|
||||
/// single-author and first-importer cases — `.none` lets the existing path
|
||||
/// bind it.)
|
||||
/// - else select among the authors reachable via `caller_file`'s FLAT import
|
||||
/// edges (bare `#import` of a file or directory, never a namespaced
|
||||
/// `ns :: #import`), deduped by `FnDecl` identity (a diamond import of the
|
||||
/// `ns :: #import`), deduped by author identity (a diamond import of the
|
||||
/// same module is one author): `≥2 distinct` → `.ambiguous`; exactly one
|
||||
/// that DIFFERS from the winner → bind it; otherwise `.none`.
|
||||
/// that DIFFERS from the winner → select it; otherwise `.none`.
|
||||
///
|
||||
/// Generic / comptime / foreign / builtin authors are never rerouted — the
|
||||
/// existing dispatch owns those shapes — so the resolver returns `.none`.
|
||||
pub fn resolveBareCallee(self: *Lowering, name: []const u8, caller_file: []const u8) BareCallee {
|
||||
const module_fns = self.program_index.module_fns orelse return .none;
|
||||
/// existing dispatch owns those shapes; `isPlainFreeFn` filters them out
|
||||
/// BEFORE the count gate (so a same-name collision of non-plain authors is
|
||||
/// NOT ambiguous), and the selector returns `.none`. No eager
|
||||
/// materialization: the returned `SelectedFunc` carries decl + source and
|
||||
/// `materialized = null`; a consumer fills the FuncId via `selectedFuncId`
|
||||
/// only when it truly needs it (0102d).
|
||||
pub fn selectPlainCallableAuthor(self: *Lowering, name: []const u8, caller_file: []const u8) BareCallee {
|
||||
const winner = self.program_index.fn_ast_map.get(name);
|
||||
var res = self.resolver();
|
||||
const set = res.collectVisibleAuthors(name, caller_file, .user_bare_flat);
|
||||
defer if (set.flat.len > 0) self.alloc.free(set.flat);
|
||||
|
||||
// own-author wins.
|
||||
if (module_fns.get(caller_file)) |own_fns| {
|
||||
if (own_fns.get(name)) |own| {
|
||||
// own-author wins. The collector's `own` spans all domains; a non-fn
|
||||
// (or a const not bound to a function) means `caller_file` has no fn
|
||||
// `name` — fall through to the flat authors, exactly as the fn-only
|
||||
// `module_fns` walk did.
|
||||
if (set.own) |own_author| {
|
||||
if (fnDeclOfRaw(own_author.raw)) |own| {
|
||||
if (winner != null and winner.? == own) return .none;
|
||||
if (!isPlainFreeFn(own)) return .none;
|
||||
return .{ .func = .{ .fid = self.bareAuthorFuncId(own, name, caller_file), .decl = own } };
|
||||
return .{ .func = .{ .decl = own, .source = own_author.source } };
|
||||
}
|
||||
}
|
||||
|
||||
// Caller does not author `name` → collect its flat-reachable authors.
|
||||
const flat_graph = self.program_index.flat_import_graph orelse return .none;
|
||||
const edges = flat_graph.get(caller_file) orelse return .none;
|
||||
var distinct = std.AutoHashMap(*const ast.FnDecl, []const u8).init(self.alloc);
|
||||
defer distinct.deinit();
|
||||
var edge_it = edges.iterator();
|
||||
while (edge_it.next()) |e| {
|
||||
const fns = module_fns.get(e.key_ptr.*) orelse continue;
|
||||
// Only plain free functions are eligible for rerouting; generic /
|
||||
// foreign / builtin / #compiler authors keep their existing
|
||||
// dispatch. Filtering BEFORE the count gate means a same-name
|
||||
// collision of non-plain authors (e.g. two flat-imported modules
|
||||
// each `#foreign`ing the same symbol) is NOT counted as ambiguous —
|
||||
// it falls through to `.none` and the existing first-wins path.
|
||||
if (fns.get(name)) |fd| {
|
||||
if (!isPlainFreeFn(fd)) continue;
|
||||
distinct.put(fd, e.key_ptr.*) catch {};
|
||||
}
|
||||
// Caller does not author `name` as a fn → its flat-reachable authors.
|
||||
// Filter to plain free functions BEFORE counting: a same-name collision
|
||||
// of non-plain authors (e.g. two flat-imported modules each `#foreign`ing
|
||||
// the same symbol) is NOT counted as ambiguous — it falls through to
|
||||
// `.none` and the existing first-wins path.
|
||||
var the_one: ?*const ast.FnDecl = null;
|
||||
var the_source: []const u8 = &.{};
|
||||
var count: usize = 0;
|
||||
for (set.flat) |fa| {
|
||||
const fd = fnDeclOfRaw(fa.raw) orelse continue;
|
||||
if (!isPlainFreeFn(fd)) continue;
|
||||
count += 1;
|
||||
if (count >= 2) return .ambiguous;
|
||||
the_one = fd;
|
||||
the_source = fa.source;
|
||||
}
|
||||
if (distinct.count() == 0) return .none;
|
||||
if (distinct.count() >= 2) return .ambiguous;
|
||||
if (count == 0) return .none;
|
||||
if (winner != null and winner.? == the_one.?) return .none;
|
||||
return .{ .func = .{ .decl = the_one.?, .source = the_source } };
|
||||
}
|
||||
|
||||
var one_it = distinct.iterator();
|
||||
const entry = one_it.next().?;
|
||||
const the_one = entry.key_ptr.*;
|
||||
const the_path = entry.value_ptr.*;
|
||||
if (winner != null and winner.? == the_one) return .none;
|
||||
return .{ .func = .{ .fid = self.bareAuthorFuncId(the_one, name, the_path), .decl = the_one } };
|
||||
/// The `*FnDecl` a raw author wraps, or null when the author is not a
|
||||
/// function — `imports.fnDeclOf` over a `RawDeclRef` so the collector's
|
||||
/// all-domain authors reproduce `module_fns`' fn-only view (a `const`-wrapped
|
||||
/// fn unwraps to its inner fn, the same pointer `module_fns` holds; every
|
||||
/// other domain → null).
|
||||
fn fnDeclOfRaw(ref: resolver_mod.RawDeclRef) ?*const ast.FnDecl {
|
||||
return switch (ref) {
|
||||
.fn_decl => |fd| fd,
|
||||
.const_decl => |cd| if (cd.value.data == .fn_decl) &cd.value.data.fn_decl else null,
|
||||
else => null,
|
||||
};
|
||||
}
|
||||
|
||||
/// Materialize (lower-on-demand) the FuncId for a selected bare-call author,
|
||||
/// caching into `sf.materialized`. Shadow-only: the winner owns the
|
||||
/// name-keyed slot and lowers through the lazy path, so
|
||||
/// `selectPlainCallableAuthor` returns `.none` for it and this is never asked
|
||||
/// to lower the winner (0102d). `name` is the call name (== the author's
|
||||
/// registered name); `sf.source` pins the author's own visibility context.
|
||||
fn selectedFuncId(self: *Lowering, sf: *SelectedFunc, name: []const u8) FuncId {
|
||||
if (sf.materialized) |fid| return fid;
|
||||
const fid = self.bareAuthorFuncId(sf.decl, name, sf.source);
|
||||
sf.materialized = fid;
|
||||
return fid;
|
||||
}
|
||||
|
||||
/// The FuncId for a resolved bare-call author, ensuring its body is lowered.
|
||||
/// Only ever called for a SHADOW (an author that is not the name-keyed
|
||||
/// winner): the winner owns the name-keyed slot and lowers through the
|
||||
/// normal lazy path, so `resolveBareCallee` returns `.none` for it. A shadow
|
||||
/// normal lazy path, so `selectPlainCallableAuthor` returns `.none` for it. A shadow
|
||||
/// is declared a fresh same-name FuncId in its OWN module's visibility
|
||||
/// context and its body lowered into that slot via fix-0102b's identity-
|
||||
/// addressable `lowerFunctionBodyInto`. Idempotent: `lowered_fids` tracks
|
||||
@@ -3325,8 +3371,11 @@ pub const Lowering = struct {
|
||||
(if (self.scope) |scope| scope.lookup(id.name) == null else true))
|
||||
{
|
||||
if (self.current_source_file) |caller_file| {
|
||||
switch (self.resolveBareCallee(id.name, caller_file)) {
|
||||
.func => |resolved| break :blk_fv resolved.fid,
|
||||
switch (self.selectPlainCallableAuthor(id.name, caller_file)) {
|
||||
.func => |sf| {
|
||||
var selected = sf;
|
||||
break :blk_fv self.selectedFuncId(&selected, id.name);
|
||||
},
|
||||
.ambiguous => {
|
||||
if (self.diagnostics) |d|
|
||||
d.addFmt(.err, node.span, "'{s}' is ambiguous; declared by multiple imported modules — qualify the call", .{id.name});
|
||||
@@ -7377,8 +7426,11 @@ pub const Lowering = struct {
|
||||
(if (self.scope) |scope| scope.lookup(fn_name) == null else true))
|
||||
{
|
||||
if (self.current_source_file) |caller_file| {
|
||||
switch (self.resolveBareCallee(fn_name, caller_file)) {
|
||||
.func => |resolved| break :blk_cl resolved.fid,
|
||||
switch (self.selectPlainCallableAuthor(fn_name, caller_file)) {
|
||||
.func => |sf| {
|
||||
var selected = sf;
|
||||
break :blk_cl self.selectedFuncId(&selected, fn_name);
|
||||
},
|
||||
.ambiguous => {
|
||||
if (self.diagnostics) |d|
|
||||
d.addFmt(.err, arg.span, "'{s}' is ambiguous; declared by multiple imported modules — qualify the call", .{fn_name});
|
||||
@@ -7682,27 +7734,28 @@ pub const Lowering = struct {
|
||||
// author. Only a plain top-level identifier call routes here:
|
||||
// scope-mangled / UFCS-aliased / locally-shadowed names and
|
||||
// 0/1-author names fall straight to the existing path below
|
||||
// (`resolveBareCallee` returns `.none`).
|
||||
// (`selectPlainCallableAuthor` returns `.none`).
|
||||
if (std.mem.eql(u8, func_name, id.name) and
|
||||
(if (self.scope) |scope| scope.lookup(id.name) == null else true))
|
||||
{
|
||||
if (self.current_source_file) |caller_file| {
|
||||
switch (self.resolveBareCallee(func_name, caller_file)) {
|
||||
switch (self.selectPlainCallableAuthor(func_name, caller_file)) {
|
||||
.none => {},
|
||||
.ambiguous => {
|
||||
if (self.diagnostics) |d|
|
||||
d.addFmt(.err, c.callee.span, "'{s}' is ambiguous; declared by multiple imported modules — qualify the call", .{func_name});
|
||||
return Ref.none;
|
||||
},
|
||||
.func => |resolved| {
|
||||
const fid = resolved.fid;
|
||||
.func => |sf| {
|
||||
var selected = sf;
|
||||
const fid = self.selectedFuncId(&selected, func_name);
|
||||
const func = &self.module.functions.items[@intFromEnum(fid)];
|
||||
const ret_ty = func.ret;
|
||||
const params = func.params;
|
||||
// The RESOLVED author's decl drives variadic
|
||||
// packing — not a first-wins re-lookup by name,
|
||||
// whose variadic shape may differ (fix-0102c F1).
|
||||
self.packVariadicCallArgs(resolved.decl, c, &args);
|
||||
self.packVariadicCallArgs(selected.decl, c, &args);
|
||||
const final_args = self.prependCtxIfNeeded(func, args.items);
|
||||
self.coerceCallArgs(final_args, params);
|
||||
if (func.is_variadic) self.promoteCVariadicArgs(final_args, params.len);
|
||||
@@ -8202,8 +8255,11 @@ pub const Lowering = struct {
|
||||
// → existing first-wins path.
|
||||
const ufcs_fid: ?FuncId = blk_uf: {
|
||||
if (self.current_source_file) |caller_file| {
|
||||
switch (self.resolveBareCallee(fa.field, caller_file)) {
|
||||
.func => |resolved| break :blk_uf resolved.fid,
|
||||
switch (self.selectPlainCallableAuthor(fa.field, caller_file)) {
|
||||
.func => |sf| {
|
||||
var selected = sf;
|
||||
break :blk_uf self.selectedFuncId(&selected, fa.field);
|
||||
},
|
||||
.ambiguous => {
|
||||
if (self.diagnostics) |d|
|
||||
d.addFmt(.err, c.callee.span, "'{s}' is ambiguous; declared by multiple imported modules — qualify the call", .{fa.field});
|
||||
@@ -12054,8 +12110,14 @@ pub const Lowering = struct {
|
||||
(if (self.scope) |scope| scope.lookup(id.name) == null else true))
|
||||
{
|
||||
if (self.current_source_file) |caller_file| {
|
||||
switch (self.resolveBareCallee(id.name, caller_file)) {
|
||||
.func => |resolved| break :blk resolved.decl,
|
||||
switch (self.selectPlainCallableAuthor(id.name, caller_file)) {
|
||||
// Default expansion needs only the author's decl
|
||||
// (its param defaults) — never the FuncId. Reading
|
||||
// `sf.decl` here keeps `materialized` null, so a
|
||||
// bare call whose body is never emitted (e.g. only
|
||||
// its defaults are inspected) does not lower the
|
||||
// author (0102d).
|
||||
.func => |sf| break :blk sf.decl,
|
||||
.ambiguous => return null,
|
||||
.none => {},
|
||||
}
|
||||
@@ -12296,9 +12358,11 @@ pub const Lowering = struct {
|
||||
(if (self.scope) |scope| scope.lookup(bare_name) == null else true))
|
||||
{
|
||||
if (self.current_source_file) |caller_file| {
|
||||
switch (self.resolveBareCallee(bare_name, caller_file)) {
|
||||
.func => |resolved| {
|
||||
const func = &self.module.functions.items[@intFromEnum(resolved.fid)];
|
||||
switch (self.selectPlainCallableAuthor(bare_name, caller_file)) {
|
||||
.func => |sf| {
|
||||
var selected = sf;
|
||||
const fid = self.selectedFuncId(&selected, bare_name);
|
||||
const func = &self.module.functions.items[@intFromEnum(fid)];
|
||||
return self.userParamTypes(func);
|
||||
},
|
||||
.ambiguous, .none => {},
|
||||
@@ -14548,6 +14612,13 @@ pub const Lowering = struct {
|
||||
return .{ .l = self };
|
||||
}
|
||||
|
||||
/// A `Resolver` facade over the borrowed Phase A import facts (Phase B). Cheap
|
||||
/// by-value; `collectVisibleAuthors`'s `AuthorSet.flat` slice is backed by
|
||||
/// `self.alloc` and owned by the caller (`selectPlainCallableAuthor` frees it).
|
||||
fn resolver(self: *Lowering) resolver_mod.Resolver {
|
||||
return resolver_mod.Resolver.init(&self.program_index, self.alloc);
|
||||
}
|
||||
|
||||
pub fn genericResolver(self: *Lowering) GenericResolver {
|
||||
return .{ .l = self };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user