zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

commit f4f5d06b8019bf6b7b3c205c730d70f291a07fbf (tree)
parent 73b760e03361a86c2121b0e8b6e62de98721ee0d
Author: Justus Klausecker <justus@klausecker.de>
Date:   Tue,  5 May 2026 02:06:53 +0200

Sema: fail on switch loop tag capture of OPV type

This:
```zig
label: switch (@as(u0, 0)) {
    0 => |_, tag| {
        _ = tag;
        continue :label 0;
    },
}
```
would previously compile even though the operand is not a tagged union.
It is now a compile error.

Diffstat:
Msrc/Sema.zig | 60+++++++++++++++++++++++++++++++++++++-----------------------
Mtest/cases/compile_errors/switch_invalid_tag_capture.zig | 48++++++++++++++++++++++++++++++++++++++++++++----
2 files changed, 81 insertions(+), 27 deletions(-)

diff --git a/src/Sema.zig b/src/Sema.zig @@ -2418,6 +2418,32 @@ fn failWithModRemNegative(sema: *Sema, block: *Block, src: LazySrcLoc, lhs_ty: T }); } +fn failWithInvalidSwitchTagCapture(sema: *Sema, block: *Block, tag_capture_src: LazySrcLoc, operand_ty: Type) CompileError { + const pt = sema.pt; + const zcu = pt.zcu; + + if (operand_ty.zigTypeTag(zcu) == .@"union") { + assert(operand_ty.containerLayout(zcu) == .@"packed"); + return sema.failWithOwnedErrorMsg(block, msg: { + const msg = try sema.errMsg(tag_capture_src, "cannot capture tag of packed union", .{}); + errdefer msg.destroy(sema.gpa); + try sema.addDeclaredHereNote(msg, operand_ty); + if (operand_ty.srcLocOrNull(zcu)) |ty_src| { + try sema.errNote(ty_src, msg, "consider using a tagged union", .{}); + } + break :msg msg; + }); + } + return sema.failWithOwnedErrorMsg(block, msg: { + const msg = try sema.errMsg(tag_capture_src, "cannot capture tag of non-union type '{f}'", .{ + operand_ty.fmt(pt), + }); + errdefer msg.destroy(sema.gpa); + try sema.addDeclaredHereNote(msg, operand_ty); + break :msg msg; + }); +} + fn failWithExpectedOptionalType(sema: *Sema, block: *Block, src: LazySrcLoc, non_optional_ty: Type) CompileError { const pt = sema.pt; const msg = msg: { @@ -10216,7 +10242,7 @@ fn analyzeSwitchBlock( const case_vals = validated_switch.case_vals; - const body, const capture, const has_tag_capture = find_prong: { + const case_idx, const body, const capture, const has_tag_capture = find_prong: { var case_val_idx: usize = 0; var case_it = zir_switch.iterateCases(); var extra_index = zir_switch.end; @@ -10239,12 +10265,12 @@ fn analyzeSwitchBlock( } continue; } - break :find_prong .{ prong_body, prong_info.capture, prong_info.has_tag_capture }; + break :find_prong .{ case.index, prong_body, prong_info.capture, prong_info.has_tag_capture }; } if (has_else) { // This *has* to be checked after iterating all regular cases because // we allow simple noreturn else prongs when switching on error sets! - break :find_prong .{ else_case.body, else_case.capture, else_case.has_tag_capture }; + break :find_prong .{ else_case.index, else_case.body, else_case.capture, else_case.has_tag_capture }; } unreachable; // malformed validated switch }; @@ -10289,6 +10315,13 @@ fn analyzeSwitchBlock( const tag_inst: Zir.Inst.Index = if (has_tag_capture) inst: { const tag_inst = zir_switch.tag_capture_placeholder.unwrap() orelse switch_inst; + if (!tagged_union_originally) { + const tag_capture_src = block.src(.{ .switch_tag_capture = .{ + .switch_node_offset = src_node_offset, + .case_idx = case_idx, + } }); + return sema.failWithInvalidSwitchTagCapture(block, tag_capture_src, operand_ty); + } sema.inst_map.putAssumeCapacity(tag_inst, .fromValue(item_opv)); break :inst tag_inst; } else undefined; @@ -12157,26 +12190,7 @@ fn analyzeSwitchCaptures( .base_node_inst = capture_src.base_node_inst, .offset = .{ .switch_tag_capture = capture_src.offset.switch_capture }, }; - if (operand_ty.zigTypeTag(zcu) == .@"union") { - assert(operand_ty.containerLayout(zcu) == .@"packed"); - return sema.failWithOwnedErrorMsg(case_block, msg: { - const msg = try sema.errMsg(tag_capture_src, "cannot capture tag of packed union", .{}); - errdefer msg.destroy(sema.gpa); - try sema.addDeclaredHereNote(msg, operand_ty); - if (operand_ty.srcLocOrNull(zcu)) |ty_src| { - try sema.errNote(ty_src, msg, "consider using a tagged union", .{}); - } - break :msg msg; - }); - } - return sema.failWithOwnedErrorMsg(case_block, msg: { - const msg = try sema.errMsg(tag_capture_src, "cannot capture tag of non-union type '{f}'", .{ - operand_ty.fmt(pt), - }); - errdefer msg.destroy(sema.gpa); - try sema.addDeclaredHereNote(msg, operand_ty); - break :msg msg; - }); + return sema.failWithInvalidSwitchTagCapture(case_block, tag_capture_src, operand_ty); } return .{ .payload_ref = payload_ref, .tag_ref = .none }; diff --git a/test/cases/compile_errors/switch_invalid_tag_capture.zig b/test/cases/compile_errors/switch_invalid_tag_capture.zig @@ -8,6 +8,12 @@ export fn entry1(p: P) void { else => {}, } } +export fn entry2(p: P) void { + label: switch (p) { + .{ .a = 123 } => |_, tag| _ = tag, + else => continue :label .{ .a = 123 }, + } +} const E = enum(u8) { a, b }; export fn entry3(e: E) void { @@ -16,23 +22,57 @@ export fn entry3(e: E) void { else => {}, } } +export fn entry4(e: E) void { + label: switch (e) { + .a => |_, tag| _ = tag, + else => continue :label .a, + } +} const Error = error{ MyError, MyOtherError }; -export fn entry2(ok: bool) void { +export fn entry5(ok: bool) void { switch (foo(ok)) { error.MyError => |_, tag| _ = tag, else => {}, } } +export fn entry6(ok: bool) void { + label: switch (foo(ok)) { + error.MyError => |_, tag| _ = tag, + else => continue :label error.MyError, + } +} fn foo(ok: bool) Error { return if (ok) error.MyError else error.MyOtherError; } +export fn entry7() void { + switch (@as(u0, 0)) { + 0 => |_, tag| _ = tag, + } +} +export fn entry8() void { + label: switch (@as(u0, 0)) { + 0 => |_, tag| { + _ = tag; + continue :label 0; + }, + } +} + // error // // :7:30: error: cannot capture tag of packed union // :1:18: note: union declared here // :1:18: note: consider using a tagged union -// :15:19: error: cannot capture tag of non-union type 'tmp.E' -// :12:11: note: enum declared here -// :23:30: error: cannot capture tag of non-union type 'error{MyError,MyOtherError}' +// :13:30: error: cannot capture tag of packed union +// :1:18: note: union declared here +// :1:18: note: consider using a tagged union +// :21:19: error: cannot capture tag of non-union type 'tmp.E' +// :18:11: note: enum declared here +// :27:19: error: cannot capture tag of non-union type 'tmp.E' +// :18:11: note: enum declared here +// :35:30: error: cannot capture tag of non-union type 'error{MyError,MyOtherError}' +// :41:30: error: cannot capture tag of non-union type 'error{MyError,MyOtherError}' +// :51:18: error: cannot capture tag of non-union type 'u0' +// :56:18: error: cannot capture tag of non-union type 'u0'