diff --git a/src/ir/calls.zig b/src/ir/calls.zig index daa502f..5214d5c 100644 --- a/src/ir/calls.zig +++ b/src/ir/calls.zig @@ -413,7 +413,9 @@ pub const CallResolver = struct { /// (so `pkg.fn(...)` is a namespace call). This is exactly the negation of /// `lowerCall`'s `is_namespace`: a non-identifier object is always a value; /// an identifier / type_expr is a value iff it names a local or a global. - fn objectIsValue(self: CallResolver, obj: *const Node) bool { + /// `pub` so `lowerCall` sources its namespace/value boundary here rather + /// than re-deriving it — one definition, shared by typing and lowering. + pub fn objectIsValue(self: CallResolver, obj: *const Node) bool { const obj_name: []const u8 = switch (obj.data) { .identifier => |id| id.name, .type_expr => |te| te.name, diff --git a/src/ir/lower.zig b/src/ir/lower.zig index 1455bb8..d795af5 100644 --- a/src/ir/lower.zig +++ b/src/ir/lower.zig @@ -7696,26 +7696,13 @@ pub const Lowering = struct { } } - // Check if this is a namespace-qualified call (e.g., std.print) - // If the object is an identifier/type_expr not in scope, treat as namespace prefix - const is_namespace = blk: { - const obj_name: ?[]const u8 = switch (fa.object.data) { - .identifier => |id| id.name, - .type_expr => |te| te.name, - else => null, - }; - if (obj_name) |name| { - // Check local scope first - if (self.scope) |scope| { - if (scope.lookup(name) != null) break :blk false; - } - // Check global variables (e.g., g_font : *FontAtlas) - if (self.program_index.global_names.contains(name)) break :blk false; - // Not a local or global variable → namespace prefix - break :blk true; - } - break :blk false; - }; + // Namespace-qualified call (e.g. `std.print`) vs method / UFCS + // call on a value (`recv.method`). This boundary decides whether + // the receiver is prepended, so it MUST agree with the call + // plan's `free_fn_ufcs` (prepends) vs `namespace_fn` (does not) + // classification — source it from the single definition in + // `CallResolver` rather than re-deriving it here. + const is_namespace = !self.callResolver().objectIsValue(fa.object); if (is_namespace) { // Namespace call: module.func(args) — don't prepend object