Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/std/testing.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
12 changes: 9 additions & 3 deletions lib/std/zig/AstGen.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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", .{});
}
Expand Down
4 changes: 3 additions & 1 deletion src/Sema.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
27 changes: 27 additions & 0 deletions test/behavior/switch_loop.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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 changes: 16 additions & 0 deletions test/cases/compile_errors/switch_loop_discarded_capture.zig
Original file line number Diff line number Diff line change
@@ -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