mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-06 13:54:21 +00:00
disallow switch case capture discards
Previously Zig allowed you to write something like,
```zig
switch (x) {
.y => |_| {
```
This seems a bit strange because in other cases, such as when
capturing the tag in a switch case,
```zig
switch (x) {
.y => |_, _| {
```
this produces an error.
The only usecase I can think of for the previous behaviour is
if you wanted to assert that all union payloads are able
to coerce,
```zig
const X = union(enum) { y: u8, z: f32 };
switch (x) {
.y, .z => |_| {
```
This will compile-error with the `|_|` and pass without it.
I don't believe this usecase is strong enough to keep the current
behaviour; it was never used in the Zig codebase and I cannot
find a single usage of this behaviour in the real world, searching
through Sourcegraph.
This commit is contained in:
parent
c4b7caa528
commit
d4496d9230
5 changed files with 56 additions and 5 deletions
|
|
@ -801,7 +801,7 @@ fn expectEqualDeepInner(comptime T: type, expected: T, actual: T) error{TestExpe
|
|||
}
|
||||
},
|
||||
|
||||
.array => |_| {
|
||||
.array => {
|
||||
if (expected.len != actual.len) {
|
||||
print("Array len not the same, expected {d}, found {d}\n", .{ expected.len, actual.len });
|
||||
return error.TestExpectedEqual;
|
||||
|
|
|
|||
|
|
@ -7882,8 +7882,10 @@ fn switchExpr(
|
|||
var payload_sub_scope: *Scope = undefined;
|
||||
if (mem.eql(u8, ident_slice, "_")) {
|
||||
if (capture_is_ref) {
|
||||
// |*_, tag| is invalid, so we can fail early
|
||||
return astgen.failTok(payload_token, "pointer modifier invalid on discard", .{});
|
||||
}
|
||||
capture = .none;
|
||||
payload_sub_scope = &case_scope.base;
|
||||
} else {
|
||||
const capture_name = try astgen.identAsString(ident);
|
||||
|
|
@ -7903,11 +7905,15 @@ fn switchExpr(
|
|||
|
||||
const tag_token = if (tree.tokenTag(ident + 1) == .comma)
|
||||
ident + 2
|
||||
else
|
||||
break :blk payload_sub_scope;
|
||||
else if (capture == .none) {
|
||||
// discarding the capture is only valid iff the tag is captured
|
||||
// whether the tag capture is discarded is handled below
|
||||
return astgen.failTok(payload_token, "discard of capture; omit it instead", .{});
|
||||
} else break :blk payload_sub_scope;
|
||||
|
||||
const tag_slice = tree.tokenSlice(tag_token);
|
||||
if (mem.eql(u8, tag_slice, "_")) {
|
||||
try astgen.appendErrorTok(tag_token, "discard of tag capture; omit it instead", .{});
|
||||
return astgen.failTok(tag_token, "discard of tag capture; omit it instead", .{});
|
||||
} else if (case.inline_token == null) {
|
||||
return astgen.failTok(tag_token, "tag capture on non-inline prong", .{});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11338,7 +11338,9 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
|
|||
});
|
||||
}
|
||||
try sema.validateRuntimeValue(block, operand_src, maybe_ptr);
|
||||
const operand_alloc = if (extra.data.bits.any_non_inline_capture) a: {
|
||||
const operand_alloc = if (extra.data.bits.any_non_inline_capture or
|
||||
extra.data.bits.any_has_tag_capture)
|
||||
a: {
|
||||
const operand_ptr_ty = try pt.singleMutPtrType(sema.typeOf(maybe_ptr));
|
||||
const operand_alloc = try block.addTy(.alloc, operand_ptr_ty);
|
||||
_ = try block.addBinOp(.store, operand_alloc, maybe_ptr);
|
||||
|
|
|
|||
|
|
@ -273,3 +273,30 @@ test "switch loop on non-exhaustive enum" {
|
|||
try S.doTheTest();
|
||||
try comptime S.doTheTest();
|
||||
}
|
||||
|
||||
test "switch loop with discarded tag capture" {
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
|
||||
|
||||
const S = struct {
|
||||
const U = union(enum) {
|
||||
a: u32,
|
||||
b: u32,
|
||||
c: u32,
|
||||
};
|
||||
|
||||
fn doTheTest() void {
|
||||
const a: U = .{ .a = 10 };
|
||||
blk: switch (a) {
|
||||
inline .b => |_, tag| {
|
||||
_ = tag;
|
||||
continue :blk .{ .c = 20 };
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
};
|
||||
S.doTheTest();
|
||||
comptime S.doTheTest();
|
||||
}
|
||||
|
|
|
|||
16
test/cases/compile_errors/switch_loop_discarded_capture.zig
Normal file
16
test/cases/compile_errors/switch_loop_discarded_capture.zig
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
export fn foo() void {
|
||||
const S = struct {
|
||||
fn doTheTest() void {
|
||||
blk: switch (@as(u8, 'a')) {
|
||||
'1' => |_| continue :blk '1',
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
};
|
||||
S.doTheTest();
|
||||
comptime S.doTheTest();
|
||||
}
|
||||
|
||||
// error
|
||||
//
|
||||
// :5:25: error: discard of capture; omit it instead
|
||||
Loading…
Add table
Reference in a new issue