fix(lower): fn-value site lazily lowers winner only on resolver .none [0102d F1]
The bare-fn-as-value site (func_ref / fn-ptr / closure coercion) eagerly lazily-lowered the name-keyed first-wins WINNER before the resolveBareCallee block could reroute a genuine flat same-name collision to its per-source author. Taking a SHADOW author's fn value therefore lowered (and could mis-diagnose) the unused winner's body. Move lazyLowerFunction INSIDE blk_fv onto the `.none` fallback only, mirroring the closure(fn) and free-function UFCS sites: on `.func` use the resolved author's FuncId and never touch the winner; on `.none` fall through to lazy-lower + resolveFuncByName the winner. Regression: examples/0735-modules-flat-same-name-fn-value-winner — the first-wins winner's body is independently broken and never used; a shadow taken as a function value binds the shadow and runs (exit 0) while the winner is not lowered. Fails-before (unresolved symbol in the winner), passes-after.
This commit is contained in:
17
examples/0735-modules-flat-same-name-fn-value-winner.sx
Normal file
17
examples/0735-modules-flat-same-name-fn-value-winner.sx
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
// fix-0102d site 2 / attempt-2 (issue 0102): the first-wins winner's body is
|
||||||
|
// independently BROKEN (references an undefined symbol) and is never used. A
|
||||||
|
// shadow author from a later flat import takes its OWN `pick` as a function
|
||||||
|
// VALUE (`g : () -> s64 = pick`). The value must bind the shadow (own-author
|
||||||
|
// wins) and the broken winner must NOT be lowered — a rerouted fn value never
|
||||||
|
// uses the winner. Before the fix the fn-value site eagerly lazily-lowered the
|
||||||
|
// name-keyed winner BEFORE the resolver rerouted, surfacing the winner's
|
||||||
|
// `unresolved 'missing_from_a'` for a function the value never touches.
|
||||||
|
// Regression: per-source function-value conversion must not pre-lower the winner.
|
||||||
|
#import "modules/std.sx";
|
||||||
|
#import "0735-modules-flat-same-name-fn-value-winner/a.sx";
|
||||||
|
#import "0735-modules-flat-same-name-fn-value-winner/b.sx";
|
||||||
|
|
||||||
|
main :: () -> s32 {
|
||||||
|
print("from_b_value = {}\n", from_b_value());
|
||||||
|
0
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
// a.sx authors `pick` (imported first → the first-wins name-keyed winner) but
|
||||||
|
// its body references an undefined symbol, so lowering a.pick AT ALL is an
|
||||||
|
// error. Nothing uses a.pick — taking b.pick as a value must not pre-lower it.
|
||||||
|
pick :: () -> s64 { return missing_from_a(); }
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// b.sx authors its OWN `pick` (returns 2) and takes it as a function VALUE. The
|
||||||
|
// value binds b.pick (own-author wins), never the broken winner from a.sx.
|
||||||
|
pick :: () -> s64 { return 2; }
|
||||||
|
from_b_value :: () -> s64 {
|
||||||
|
g : () -> s64 = pick;
|
||||||
|
return g();
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
0
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
from_b_value = 2
|
||||||
@@ -60,7 +60,9 @@ collision:
|
|||||||
args fill from the RESOLVED author's defaults, not the winner's.
|
args fill from the RESOLVED author's defaults, not the winner's.
|
||||||
2. **Function-value conversion** (`closure(fn)` and the bare-fn-as-value
|
2. **Function-value conversion** (`closure(fn)` and the bare-fn-as-value
|
||||||
`func_ref` / fn-ptr / closure-coercion path): captures the resolved
|
`func_ref` / fn-ptr / closure-coercion path): captures the resolved
|
||||||
author's FuncId.
|
author's FuncId. The winner's body is lazily lowered ONLY on the `.none`
|
||||||
|
fallback — a rerouted value never uses the winner, so taking a shadow as a
|
||||||
|
value must not pre-lower (and possibly mis-diagnose) the winner's body.
|
||||||
3. **Free-function UFCS** (`recv.fn()` → `fn(recv, …)`): dispatches the
|
3. **Free-function UFCS** (`recv.fn()` → `fn(recv, …)`): dispatches the
|
||||||
resolved author for the receiver's source.
|
resolved author for the receiver's source.
|
||||||
4. **Comptime `#run`** of a bare call: `lowerMainAndComptime` now sets
|
4. **Comptime `#run`** of a bare call: `lowerMainAndComptime` now sets
|
||||||
@@ -71,7 +73,7 @@ collision:
|
|||||||
|
|
||||||
## Regression tests
|
## Regression tests
|
||||||
|
|
||||||
`examples/0722`–`0734` (each a focused multi-file flat-collision scene that
|
`examples/0722`–`0735` (each a focused multi-file flat-collision scene that
|
||||||
fails on pre-fix code and passes after):
|
fails on pre-fix code and passes after):
|
||||||
|
|
||||||
- `0722-modules-flat-same-name-own` — own-author wins on the call path.
|
- `0722-modules-flat-same-name-own` — own-author wins on the call path.
|
||||||
@@ -93,3 +95,8 @@ fails on pre-fix code and passes after):
|
|||||||
callee.
|
callee.
|
||||||
- `0734-modules-flat-same-name-ufcs-ambiguous` — `≥2` flat authors, UFCS call
|
- `0734-modules-flat-same-name-ufcs-ambiguous` — `≥2` flat authors, UFCS call
|
||||||
→ loud diagnostic (pre-fix: silently bound the winner).
|
→ loud diagnostic (pre-fix: silently bound the winner).
|
||||||
|
- `0735-modules-flat-same-name-fn-value-winner` — the first-wins winner's body
|
||||||
|
is independently broken and never used; a shadow taken as a function value
|
||||||
|
binds the shadow and runs while the winner is NOT lowered (pre-fix: the
|
||||||
|
fn-value site eagerly lowered the winner before the resolver rerouted,
|
||||||
|
surfacing the winner's error for a function the value never touches).
|
||||||
|
|||||||
@@ -3250,14 +3250,13 @@ pub const Lowering = struct {
|
|||||||
const str = self.builder.constString(sid);
|
const str = self.builder.constString(sid);
|
||||||
break :blk self.builder.boxAny(str, .string);
|
break :blk self.builder.boxAny(str, .string);
|
||||||
}
|
}
|
||||||
if (!self.lowered_functions.contains(eff_fn_name)) {
|
|
||||||
self.lazyLowerFunction(eff_fn_name);
|
|
||||||
}
|
|
||||||
// fix-0102d site 2: taking a bare same-name fn as a VALUE
|
// fix-0102d site 2: taking a bare same-name fn as a VALUE
|
||||||
// (func_ref, fn-ptr / closure coercion) must capture the
|
// (func_ref, fn-ptr / closure coercion) must capture the
|
||||||
// RESOLVED author's FuncId for a genuine flat collision, not
|
// RESOLVED author's FuncId for a genuine flat collision, not
|
||||||
// the first-wins winner's. Plain bare name only; `.ambiguous`
|
// the first-wins winner's. Plain bare name only; `.ambiguous`
|
||||||
// → loud diagnostic; `.none` → existing first-wins path.
|
// → loud diagnostic; `.none` → existing first-wins path. The
|
||||||
|
// winner is lazily lowered ONLY on `.none` — a rerouted value
|
||||||
|
// never uses the winner, so its body must not be lowered.
|
||||||
const value_fid: ?FuncId = blk_fv: {
|
const value_fid: ?FuncId = blk_fv: {
|
||||||
if (std.mem.eql(u8, eff_fn_name, id.name) and
|
if (std.mem.eql(u8, eff_fn_name, id.name) and
|
||||||
self.program_index.ufcs_alias_map.get(id.name) == null and
|
self.program_index.ufcs_alias_map.get(id.name) == null and
|
||||||
@@ -3275,6 +3274,9 @@ pub const Lowering = struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!self.lowered_functions.contains(eff_fn_name)) {
|
||||||
|
self.lazyLowerFunction(eff_fn_name);
|
||||||
|
}
|
||||||
break :blk_fv self.resolveFuncByName(eff_fn_name);
|
break :blk_fv self.resolveFuncByName(eff_fn_name);
|
||||||
};
|
};
|
||||||
if (value_fid) |fid| {
|
if (value_fid) |fid| {
|
||||||
|
|||||||
Reference in New Issue
Block a user