ERR/E1.8: reject dropping a failable's error slot on destructure
The error slot of a value-carrying failable can no longer be silently dropped on a bare destructure. In lowerDestructureDecl, when the RHS is failable (errorChannelOf(ty) != null), the error slot (always the last tuple field) must be bound to a non-`_` name. Reject when it is omitted entirely (fewer names than slots — e.g. `a, c := inc(5)` for `inc: -> (s32,s32,!E)`) or bound to `_` (`v, _ := parse(5)`). The `try` / `catch` / `or value` consumer forms all strip the error channel (their result type is non-failable), so the check never fires on them — only a bare failable destructure is rejected. Value-slot `_` discards stay legal (`a, _, ae := pair()` binds the error). This is the discard-rejection slice of E1.8; the path-sensitive flow-check (value live only where err==null is provable) is a separate follow-up. examples/236-failable-discard-reject.sx covers both rejected shapes (exit 1). Gates: zig build, zig build test, 274/274 examples.
This commit is contained in:
@@ -8313,6 +8313,23 @@ pub const Lowering = struct {
|
||||
const tuple = ti.tuple;
|
||||
if (dd.names.len > tuple.fields.len) return;
|
||||
|
||||
// E1.8 (discard rejection): when the RHS is a value-carrying failable,
|
||||
// the error slot (always the LAST tuple field) cannot be dropped. It is
|
||||
// dropped when the destructure omits it (fewer names than fields, so the
|
||||
// trailing error slot is never reached) or binds it to `_`. The `try` /
|
||||
// `catch` / `or value` consumer forms all strip the error channel (their
|
||||
// result type is non-failable), so this fires only on a BARE failable
|
||||
// destructure — exactly the case that would let an error vanish silently.
|
||||
if (self.errorChannelOf(ty) != null) {
|
||||
const err_dropped = dd.names.len < tuple.fields.len or
|
||||
std.mem.eql(u8, dd.names[dd.names.len - 1], "_");
|
||||
if (err_dropped) {
|
||||
if (self.diagnostics) |diags| {
|
||||
diags.addFmt(.err, dd.value.span, "the error slot of a failable cannot be dropped — bind it (`v, err := …`) and handle it, or use `try` / `catch`", .{});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Extract each field and bind to a new variable
|
||||
for (dd.names, 0..) |name, i| {
|
||||
if (std.mem.eql(u8, name, "_")) continue; // discard
|
||||
|
||||
Reference in New Issue
Block a user