From 2131557669aef571df5c6d1333baa2f6c519690e Mon Sep 17 00:00:00 2001 From: agra Date: Sat, 6 Jun 2026 15:31:14 +0300 Subject: [PATCH] fix(lower): bare-call resolver skips non-plain authors before ambiguity gate [0102c F3] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit resolveBareCallee's flat-collect branch counted ALL same-name authors — including #foreign / generic / builtin / #compiler — before the isPlainFreeFn filter, so two flat-imported modules each #foreign-ing the same libc symbol under one sx name returned `.ambiguous` and errored, instead of falling to `.none` and the existing first-wins foreign path (master behavior). Filter authors to plain free functions DURING collection, before the count/ambiguity determination: a non-plain collision now yields 0 reroutable authors -> `.none`; genuine plain-fn collisions still yield >= 2 -> `.ambiguous` (0724 unchanged). The now-redundant single-author isPlainFreeFn check is dropped. Regression: examples/0729-modules-flat-same-name-foreign — two flat FILE imports each #foreign the same libc "abs" under name `absval`; a bare call resolves first-wins and runs (exit 0). Fails-before on this branch (ambiguity error), passes-after. --- examples/0729-modules-flat-same-name-foreign.sx | 14 ++++++++++++++ examples/0729-modules-flat-same-name-foreign/a.sx | 5 +++++ examples/0729-modules-flat-same-name-foreign/b.sx | 2 ++ .../0729-modules-flat-same-name-foreign.exit | 1 + .../0729-modules-flat-same-name-foreign.stderr | 1 + .../0729-modules-flat-same-name-foreign.stdout | 1 + src/ir/lower.zig | 12 ++++++++++-- 7 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 examples/0729-modules-flat-same-name-foreign.sx create mode 100644 examples/0729-modules-flat-same-name-foreign/a.sx create mode 100644 examples/0729-modules-flat-same-name-foreign/b.sx create mode 100644 examples/expected/0729-modules-flat-same-name-foreign.exit create mode 100644 examples/expected/0729-modules-flat-same-name-foreign.stderr create mode 100644 examples/expected/0729-modules-flat-same-name-foreign.stdout diff --git a/examples/0729-modules-flat-same-name-foreign.sx b/examples/0729-modules-flat-same-name-foreign.sx new file mode 100644 index 0000000..1fcb5b1 --- /dev/null +++ b/examples/0729-modules-flat-same-name-foreign.sx @@ -0,0 +1,14 @@ +// fix-0102c (issue 0102) F3 regression: two flat FILE imports each `#foreign` +// the SAME libc symbol under the SAME sx name `absval`. The bare-call resolver +// must NOT count `#foreign` (non-plain) authors when deciding ambiguity — it +// filters them out, returns "no rerouting", and the existing first-wins foreign +// dispatch binds the call. A same-name foreign collision therefore compiles and +// runs (master behavior), it does NOT error as ambiguous. +#import "modules/std.sx"; +#import "0729-modules-flat-same-name-foreign/a.sx"; +#import "0729-modules-flat-same-name-foreign/b.sx"; + +main :: () -> s32 { + print("absval = {}\n", absval(-7)); + 0 +} diff --git a/examples/0729-modules-flat-same-name-foreign/a.sx b/examples/0729-modules-flat-same-name-foreign/a.sx new file mode 100644 index 0000000..13b27f3 --- /dev/null +++ b/examples/0729-modules-flat-same-name-foreign/a.sx @@ -0,0 +1,5 @@ +// One of two flat authors of `absval`, a `#foreign` libc binding. A consumer +// flat-importing BOTH must NOT see this as an ambiguous bare-call collision — +// foreign authors are never rerouted by the bare-call resolver, so the call +// falls to the existing first-wins foreign dispatch. +absval :: (n: s32) -> s32 #foreign libc "abs"; diff --git a/examples/0729-modules-flat-same-name-foreign/b.sx b/examples/0729-modules-flat-same-name-foreign/b.sx new file mode 100644 index 0000000..527122e --- /dev/null +++ b/examples/0729-modules-flat-same-name-foreign/b.sx @@ -0,0 +1,2 @@ +// The second flat author of `absval` — the identical `#foreign` libc binding. +absval :: (n: s32) -> s32 #foreign libc "abs"; diff --git a/examples/expected/0729-modules-flat-same-name-foreign.exit b/examples/expected/0729-modules-flat-same-name-foreign.exit new file mode 100644 index 0000000..573541a --- /dev/null +++ b/examples/expected/0729-modules-flat-same-name-foreign.exit @@ -0,0 +1 @@ +0 diff --git a/examples/expected/0729-modules-flat-same-name-foreign.stderr b/examples/expected/0729-modules-flat-same-name-foreign.stderr new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/examples/expected/0729-modules-flat-same-name-foreign.stderr @@ -0,0 +1 @@ + diff --git a/examples/expected/0729-modules-flat-same-name-foreign.stdout b/examples/expected/0729-modules-flat-same-name-foreign.stdout new file mode 100644 index 0000000..bb4defe --- /dev/null +++ b/examples/expected/0729-modules-flat-same-name-foreign.stdout @@ -0,0 +1 @@ +absval = 7 diff --git a/src/ir/lower.zig b/src/ir/lower.zig index 7530a24..4ccbedd 100644 --- a/src/ir/lower.zig +++ b/src/ir/lower.zig @@ -1581,7 +1581,16 @@ pub const Lowering = struct { var edge_it = edges.iterator(); while (edge_it.next()) |e| { const fns = module_fns.get(e.key_ptr.*) orelse continue; - if (fns.get(name)) |fd| distinct.put(fd, e.key_ptr.*) catch {}; + // 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 {}; + } } if (distinct.count() == 0) return .none; if (distinct.count() >= 2) return .ambiguous; @@ -1591,7 +1600,6 @@ pub const Lowering = struct { const the_one = entry.key_ptr.*; const the_path = entry.value_ptr.*; if (winner != null and winner.? == the_one) return .none; - if (!isPlainFreeFn(the_one)) return .none; return .{ .func = .{ .fid = self.bareAuthorFuncId(the_one, name, the_path), .decl = the_one } }; }