issue 0153 RESOLVED: pin generic return-type resolution to the fn's defining module
inferGenericReturnType resolved a generic call's return-type AST ($R, !E) in the CALL-SITE module context. For a re-exported fn the error-set name (LE / IoErr, re-exported as LE :: lib.LE) resolved through the call-site alias to a TypeId NOT tagged .error_set, so the planned result was a tuple whose last field wasn't an error set — errorChannelOf saw a plain tuple and the value- failable's ! channel was lost (try/or rejected it / built a malformed i1 PHI). monomorphizeFunction already pins the source to the fn's defining module before resolving the return type; inferGenericReturnType did not, so the planned call-result type disagreed with the instance's real signature. Fix: pin the source to fd.body.source_file around the return-type resolution (binding-build stays in the call-site context — its args are typed there). Regression test examples/1058-errors-reexport-value-failable-channel.sx (+ companion lib.sx). Suite green 732/0.
This commit is contained in:
@@ -1,5 +1,34 @@
|
||||
# 0153 — a re-exported generic value-failable `($R, !E)` loses its `!` error channel
|
||||
|
||||
## ✅ RESOLVED (2026-06-21)
|
||||
|
||||
**Root cause** — `GenericResolver.inferGenericReturnType`
|
||||
(`src/ir/generics.zig`) resolved the generic call's return-type AST
|
||||
(`($R, !E)`) in the CALL-SITE module context. For a re-exported fn the error
|
||||
set name (`LE` / `IoErr`, re-exported as `LE :: lib.LE`) resolved through the
|
||||
call-site alias to a TypeId that is NOT tagged `.error_set`, so the planned
|
||||
result was a tuple whose last field wasn't an error set — `errorChannelOf`
|
||||
(`lower/error.zig:148`) saw a plain tuple and the failable channel was lost.
|
||||
`monomorphizeFunction` already pins the source to the fn's defining module
|
||||
before resolving the return type; `inferGenericReturnType` did not, so the
|
||||
planned call-result type and the instance's real signature disagreed.
|
||||
|
||||
**Fix** — pin the source to the function's defining module
|
||||
(`fd.body.source_file`) around the return-type resolution in
|
||||
`inferGenericReturnType`, mirroring `monomorphizeFunction`. The binding-build
|
||||
stays in the call-site context (its args are typed there). Now the `!E`
|
||||
resolves to the same `.error_set` TypeId the instance's signature uses.
|
||||
|
||||
**Verified** — the repro prints `r=42`; regression test
|
||||
`examples/1058-errors-reexport-value-failable-channel.sx` (+ companion
|
||||
`lib.sx`). This also unblocked the B1.2 async surface end-to-end:
|
||||
`examples/1805-concurrency-io-blocking-async.sx` (`sum: 42` / `double: 42` /
|
||||
`clock ok`) + `examples/1806-concurrency-io-cancel.sx` (cancel → `await`
|
||||
raises `.Canceled`). Full suite green.
|
||||
|
||||
---
|
||||
|
||||
|
||||
## Symptom
|
||||
|
||||
A generic function returning a value-failable `($R, !E)` keeps its error
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
// Repro for issue 0153 — a generic value-failable fn `($R, !E)` reached
|
||||
// through a RE-EXPORT alias loses its `!` error channel at the call site:
|
||||
// the result is typed as a plain tuple, so `try`/`or` reject it / build a
|
||||
// malformed PHI. Needs BOTH generic + re-export: a non-generic re-export
|
||||
// works, and a directly-imported (non-re-exported) generic value-failable
|
||||
// works. Mirrors std.sx's `await :: io_mod.await` (+ `IoErr :: io_mod.IoErr`).
|
||||
#import "modules/std.sx";
|
||||
lib :: #import "issues/0153-reexport-generic-value-failable-loses-error-channel/lib.sx";
|
||||
|
||||
// Re-export the generic fn AND its error set (the std.sx facade pattern).
|
||||
Box :: lib.Box;
|
||||
get :: lib.get;
|
||||
LE :: lib.LE;
|
||||
|
||||
main :: () -> i32 {
|
||||
b : Box(i64) = .{ v = 42 };
|
||||
r := b.get() or { -1 }; // BUG: PHI i1/i64 mismatch (was: clean → r=42)
|
||||
print("r={}\n", r);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
// Implementation module: a generic value-failable `ufcs` fn + its error set.
|
||||
#import "modules/std.sx";
|
||||
|
||||
LE :: error { Bad }
|
||||
Box :: struct ($R: Type) { v: R; }
|
||||
|
||||
// Returns `($R, !LE)` — a value-failable. `$R` is inferred from the arg.
|
||||
get :: ufcs (b: *Box($R)) -> ($R, !LE) { return b.v; }
|
||||
Reference in New Issue
Block a user