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 } }; }