authorgravatar for justus@klausecker.deJustus Klausecker <justus@klausecker.de> 2026-01-09 05:04:58+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-01-11 11:37:17+00:00
logb79bd313566347873e022adcd61891339735d9a6
treeeeebc217c745a964d6f72788cf25df8ad2cbe9e9
parentd94137d23fff3958e003c7d6d70a5d1087a1e98c
signaturelock-open Commit is signed but in an unrecognized format.

Sema: rework `switch_block[_ref/_err_union]` logic

This commit aims to simplify and de-duplicate the logic required for semantically analyzing `switch` expressions. The core logic has been moved to `analyzeSwitchBlock`, `resolveSwitchBlock` and `finishSwitchBlock` and has been rewritten around the new iterator-based API exposed by `Zir.UnwrappedSwitchBlock`. All validation logic and switch prong item resolution have been moved to `validateSwitchBlock`, which produces a `ValidatedSwitchBlock` containing all the necessary information for further analysis. `Zir.UnwrappedSwitchBlock`, `ValidatedSwitchBlock` and `SwitchOperand` replace `SwitchProngAnalysis` while adding more flexibility, mainly for better integration with `switch_block_err_union`. `analyzeSwitchBlock` has an explicit code path for OPV types which lowers them to either a `block`-`br` or a `loop`-`repeat` construct instead of a switch. Backends expect `switch` to actually have an operand that exists at runtime, so this is a bug fix and avoids further special cases in the rest of the switch logic. `resolveSwitchBlock` and `finishSwitchBr` exclusively deal with operands which can have more than one value, at comptime and at runtime respectively. This commit also reworks `RangeSet` to be an unmanaged container and adds `Air.SwitchBr.BranchHints` to offload some complexity from Sema to there and save a few bytes of memory in the process. Additionally, some new features have been implemented: - decl literals and everything else requiring a result type (`@enumFromInt`!) may now be used as switch prong items - union tag captures are now allowed for all prongs, not just `inline` ones - switch prongs may contain errors which are not in the error set being switched on, if these prongs contain `=> comptime unreachable` and some bugs have been fixed: - lots of issues with switching on OPV types are now fixed - the rules around unreachable `else` prongs when switching on errors now apply to *any* switch on an error, not just to `switch_block_err_union`, and are applied properly based on the AST - switching on `void` no longer requires an `else` prong unconditionally - lazy values are properly resolved before any comparisons with prong items - evaluation order between all kinds of switch statements is now the same, with or without label

11 files changed, 3188 insertions(+), 3094 deletions(-)

src/Air.zig+44
......@@ -1339,6 +1339,50 @@ pub const SwitchBr = struct {
13391339 ranges_len: u32,
13401340 body_len: u32,
13411341 };
1342
1343 pub const BranchHints = struct {
1344 bags: std.ArrayList(u32),
1345 count: u32,
1346
1347 const hints_per_bag = 10;
1348 const hint_bits = @bitSizeOf(std.builtin.BranchHint);
1349
1350 pub const empty: BranchHints = .{
1351 .bags = .empty,
1352 .count = 0,
1353 };
1354
1355 pub fn initCapacity(gpa: std.mem.Allocator, num: u32) std.mem.Allocator.Error!BranchHints {
1356 const bags_required = std.math.divCeil(u32, num, hints_per_bag) catch unreachable;
1357 const bags: std.ArrayList(u32) = try .initCapacity(gpa, bags_required);
1358 return .{ .bags = bags, .count = 0 };
1359 }
1360
1361 pub fn ensureUnusedCapacity(hints: *BranchHints, gpa: std.mem.Allocator, additional_count: u32) std.mem.Allocator.Error!void {
1362 const unused_hints = hints.bags.capacity * hints_per_bag - hints.count;
1363 if (unused_hints >= additional_count) return;
1364 const bags_required = std.math.divCeil(u32, hints.count + additional_count, hints_per_bag) catch unreachable;
1365 return hints.bags.ensureUnusedCapacity(gpa, bags_required);
1366 }
1367
1368 pub fn appendAssumeCapacity(hints: *BranchHints, hint: std.builtin.BranchHint) void {
1369 const idx_in_bag = hints.count % hints_per_bag;
1370 var bag: u32 = if (idx_in_bag > 0) hints.bags.pop().? else 0;
1371 bag |= @as(u32, @intFromEnum(hint)) << @intCast(hint_bits * idx_in_bag);
1372 hints.count += 1;
1373 return hints.bags.appendAssumeCapacity(bag);
1374 }
1375
1376 pub fn append(hints: *BranchHints, gpa: std.mem.Allocator, hint: std.builtin.BranchHint) std.mem.Allocator.Error!void {
1377 try hints.ensureUnusedCapacity(gpa, 1);
1378 return hints.appendAssumeCapacity(hint);
1379 }
1380
1381 pub fn deinit(hints: *BranchHints, gpa: std.mem.Allocator) void {
1382 hints.bags.deinit(gpa);
1383 hints.* = undefined;
1384 }
1385 };
13421386};
13431387
13441388/// This data is stored inside extra. Trailing:
src/RangeSet.zig+66-66
......@@ -1,102 +1,92 @@
1const std = @import("std");
2const assert = std.debug.assert;
3const Order = std.math.Order;
4
5const InternPool = @import("InternPool.zig");
6const Type = @import("Type.zig");
7const Value = @import("Value.zig");
8const Zcu = @import("Zcu.zig");
91const RangeSet = @This();
10const LazySrcLoc = Zcu.LazySrcLoc;
112
12zcu: *Zcu,
13ranges: std.array_list.Managed(Range),
3ranges: std.ArrayList(Range),
144
155pub const Range = struct {
16 first: InternPool.Index,
17 last: InternPool.Index,
6 first: Value,
7 last: Value,
188 src: LazySrcLoc,
199};
2010
21pub fn init(allocator: std.mem.Allocator, zcu: *Zcu) RangeSet {
22 return .{
23 .zcu = zcu,
24 .ranges = std.array_list.Managed(Range).init(allocator),
25 };
26}
11pub const empty: RangeSet = .{ .ranges = .empty };
2712
28pub fn deinit(self: *RangeSet) void {
29 self.ranges.deinit();
13pub fn deinit(self: *RangeSet, allocator: Allocator) void {
14 self.ranges.deinit(allocator);
15 self.* = undefined;
3016}
3117
32pub fn add(
33 self: *RangeSet,
34 first: InternPool.Index,
35 last: InternPool.Index,
36 src: LazySrcLoc,
37) !?LazySrcLoc {
38 const zcu = self.zcu;
39 const ip = &zcu.intern_pool;
40
41 const ty = ip.typeOf(first);
42 assert(ty == ip.typeOf(last));
18pub fn ensureUnusedCapacity(self: *RangeSet, allocator: Allocator, additional_count: usize) Allocator.Error!void {
19 return self.ranges.ensureUnusedCapacity(allocator, additional_count);
20}
4321
44 for (self.ranges.items) |range| {
45 assert(ty == ip.typeOf(range.first));
46 assert(ty == ip.typeOf(range.last));
22pub fn addAssumeCapacity(set: *RangeSet, new: Range, ty: Type, zcu: *Zcu) ?LazySrcLoc {
23 assert(new.first.typeOf(zcu).eql(ty, zcu));
24 assert(new.last.typeOf(zcu).eql(ty, zcu));
4725
48 if (Value.fromInterned(last).compareScalar(.gte, Value.fromInterned(range.first), Type.fromInterned(ty), zcu) and
49 Value.fromInterned(first).compareScalar(.lte, Value.fromInterned(range.last), Type.fromInterned(ty), zcu))
26 for (set.ranges.items) |range| {
27 if (new.last.compareScalar(.gte, range.first, ty, zcu) and
28 new.first.compareScalar(.lte, range.last, ty, zcu))
5029 {
5130 return range.src; // They overlap.
5231 }
5332 }
54
55 try self.ranges.append(.{
56 .first = first,
57 .last = last,
58 .src = src,
59 });
33 set.ranges.appendAssumeCapacity(new);
6034 return null;
6135}
6236
63/// Assumes a and b do not overlap
64fn lessThan(zcu: *Zcu, a: Range, b: Range) bool {
65 const ty = Type.fromInterned(zcu.intern_pool.typeOf(a.first));
66 return Value.fromInterned(a.first).compareScalar(.lt, Value.fromInterned(b.first), ty, zcu);
37pub fn add(set: *RangeSet, allocator: Allocator, new: Range, ty: Type, zcu: *Zcu) Allocator.Error!?LazySrcLoc {
38 try set.ensureUnusedCapacity(allocator, 1);
39 return set.addAssumeCapacity(new, ty, zcu);
6740}
6841
69pub fn spans(self: *RangeSet, first: InternPool.Index, last: InternPool.Index) !bool {
70 const zcu = self.zcu;
71 const ip = &zcu.intern_pool;
72 assert(ip.typeOf(first) == ip.typeOf(last));
73
74 if (self.ranges.items.len == 0)
75 return false;
76
77 std.mem.sort(Range, self.ranges.items, zcu, lessThan);
42const SortCtx = struct {
43 ty: Type,
44 zcu: *Zcu,
45};
46/// Assumes a and b do not overlap
47fn lessThan(ctx: SortCtx, a: Range, b: Range) bool {
48 return a.first.compareScalar(.lt, b.first, ctx.ty, ctx.zcu);
49}
7850
79 if (self.ranges.items[0].first != first or
80 self.ranges.items[self.ranges.items.len - 1].last != last)
51pub fn spans(
52 set: *RangeSet,
53 allocator: Allocator,
54 first: Value,
55 last: Value,
56 ty: Type,
57 zcu: *Zcu,
58) Allocator.Error!bool {
59 assert(first.typeOf(zcu).eql(ty, zcu));
60 assert(last.typeOf(zcu).eql(ty, zcu));
61 if (set.ranges.items.len == 0) return false;
62
63 std.mem.sort(Range, set.ranges.items, SortCtx{ .ty = ty, .zcu = zcu }, lessThan);
64
65 if (!set.ranges.items[0].first.eql(first, ty, zcu) or
66 !set.ranges.items[set.ranges.items.len - 1].last.eql(last, ty, zcu))
8167 {
8268 return false;
8369 }
8470
85 var space: InternPool.Key.Int.Storage.BigIntSpace = undefined;
71 const limbs = try allocator.alloc(
72 std.math.big.Limb,
73 std.math.big.int.calcTwosCompLimbCount(ty.intInfo(zcu).bits),
74 );
75 defer allocator.free(limbs);
76 var counter: std.math.big.int.Mutable = .init(limbs, 0);
8677
87 var counter = try std.math.big.int.Managed.init(self.ranges.allocator);
88 defer counter.deinit();
78 var space: InternPool.Key.Int.Storage.BigIntSpace = undefined;
8979
9080 // look for gaps
91 for (self.ranges.items[1..], 0..) |cur, i| {
81 for (set.ranges.items[1..], 0..) |cur, i| {
9282 // i starts counting from the second item.
93 const prev = self.ranges.items[i];
83 const prev = set.ranges.items[i];
9484
9585 // prev.last + 1 == cur.first
96 try counter.copy(Value.fromInterned(prev.last).toBigInt(&space, zcu));
97 try counter.addScalar(&counter, 1);
86 counter.copy(prev.last.toBigInt(&space, zcu));
87 counter.addScalar(counter.toConst(), 1);
9888
99 const cur_start_int = Value.fromInterned(cur.first).toBigInt(&space, zcu);
89 const cur_start_int = cur.first.toBigInt(&space, zcu);
10090 if (!cur_start_int.eql(counter.toConst())) {
10191 return false;
10292 }
......@@ -104,3 +94,13 @@ pub fn spans(self: *RangeSet, first: InternPool.Index, last: InternPool.Index) !
10494
10595 return true;
10696}
97
98const std = @import("std");
99const assert = std.debug.assert;
100const Allocator = std.mem.Allocator;
101
102const InternPool = @import("InternPool.zig");
103const Type = @import("Type.zig");
104const Value = @import("Value.zig");
105const Zcu = @import("Zcu.zig");
106const LazySrcLoc = Zcu.LazySrcLoc;
src/Sema.zig+2491-2950
......@@ -509,7 +509,7 @@ pub const Block = struct {
509509 .parent = parent,
510510 .sema = parent.sema,
511511 .namespace = parent.namespace,
512 .instructions = .{},
512 .instructions = .empty,
513513 .label = null,
514514 .inlining = parent.inlining,
515515 .comptime_reason = parent.comptime_reason,
......@@ -6496,26 +6496,23 @@ fn zirSwitchContinue(sema: *Sema, start_block: *Block, inst: Zir.Inst.Index) Com
64966496
64976497 switch (sema.code.instructions.items(.tag)[@intFromEnum(switch_inst)]) {
64986498 .switch_block, .switch_block_ref => {},
6499 .switch_block_err_union => unreachable, // wrong code path!
64996500 else => unreachable, // assertion failure
65006501 }
65016502
6502 const switch_payload_index = sema.code.instructions.items(.data)[@intFromEnum(switch_inst)].pl_node.payload_index;
6503 const switch_operand_ref = sema.code.extraData(Zir.Inst.SwitchBlock, switch_payload_index).data.operand;
6504 const switch_operand_ty = sema.typeOf(try sema.resolveInst(switch_operand_ref));
6505
6506 const operand = try sema.coerce(start_block, switch_operand_ty, uncoerced_operand, operand_src);
6507
6503 const operand_ty = (try sema.resolveInst(switch_inst.toRef())).toType();
6504 const operand = try sema.coerce(start_block, operand_ty, uncoerced_operand, operand_src);
65086505 try sema.validateRuntimeValue(start_block, operand_src, operand);
65096506
65106507 // We want to generate a `switch_dispatch` instruction with the switch condition,
65116508 // possibly preceded by a store to the stack alloc containing the raw operand.
65126509 // However, to avoid too much special-case state in Sema, this is handled by the
6513 // `switch` lowering logic. As such, we will find the `Block` corresponding to the
6514 // parent `switch_block[_ref]` instruction, create a dummy `br`, and add a merge
6515 // to signal to the switch logic to rewrite this into an appropriate dispatch.
6510 // `switch` lowering logic. As such, we will find the `Block` corresponding to
6511 // the parent `switch_block[_ref]` instruction, create a dummy `br`, and add a
6512 // merge to signal to the switch logic to rewrite this into an appropriate dispatch.
65166513
65176514 var block = start_block;
6518 while (true) {
6515 while (true) : (block = block.parent.?) {
65196516 if (block.label) |label| {
65206517 if (label.zir_block == switch_inst) {
65216518 const br_ref = try start_block.addBr(label.merges.block_inst, operand);
......@@ -6529,7 +6526,6 @@ fn zirSwitchContinue(sema: *Sema, start_block: *Block, inst: Zir.Inst.Index) Com
65296526 return;
65306527 }
65316528 }
6532 block = block.parent.?;
65336529 }
65346530}
65356531
......@@ -8483,8 +8479,20 @@ fn zirDeclLiteral(sema: *Sema, block: *Block, inst: Zir.Inst.Index, do_coerce: b
84838479 sema.code.nullTerminatedString(extra.field_name_start),
84848480 .no_embedded_nulls,
84858481 );
8486
84878482 const orig_ty: Type = try sema.resolveTypeOrPoison(block, src, extra.lhs) orelse .generic_poison;
8483 return sema.analyzeDeclLiteral(block, src, name, orig_ty, do_coerce);
8484}
8485
8486fn analyzeDeclLiteral(
8487 sema: *Sema,
8488 block: *Block,
8489 src: LazySrcLoc,
8490 name: InternPool.NullTerminatedString,
8491 orig_ty: Type,
8492 do_coerce: bool,
8493) CompileError!Air.Inst.Ref {
8494 const pt = sema.pt;
8495 const zcu = pt.zcu;
84888496
84898497 const uncoerced_result = res: {
84908498 if (orig_ty.toIntern() == .generic_poison_type) {
......@@ -10518,1284 +10526,1572 @@ fn zirSliceSentinelTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
1051810526 return Air.internedToRef(sentinel_ty.toIntern());
1051910527}
1052010528
10521/// Holds common data used when analyzing or resolving switch prong bodies,
10522/// including setting up captures.
10523const SwitchProngAnalysis = struct {
10524 sema: *Sema,
10525 /// The block containing the `switch_block` itself.
10526 parent_block: *Block,
10527 operand: Operand,
10528 /// If this switch is on an error set, this is the type to assign to the
10529 /// `else` prong. If `null`, the prong should be unreachable.
10530 else_error_ty: ?Type,
10531 /// The index of the `switch_block` instruction itself.
10532 switch_block_inst: Zir.Inst.Index,
10533 /// The dummy index into which inline tag captures should be placed. May be
10534 /// undefined if no prong has a tag capture.
10535 tag_capture_inst: Zir.Inst.Index,
10536
10537 const Operand = union(enum) {
10538 /// This switch will be dispatched only once, with the given operand.
10539 simple: struct {
10540 /// The raw switch operand value. Always defined.
10541 by_val: Air.Inst.Ref,
10542 /// The switch operand *pointer*. Defined only if there is a prong
10543 /// with a by-ref capture.
10544 by_ref: Air.Inst.Ref,
10545 /// The switch condition value. For unions, `operand` is the union
10546 /// and `cond` is its enum tag value.
10547 cond: Air.Inst.Ref,
10548 },
10549 /// This switch may be dispatched multiple times with `continue` syntax.
10550 /// As such, the operand is stored in an alloc if needed.
10551 loop: struct {
10552 /// The `alloc` containing the `switch` operand for the active dispatch.
10553 /// Each prong must load from this `alloc` to get captures.
10554 /// If there are no captures, this may be undefined.
10555 operand_alloc: Air.Inst.Ref,
10556 /// Whether `operand_alloc` contains a by-val operand or a by-ref
10557 /// operand.
10558 operand_is_ref: bool,
10559 /// The switch condition value for the *initial* dispatch. For
10560 /// unions, this is the enum tag value.
10561 init_cond: Air.Inst.Ref,
10562 },
10563 };
10564
10565 /// Resolve a switch prong which is determined at comptime to have no peers.
10566 /// Uses `resolveBlockBody`. Sets up captures as needed.
10567 fn resolveProngComptime(
10568 spa: SwitchProngAnalysis,
10569 child_block: *Block,
10570 prong_type: enum { normal, special },
10571 prong_body: []const Zir.Inst.Index,
10572 capture: Zir.Inst.SwitchBlock.ProngInfo.Capture,
10573 /// Must use the `switch_capture` field in `offset`.
10574 capture_src: LazySrcLoc,
10575 /// The set of all values which can reach this prong. May be undefined
10576 /// if the prong is special or contains ranges.
10577 case_vals: []const Air.Inst.Ref,
10578 /// The inline capture of this prong. If this is not an inline prong,
10579 /// this is `.none`.
10580 inline_case_capture: Air.Inst.Ref,
10581 /// Whether this prong has an inline tag capture. If `true`, then
10582 /// `inline_case_capture` cannot be `.none`.
10583 has_tag_capture: bool,
10584 merges: *Block.Merges,
10585 ) CompileError!Air.Inst.Ref {
10586 const sema = spa.sema;
10587 const src = spa.parent_block.nodeOffset(
10588 sema.code.instructions.items(.data)[@intFromEnum(spa.switch_block_inst)].pl_node.src_node,
10589 );
10529fn zirSwitchBlockErrUnion(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
10530 const tracy = trace(@src());
10531 defer tracy.end();
1059010532
10591 // We can propagate `.cold` hints from this branch since it's comptime-known
10592 // to be taken from the parent branch.
10593 const parent_hint = sema.branch_hint;
10594 defer sema.branch_hint = parent_hint orelse if (sema.branch_hint == .cold) .cold else null;
10533 const pt = sema.pt;
10534 const zcu = pt.zcu;
10535 const gpa = sema.gpa;
1059510536
10596 if (has_tag_capture) {
10597 const tag_ref = try spa.analyzeTagCapture(child_block, capture_src, inline_case_capture);
10598 sema.inst_map.putAssumeCapacity(spa.tag_capture_inst, tag_ref);
10599 }
10600 defer if (has_tag_capture) assert(sema.inst_map.remove(spa.tag_capture_inst));
10537 const zir_switch = sema.code.getSwitchBlock(inst);
10538 const src_node_offset = zir_switch.catch_or_if_src_node_offset.unwrap().?;
10539 const src = block.src(.{ .node_offset_main_token = src_node_offset });
10540 const operand_src = block.src(.{ .node_offset_if_cond = src_node_offset });
1060110541
10602 switch (capture) {
10603 .none => {
10604 return sema.resolveBlockBody(spa.parent_block, src, child_block, prong_body, spa.switch_block_inst, merges);
10605 },
10542 assert(!zir_switch.has_continue); // wrong codepath!
1060610543
10607 .by_val, .by_ref => {
10608 const capture_ref = try spa.analyzeCapture(
10609 child_block,
10610 capture == .by_ref,
10611 prong_type == .special,
10612 capture_src,
10613 case_vals,
10614 inline_case_capture,
10615 );
10544 const block_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len);
10545 try sema.air_instructions.append(gpa, .{
10546 .tag = .block,
10547 .data = undefined,
10548 });
10549 var label: Block.Label = .{
10550 .zir_block = inst,
10551 .merges = .{
10552 .src_locs = .{},
10553 .results = .{},
10554 .br_list = .{},
10555 .block_inst = block_inst,
10556 },
10557 };
10558 var child_block = block.makeSubBlock();
10559 child_block.label = &label;
10560 const merges = &child_block.label.?.merges;
10561 defer child_block.instructions.deinit(gpa);
10562 defer merges.deinit(gpa);
1061610563
10617 if (sema.typeOf(capture_ref).isNoReturn(sema.pt.zcu)) {
10618 // This prong should be unreachable!
10619 return .unreachable_value;
10620 }
10564 const non_err_case = zir_switch.non_err_case.?;
10565
10566 var non_err_block: Block = child_block.makeSubBlock();
10567 non_err_block.runtime_loop = null;
10568 non_err_block.runtime_cond = operand_src;
10569 non_err_block.runtime_index.increment();
10570 non_err_block.need_debug_scope = null;
10571 defer non_err_block.instructions.deinit(gpa);
10572
10573 var switch_block: Block = child_block.makeSubBlock();
10574 switch_block.runtime_loop = null;
10575 switch_block.runtime_cond = operand_src;
10576 switch_block.runtime_index.increment();
10577 switch_block.need_debug_scope = null;
10578 defer switch_block.instructions.deinit(gpa);
10579
10580 // We begin with unwrapping the error union we're switching on as necessary.
10581 // Then we analyze the non-error prong if it's not comptime-unreachable.
10582 // Lastly, we analyze the error prong(s) as a regular switch.
10583
10584 const raw_switch_operand, const non_err_reachable, const non_err_cond, const non_err_hint, const err_set_empty = non_err: {
10585 const eu_maybe_ptr = try sema.resolveInst(zir_switch.main_operand);
10586 const err_union_ty: Type = err_union_ty: {
10587 const raw_operand_ty = sema.typeOf(eu_maybe_ptr);
10588 if (!non_err_case.operand_is_ref) break :err_union_ty raw_operand_ty;
10589 try sema.checkPtrOperand(block, operand_src, raw_operand_ty);
10590 break :err_union_ty raw_operand_ty.childType(zcu);
10591 };
10592 if (err_union_ty.zigTypeTag(zcu) != .error_union) {
10593 return sema.fail(block, src, "expected error union type, found '{f}'", .{
10594 err_union_ty.fmt(pt),
10595 });
10596 }
1062110597
10622 sema.inst_map.putAssumeCapacity(spa.switch_block_inst, capture_ref);
10623 defer assert(sema.inst_map.remove(spa.switch_block_inst));
10598 const non_err_cond = if (non_err_case.operand_is_ref)
10599 try sema.analyzePtrIsNonErr(block, operand_src, eu_maybe_ptr)
10600 else
10601 try sema.analyzeIsNonErr(block, operand_src, eu_maybe_ptr);
1062410602
10625 return sema.resolveBlockBody(spa.parent_block, src, child_block, prong_body, spa.switch_block_inst, merges);
10626 },
10627 }
10628 }
10603 const is_non_err = try sema.resolveDefinedValue(block, operand_src, non_err_cond);
10604 const non_err_reachable = if (is_non_err) |val| val.toBool() else true;
1062910605
10630 /// Analyze a switch prong which may have peers at runtime.
10631 /// Uses `analyzeBodyRuntimeBreak`. Sets up captures as needed.
10632 /// Returns the `BranchHint` for the prong.
10633 fn analyzeProngRuntime(
10634 spa: SwitchProngAnalysis,
10635 case_block: *Block,
10636 prong_type: enum { normal, special },
10637 prong_body: []const Zir.Inst.Index,
10638 capture: Zir.Inst.SwitchBlock.ProngInfo.Capture,
10639 /// Must use the `switch_capture` field in `offset`.
10640 capture_src: LazySrcLoc,
10641 /// The set of all values which can reach this prong. May be undefined
10642 /// if the prong is special or contains ranges.
10643 case_vals: []const Air.Inst.Ref,
10644 /// The inline capture of this prong. If this is not an inline prong,
10645 /// this is `.none`.
10646 inline_case_capture: Air.Inst.Ref,
10647 /// Whether this prong has an inline tag capture. If `true`, then
10648 /// `inline_case_capture` cannot be `.none`.
10649 has_tag_capture: bool,
10650 ) CompileError!std.builtin.BranchHint {
10651 const sema = spa.sema;
10606 const err_set_empty = err_set_empty: {
10607 const err_set_ty = err_union_ty.errorUnionSet(zcu);
10608 break :err_set_empty err_set_ty.errorSetIsEmpty(zcu);
10609 };
1065210610
10653 if (has_tag_capture) {
10654 const tag_ref = try spa.analyzeTagCapture(case_block, capture_src, inline_case_capture);
10655 sema.inst_map.putAssumeCapacity(spa.tag_capture_inst, tag_ref);
10656 }
10657 defer if (has_tag_capture) assert(sema.inst_map.remove(spa.tag_capture_inst));
10611 const non_err_hint: std.builtin.BranchHint = hint: {
10612 // don't analyze the non-error body if it's unreachable
10613 if (!non_err_reachable) {
10614 break :hint undefined;
10615 }
1065810616
10659 switch (capture) {
10660 .none => {
10661 return sema.analyzeBodyRuntimeBreak(case_block, prong_body);
10662 },
10617 const eu_payload: Air.Inst.Ref = switch (non_err_case.capture) {
10618 .by_val => try sema.analyzeErrUnionPayload(&non_err_block, src, err_union_ty, eu_maybe_ptr, operand_src, false),
10619 .by_ref => try sema.analyzeErrUnionPayloadPtr(&non_err_block, src, eu_maybe_ptr, false, false),
10620 .none => undefined,
10621 };
10622 if (non_err_case.capture != .none) sema.inst_map.putAssumeCapacity(inst, eu_payload);
10623 defer if (non_err_case.capture != .none) assert(sema.inst_map.remove(inst));
1066310624
10664 .by_val, .by_ref => {
10665 const capture_ref = try spa.analyzeCapture(
10666 case_block,
10667 capture == .by_ref,
10668 prong_type == .special,
10669 capture_src,
10670 case_vals,
10671 inline_case_capture,
10672 );
10625 const always_non_err = if (is_non_err) |val| val.toBool() else err_set_empty;
10626 if (always_non_err) {
10627 // Early return; we don't analyze the switch as it's unreachable.
10628 return sema.resolveBlockBody(block, src, &non_err_block, non_err_case.body, inst, merges);
10629 }
10630 break :hint try sema.analyzeBodyRuntimeBreak(&non_err_block, non_err_case.body);
10631 };
1067310632
10674 if (sema.typeOf(capture_ref).isNoReturn(sema.pt.zcu)) {
10675 // No need to analyze any further, the prong is unreachable
10676 return .none;
10677 }
10633 // Emit this into the switch block as it's our error case!
10634 const eu_code = if (non_err_case.operand_is_ref)
10635 try sema.analyzeErrUnionCodePtr(&switch_block, operand_src, eu_maybe_ptr)
10636 else
10637 try sema.analyzeErrUnionCode(&switch_block, operand_src, eu_maybe_ptr);
1067810638
10679 sema.inst_map.putAssumeCapacity(spa.switch_block_inst, capture_ref);
10680 defer assert(sema.inst_map.remove(spa.switch_block_inst));
10639 break :non_err .{ eu_code, non_err_reachable, non_err_cond, non_err_hint, err_set_empty };
10640 };
1068110641
10682 return sema.analyzeBodyRuntimeBreak(case_block, prong_body);
10683 },
10684 }
10685 }
10642 const validated_switch = try sema.validateSwitchBlock(block, raw_switch_operand, false, inst, &zir_switch);
1068610643
10687 fn analyzeTagCapture(
10688 spa: SwitchProngAnalysis,
10689 block: *Block,
10690 capture_src: LazySrcLoc,
10691 inline_case_capture: Air.Inst.Ref,
10692 ) CompileError!Air.Inst.Ref {
10693 const sema = spa.sema;
10694 const pt = sema.pt;
10695 const zcu = pt.zcu;
10696 const operand_ty = switch (spa.operand) {
10697 .simple => |s| sema.typeOf(s.by_val),
10698 .loop => |l| ty: {
10699 const alloc_ty = sema.typeOf(l.operand_alloc);
10700 const alloc_child = alloc_ty.childType(zcu);
10701 if (l.operand_is_ref) break :ty alloc_child.childType(zcu);
10702 break :ty alloc_child;
10703 },
10644 const maybe_switch_ref: ?Air.Inst.Ref = ref: {
10645 if (err_set_empty) break :ref .unreachable_value;
10646 // make err capture (i.e. switch operand) available to switch prong bodies
10647 sema.inst_map.putAssumeCapacityNoClobber(inst, raw_switch_operand);
10648 defer assert(sema.inst_map.remove(inst));
10649 break :ref try sema.analyzeSwitchBlock(block, &switch_block, raw_switch_operand, false, merges, inst, &zir_switch, &validated_switch);
10650 };
10651
10652 if (!non_err_reachable) {
10653 return maybe_switch_ref orelse {
10654 const switch_src = block.nodeOffset(zir_switch.switch_src_node_offset);
10655 return sema.resolveAnalyzedBlock(block, switch_src, &switch_block, merges, false);
1070410656 };
10705 if (operand_ty.zigTypeTag(zcu) != .@"union") {
10706 const tag_capture_src: LazySrcLoc = .{
10707 .base_node_inst = capture_src.base_node_inst,
10708 .offset = .{ .switch_tag_capture = capture_src.offset.switch_capture },
10709 };
10710 return sema.fail(block, tag_capture_src, "cannot capture tag of non-union type '{f}'", .{
10711 operand_ty.fmt(pt),
10712 });
10713 }
10714 assert(inline_case_capture != .none);
10715 return inline_case_capture;
1071610657 }
1071710658
10718 fn analyzeCapture(
10719 spa: SwitchProngAnalysis,
10720 block: *Block,
10721 capture_byref: bool,
10722 is_special_prong: bool,
10723 capture_src: LazySrcLoc,
10724 case_vals: []const Air.Inst.Ref,
10725 inline_case_capture: Air.Inst.Ref,
10726 ) CompileError!Air.Inst.Ref {
10727 const sema = spa.sema;
10728 const pt = sema.pt;
10729 const zcu = pt.zcu;
10730 const ip = &zcu.intern_pool;
10659 if (maybe_switch_ref) |switch_ref| {
10660 if (sema.typeOf(switch_ref).isNoReturn(zcu)) {
10661 _ = try switch_block.addNoOp(.unreach);
10662 } else {
10663 const br_ref = try switch_block.addBr(merges.block_inst, switch_ref);
10664 try merges.results.append(gpa, switch_ref);
10665 try merges.br_list.append(gpa, br_ref.toIndex().?);
10666 try merges.src_locs.append(gpa, null);
10667 }
10668 }
1073110669
10732 const zir_datas = sema.code.instructions.items(.data);
10733 const switch_node_offset = zir_datas[@intFromEnum(spa.switch_block_inst)].pl_node.src_node;
10670 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).@"struct".fields.len +
10671 non_err_block.instructions.items.len + switch_block.instructions.items.len);
10672 const cond_br_payload = sema.addExtraAssumeCapacity(Air.CondBr{
10673 .then_body_len = @intCast(non_err_block.instructions.items.len),
10674 .else_body_len = @intCast(switch_block.instructions.items.len),
10675 .branch_hints = .{
10676 .true = non_err_hint,
10677 .false = .unlikely, // errors are unlikely
10678 // Code coverage is desired for error handling.
10679 .then_cov = .poi,
10680 .else_cov = .poi,
10681 },
10682 });
10683 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(non_err_block.instructions.items));
10684 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(switch_block.instructions.items));
1073410685
10735 const operand_src = block.src(.{ .node_offset_switch_operand = switch_node_offset });
10686 _ = try child_block.addInst(.{ .tag = .cond_br, .data = .{ .pl_op = .{
10687 .operand = non_err_cond,
10688 .payload = cond_br_payload,
10689 } } });
1073610690
10737 const operand_val, const operand_ptr = switch (spa.operand) {
10738 .simple => |s| .{ s.by_val, s.by_ref },
10739 .loop => |l| op: {
10740 const loaded = try sema.analyzeLoad(block, operand_src, l.operand_alloc, operand_src);
10741 if (l.operand_is_ref) {
10742 const by_val = try sema.analyzeLoad(block, operand_src, loaded, operand_src);
10743 break :op .{ by_val, loaded };
10744 } else {
10745 break :op .{ loaded, undefined };
10746 }
10747 },
10748 };
10691 return sema.resolveAnalyzedBlock(block, src, &child_block, merges, false);
10692}
1074910693
10750 const operand_ty = sema.typeOf(operand_val);
10751 const operand_ptr_ty = if (capture_byref) sema.typeOf(operand_ptr) else undefined;
10694fn zirSwitchBlock(
10695 sema: *Sema,
10696 block: *Block,
10697 inst: Zir.Inst.Index,
10698 operand_is_ref: bool,
10699) CompileError!Air.Inst.Ref {
10700 const tracy = trace(@src());
10701 defer tracy.end();
10702 const zir_switch = sema.code.getSwitchBlock(inst);
1075210703
10753 if (inline_case_capture != .none) {
10754 const item_val = sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, inline_case_capture, undefined) catch unreachable;
10755 if (operand_ty.zigTypeTag(zcu) == .@"union") {
10756 const field_index: u32 = @intCast(operand_ty.unionTagFieldIndex(item_val, zcu).?);
10757 const union_obj = zcu.typeToUnion(operand_ty).?;
10758 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]);
10759 if (capture_byref) {
10760 const ptr_field_ty = try pt.ptrTypeSema(.{
10761 .child = field_ty.toIntern(),
10762 .flags = .{
10763 .is_const = !operand_ptr_ty.ptrIsMutable(zcu),
10764 .is_volatile = operand_ptr_ty.isVolatilePtr(zcu),
10765 .address_space = operand_ptr_ty.ptrAddressSpace(zcu),
10766 },
10767 });
10768 if (try sema.resolveDefinedValue(block, operand_src, operand_ptr)) |union_ptr| {
10769 return Air.internedToRef((try union_ptr.ptrField(field_index, pt)).toIntern());
10770 }
10771 return block.addStructFieldPtr(operand_ptr, field_index, ptr_field_ty);
10772 } else {
10773 if (try sema.resolveDefinedValue(block, operand_src, operand_val)) |union_val| {
10774 const tag_and_val = ip.indexToKey(union_val.toIntern()).un;
10775 return Air.internedToRef(tag_and_val.val);
10776 }
10777 return block.addStructFieldVal(operand_val, field_index, field_ty);
10778 }
10779 } else if (capture_byref) {
10780 return sema.uavRef(item_val.toIntern());
10781 } else {
10782 return inline_case_capture;
10783 }
10784 }
10704 const block_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len);
10705 try sema.air_instructions.append(sema.gpa, .{
10706 .tag = .block,
10707 .data = undefined,
10708 });
10709 var label: Block.Label = .{
10710 .zir_block = inst,
10711 .merges = .{
10712 .src_locs = .{},
10713 .results = .{},
10714 .br_list = .{},
10715 .block_inst = block_inst,
10716 },
10717 };
10718 var child_block = block.makeSubBlock();
10719 child_block.label = &label;
10720 const merges = &child_block.label.?.merges;
10721 defer child_block.instructions.deinit(sema.gpa);
10722 defer merges.deinit(sema.gpa);
1078510723
10786 if (is_special_prong) {
10787 if (capture_byref) {
10788 return operand_ptr;
10789 }
10724 const raw_operand = try sema.resolveInst(zir_switch.main_operand);
10725 const validated_switch = try sema.validateSwitchBlock(block, raw_operand, operand_is_ref, inst, &zir_switch);
10726 const maybe_ref = try sema.analyzeSwitchBlock(block, &child_block, raw_operand, operand_is_ref, merges, inst, &zir_switch, &validated_switch);
10727 return maybe_ref orelse {
10728 const src = block.nodeOffset(zir_switch.switch_src_node_offset);
10729 return sema.resolveAnalyzedBlock(block, src, &child_block, merges, false);
10730 };
10731}
1079010732
10791 switch (operand_ty.zigTypeTag(zcu)) {
10792 .error_set => if (spa.else_error_ty) |ty| {
10793 return sema.bitCast(block, ty, operand_val, operand_src, null);
10794 } else {
10795 try sema.analyzeUnreachable(block, operand_src, false);
10796 return .unreachable_value;
10797 },
10798 else => return operand_val,
10799 }
10800 }
10733/// If the switch can be resolved to a value at comptime, this will return a `Ref`
10734/// that's never `.none`.
10735/// If not, this will return `null` and emit its instructions into `child_block`.
10736fn analyzeSwitchBlock(
10737 sema: *Sema,
10738 block: *Block,
10739 child_block: *Block,
10740 raw_operand: Air.Inst.Ref,
10741 operand_is_ref: bool,
10742 merges: *Block.Merges,
10743 switch_inst: Zir.Inst.Index,
10744 zir_switch: *const Zir.UnwrappedSwitchBlock,
10745 validated_switch: *const ValidatedSwitchBlock,
10746) CompileError!?Air.Inst.Ref {
10747 const pt = sema.pt;
10748 const zcu = pt.zcu;
10749 const gpa = sema.gpa;
1080110750
10802 switch (operand_ty.zigTypeTag(zcu)) {
10803 .@"union" => {
10804 const union_obj = zcu.typeToUnion(operand_ty).?;
10805 const first_item_val = sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, case_vals[0], undefined) catch unreachable;
10751 const src_node_offset = zir_switch.switch_src_node_offset;
10752 const src = block.nodeOffset(src_node_offset);
10753 const operand_src = block.src(.{ .node_offset_switch_operand = src_node_offset });
1080610754
10807 const first_field_index: u32 = zcu.unionTagFieldIndex(union_obj, first_item_val).?;
10808 const first_field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[first_field_index]);
10755 const has_else = zir_switch.else_case != null;
10756 const has_under = zir_switch.under_case != .none;
1080910757
10810 const field_indices = try sema.arena.alloc(u32, case_vals.len);
10811 for (case_vals, field_indices) |item, *field_idx| {
10812 const item_val = sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, item, undefined) catch unreachable;
10813 field_idx.* = zcu.unionTagFieldIndex(union_obj, item_val).?;
10814 }
10758 const else_case = validated_switch.else_case;
10759 const under_case = validated_switch.under_case;
1081510760
10816 // Fast path: if all the operands are the same type already, we don't need to hit
10817 // PTR! This will also allow us to emit simpler code.
10818 const same_types = for (field_indices[1..]) |field_idx| {
10819 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]);
10820 if (!field_ty.eql(first_field_ty, zcu)) break false;
10821 } else true;
10761 const operand: SwitchOperand, const operand_ty: Type, const maybe_operand_opv: ?Value, const item_ty: Type = operand: {
10762 const val, const ref = if (operand_is_ref)
10763 .{ try sema.analyzeLoad(block, src, raw_operand, operand_src), raw_operand }
10764 else
10765 .{ raw_operand, undefined };
1082210766
10823 const capture_ty = if (same_types) first_field_ty else capture_ty: {
10824 // We need values to run PTR on, so make a bunch of undef constants.
10825 const dummy_captures = try sema.arena.alloc(Air.Inst.Ref, case_vals.len);
10826 for (dummy_captures, field_indices) |*dummy, field_idx| {
10827 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]);
10828 dummy.* = try pt.undefRef(field_ty);
10829 }
10767 const operand_ty = sema.typeOf(val);
10768 const maybe_operand_opv = try sema.typeHasOnePossibleValue(operand_ty);
10769 const init_cond: Air.Inst.Ref, const item_ty: Type = switch (operand_ty.zigTypeTag(zcu)) {
10770 .@"union" => tag: {
10771 const tag_ty = operand_ty.unionTagType(zcu).?;
10772 const tag_val = try sema.unionToTag(block, tag_ty, val, operand_src);
10773 break :tag .{ tag_val, tag_ty };
10774 },
10775 else => .{
10776 if (maybe_operand_opv) |operand_opv| .fromValue(operand_opv) else val,
10777 operand_ty,
10778 },
10779 };
1083010780
10831 const case_srcs = try sema.arena.alloc(?LazySrcLoc, case_vals.len);
10832 for (case_srcs, 0..) |*case_src, i| {
10833 case_src.* = .{
10834 .base_node_inst = capture_src.base_node_inst,
10835 .offset = .{ .switch_case_item = .{
10836 .switch_node_offset = switch_node_offset,
10837 .case_idx = capture_src.offset.switch_capture.case_idx,
10838 .item_idx = .{ .kind = .single, .index = @intCast(i) },
10839 } },
10840 };
10841 }
10781 if (zir_switch.has_continue and !block.isComptime()) {
10782 const operand_alloc: Air.Inst.Ref = if (zir_switch.any_maybe_runtime_capture and
10783 maybe_operand_opv == null)
10784 alloc: {
10785 const operand_ptr_ty = try pt.singleMutPtrType(sema.typeOf(raw_operand));
10786 const operand_alloc = try block.addTy(.alloc, operand_ptr_ty);
10787 _ = try block.addBinOp(.store, operand_alloc, raw_operand);
10788 break :alloc operand_alloc;
10789 } else undefined;
10790 break :operand .{ .{ .loop = .{
10791 .operand_alloc = operand_alloc,
10792 .operand_is_ref = operand_is_ref,
10793 .init_cond = init_cond,
10794 } }, operand_ty, maybe_operand_opv, item_ty };
10795 } else {
10796 // We always use `simple` in the comptime/OPV case, because as far as the
10797 // dispatching logic is concerned, it really is dispatching a single prong.
10798 break :operand .{ .{ .simple = .{
10799 .by_val = val,
10800 .by_ref = ref,
10801 .cond = init_cond,
10802 } }, operand_ty, maybe_operand_opv, item_ty };
10803 }
10804 };
1084210805
10843 break :capture_ty sema.resolvePeerTypes(block, capture_src, dummy_captures, .{ .override = case_srcs }) catch |err| switch (err) {
10844 error.AnalysisFail => {
10845 const msg = sema.err orelse return error.AnalysisFail;
10846 try sema.reparentOwnedErrorMsg(capture_src, msg, "capture group with incompatible types", .{});
10847 return error.AnalysisFail;
10848 },
10849 else => |e| return e,
10850 };
10851 };
10806 const raw_operand_ty = sema.typeOf(raw_operand);
1085210807
10853 // By-reference captures have some further restrictions which make them easier to emit
10854 if (capture_byref) {
10855 const operand_ptr_info = operand_ptr_ty.ptrInfo(zcu);
10856 const capture_ptr_ty = resolve: {
10857 // By-ref captures of hetereogeneous types are only allowed if all field
10858 // pointer types are peer resolvable to each other.
10859 // We need values to run PTR on, so make a bunch of undef constants.
10860 const dummy_captures = try sema.arena.alloc(Air.Inst.Ref, case_vals.len);
10861 for (field_indices, dummy_captures) |field_idx, *dummy| {
10862 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]);
10863 const field_ptr_ty = try pt.ptrTypeSema(.{
10864 .child = field_ty.toIntern(),
10865 .flags = .{
10866 .is_const = operand_ptr_info.flags.is_const,
10867 .is_volatile = operand_ptr_info.flags.is_volatile,
10868 .address_space = operand_ptr_info.flags.address_space,
10869 .alignment = union_obj.fieldAlign(ip, field_idx),
10870 },
10871 });
10872 dummy.* = try pt.undefRef(field_ptr_ty);
10873 }
10874 const case_srcs = try sema.arena.alloc(?LazySrcLoc, case_vals.len);
10875 for (case_srcs, 0..) |*case_src, i| {
10876 case_src.* = .{
10877 .base_node_inst = capture_src.base_node_inst,
10878 .offset = .{ .switch_case_item = .{
10879 .switch_node_offset = switch_node_offset,
10880 .case_idx = capture_src.offset.switch_capture.case_idx,
10881 .item_idx = .{ .kind = .single, .index = @intCast(i) },
10882 } },
10883 };
10884 }
10808 const union_originally = operand_ty.zigTypeTag(zcu) == .@"union";
10809 const err_set = operand_ty.zigTypeTag(zcu) == .error_set;
1088510810
10886 break :resolve sema.resolvePeerTypes(block, capture_src, dummy_captures, .{ .override = case_srcs }) catch |err| switch (err) {
10887 error.AnalysisFail => {
10888 const msg = sema.err orelse return error.AnalysisFail;
10889 try sema.errNote(capture_src, msg, "this coercion is only possible when capturing by value", .{});
10890 try sema.reparentOwnedErrorMsg(capture_src, msg, "capture group with incompatible types", .{});
10891 return error.AnalysisFail;
10892 },
10893 else => |e| return e,
10894 };
10895 };
10811 if (item_ty.zigTypeTag(zcu) == .@"enum" and
10812 validated_switch.seen_enum_fields.len == 0 and
10813 !operand_ty.isNonexhaustiveEnum(zcu))
10814 {
10815 return .void_value; // switch on empty enum/union
10816 }
1089610817
10897 if (try sema.resolveDefinedValue(block, operand_src, operand_ptr)) |op_ptr_val| {
10898 if (op_ptr_val.isUndef(zcu)) return pt.undefRef(capture_ptr_ty);
10899 const field_ptr_val = try op_ptr_val.ptrField(first_field_index, pt);
10900 return Air.internedToRef((try pt.getCoerced(field_ptr_val, capture_ptr_ty)).toIntern());
10901 }
10818 const cond_ref = switch (operand) {
10819 .simple => |s| s.cond,
10820 .loop => |l| l.init_cond,
10821 };
1090210822
10903 try sema.requireRuntimeBlock(block, operand_src, null);
10904 return block.addStructFieldPtr(operand_ptr, first_field_index, capture_ptr_ty);
10905 }
10823 // We treat `else` and `_` the same, except if both are present.
10824 const else_is_named_only = has_else and has_under;
10825 const catch_all_case: CatchAllSwitchCase =
10826 if (has_under) .under else if (has_else) .@"else" else .none;
1090610827
10907 if (try sema.resolveDefinedValue(block, operand_src, operand_val)) |operand_val_val| {
10908 if (operand_val_val.isUndef(zcu)) return pt.undefRef(capture_ty);
10909 const union_val = ip.indexToKey(operand_val_val.toIntern()).un;
10910 if (Value.fromInterned(union_val.tag).isUndef(zcu)) return pt.undefRef(capture_ty);
10911 const uncoerced = Air.internedToRef(union_val.val);
10912 return sema.coerce(block, capture_ty, uncoerced, operand_src);
10913 }
10828 resolve_at_comptime: {
10829 // always runtime; evaluation in comptime scope uses `simple`
10830 if (operand == .loop) break :resolve_at_comptime;
1091410831
10915 try sema.requireRuntimeBlock(block, operand_src, null);
10832 var cur_cond_val = try sema.resolveDefinedValue(child_block, src, cond_ref) orelse {
10833 break :resolve_at_comptime;
10834 };
10835 var cur_operand = operand;
1091610836
10917 if (same_types) {
10918 return block.addStructFieldVal(operand_val, first_field_index, capture_ty);
10919 }
10837 while (true) {
10838 if (sema.resolveSwitchBlock(
10839 block,
10840 child_block,
10841 cur_operand,
10842 raw_operand_ty,
10843 cur_cond_val,
10844 catch_all_case,
10845 else_is_named_only,
10846 merges,
10847 switch_inst,
10848 zir_switch,
10849 validated_switch,
10850 )) |result| {
10851 return result;
10852 } else |err| switch (err) {
10853 error.ComptimeBreak => {
10854 const break_inst = sema.code.instructions.get(@intFromEnum(sema.comptime_break_inst));
10855 if (break_inst.tag != .switch_continue) return error.ComptimeBreak;
10856 const extra = sema.code.extraData(Zir.Inst.Break, break_inst.data.@"break".payload_index).data;
10857 if (extra.block_inst != switch_inst) return error.ComptimeBreak;
10858 // This is a `switch_continue` targeting this block. Change the operand and start over.
10859 const new_operand_src = child_block.nodeOffset(extra.operand_src_node.unwrap().?);
10860 const new_operand_uncoerced = try sema.resolveInst(break_inst.data.@"break".operand);
10861 const new_operand = try sema.coerce(child_block, raw_operand_ty, new_operand_uncoerced, new_operand_src);
10862
10863 try sema.emitBackwardBranch(child_block, src);
10864
10865 const new_val, const new_ref = if (operand_is_ref)
10866 .{ try sema.analyzeLoad(child_block, src, new_operand, new_operand_src), new_operand }
10867 else
10868 .{ new_operand, undefined };
1092010869
10921 // We may have to emit a switch block which coerces the operand to the capture type.
10922 // If we can, try to avoid that using in-memory coercions.
10923 const first_non_imc = in_mem: {
10924 for (field_indices, 0..) |field_idx, i| {
10925 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]);
10926 if (.ok != try sema.coerceInMemoryAllowed(block, capture_ty, field_ty, false, zcu.getTarget(), LazySrcLoc.unneeded, LazySrcLoc.unneeded, null)) {
10927 break :in_mem i;
10928 }
10929 }
10930 // All fields are in-memory coercible to the resolved type!
10931 // Just take the first field and bitcast the result.
10932 const uncoerced = try block.addStructFieldVal(operand_val, first_field_index, first_field_ty);
10933 return block.addBitCast(capture_ty, uncoerced);
10934 };
10870 const new_cond_ref = if (union_originally)
10871 try sema.unionToTag(child_block, item_ty, new_val, src)
10872 else
10873 new_val;
1093510874
10936 // By-val capture with heterogeneous types which are not all in-memory coercible to
10937 // the resolved capture type. We finally have to fall back to the ugly method.
10875 cur_cond_val = try sema.resolveConstDefinedValue(child_block, src, new_cond_ref, null);
10876 cur_operand = .{ .simple = .{
10877 .by_val = new_val,
10878 .by_ref = new_ref,
10879 .cond = new_cond_ref,
10880 } };
10881 },
10882 else => |e| return e,
10883 }
10884 }
10885 }
1093810886
10939 // However, let's first track which operands are in-memory coercible. There may well
10940 // be several, and we can squash all of these cases into the same switch prong using
10941 // a simple bitcast. We'll make this the 'else' prong.
10887 if (child_block.isComptime()) {
10888 _ = try sema.resolveConstDefinedValue(child_block, operand_src, operand.simple.cond, null);
10889 unreachable;
10890 }
1094210891
10943 var in_mem_coercible = try std.DynamicBitSet.initFull(sema.arena, field_indices.len);
10944 in_mem_coercible.unset(first_non_imc);
10945 {
10946 const next = first_non_imc + 1;
10947 for (field_indices[next..], next..) |field_idx, i| {
10948 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]);
10949 if (.ok != try sema.coerceInMemoryAllowed(block, capture_ty, field_ty, false, zcu.getTarget(), LazySrcLoc.unneeded, LazySrcLoc.unneeded, null)) {
10950 in_mem_coercible.unset(i);
10892 if (try sema.typeHasOnePossibleValue(item_ty)) |item_opv| {
10893 // We simplify conditions with OPV to either a `loop` or a `block` since
10894 // we cannot switch on a value which doesn't exist at runtime.
10895 assert(operand == .loop); // `simple` should have already been comptime-resolved above!
10896
10897 var case_block = child_block.makeSubBlock();
10898 case_block.runtime_loop = null;
10899 case_block.runtime_cond = operand_src;
10900 case_block.runtime_index.increment();
10901 case_block.need_debug_scope = null; // this body is emitted regardless
10902 defer case_block.instructions.deinit(gpa);
10903
10904 const case_vals = validated_switch.case_vals;
10905
10906 const index, const body, const capture, const has_tag_capture, const is_inline, const is_special = find_prong: {
10907 var case_val_idx: usize = 0;
10908 var case_it = zir_switch.iterateCases();
10909 var extra_index = zir_switch.end;
10910 while (case_it.next()) |case| {
10911 const prong_info = case.prong_info;
10912 const prong_body = sema.code.bodySlice(extra_index, prong_info.body_len);
10913 extra_index += prong_body.len;
10914 skip_case: {
10915 if (!err_set) break :skip_case;
10916 // This case might consist of errors which are not in the set
10917 // we're switching on. If so we have to skip it!
10918 const item_refs = case_vals[case_val_idx..][0..case.item_infos.len];
10919 case_val_idx += item_refs.len;
10920 assert(case.range_infos.len == 0);
10921 for (case.item_infos, item_refs) |item_info, item_ref| {
10922 if (item_info.bodyLen()) |body_len| extra_index += body_len;
10923 if (sema.wantSwitchProngBodyAnalysis(block, item_ref, operand_ty, false, true, prong_info.is_comptime_unreach)) {
10924 break :skip_case;
1095110925 }
1095210926 }
10927 continue;
1095310928 }
10929 break :find_prong .{ case.index, prong_body, prong_info.capture, prong_info.has_tag_capture, prong_info.is_inline, false };
10930 }
10931 if (has_else) {
10932 // This *has* to be checked after iterating all regular cases because
10933 // we allow simple noreturn else prongs when switching on error sets!
10934 break :find_prong .{ else_case.index, else_case.body, else_case.capture, else_case.has_tag_capture, else_case.is_inline, true };
10935 }
10936 if (has_under) {
10937 break :find_prong .{ under_case.index, under_case.body, under_case.capture, under_case.has_tag_capture, false, true };
10938 }
10939 unreachable; // malformed validated switch
10940 };
1095410941
10955 const capture_block_inst = try block.addInstAsIndex(.{
10956 .tag = .block,
10957 .data = .{
10958 .ty_pl = .{
10959 .ty = Air.internedToRef(capture_ty.toIntern()),
10960 .payload = undefined, // updated below
10961 },
10962 },
10963 });
10964
10965 const prong_count = field_indices.len - in_mem_coercible.count();
10966
10967 const estimated_extra = prong_count * 6 + (prong_count / 10); // 2 for Case, 1 item, probably 3 insts; plus hints
10968 var cases_extra = try std.array_list.Managed(u32).initCapacity(sema.gpa, estimated_extra);
10969 defer cases_extra.deinit();
10970
10971 {
10972 // All branch hints are `.none`, so just add zero elems.
10973 comptime assert(@intFromEnum(std.builtin.BranchHint.none) == 0);
10974 const need_elems = std.math.divCeil(usize, prong_count + 1, 10) catch unreachable;
10975 try cases_extra.appendNTimes(0, need_elems);
10976 }
10942 const analyze_body = sema.wantSwitchProngBodyAnalysis(block, .fromValue(item_opv), operand_ty, union_originally, err_set, false);
10943 if (!analyze_body) return .unreachable_value;
1097710944
10978 {
10979 // Non-bitcast cases
10980 var it = in_mem_coercible.iterator(.{ .kind = .unset });
10981 while (it.next()) |idx| {
10982 var coerce_block = block.makeSubBlock();
10983 defer coerce_block.instructions.deinit(sema.gpa);
10984
10985 const case_src: LazySrcLoc = .{
10986 .base_node_inst = capture_src.base_node_inst,
10987 .offset = .{ .switch_case_item = .{
10988 .switch_node_offset = switch_node_offset,
10989 .case_idx = capture_src.offset.switch_capture.case_idx,
10990 .item_idx = .{ .kind = .single, .index = @intCast(idx) },
10991 } },
10992 };
10945 if (!(err_set and
10946 try sema.maybeErrorUnwrap(&case_block, body, cond_ref, operand_src, true)))
10947 {
10948 // Set up captures manually to avoid special cases in the main logic.
10949 const payload_inst: Zir.Inst.Index = if (capture != .none) inst: {
10950 const payload_inst = zir_switch.payload_capture_placeholder.unwrap() orelse switch_inst;
10951 const payload_ref: Air.Inst.Ref = payload_ref: {
10952 const item_val: InternPool.Index = switch (operand_ty.zigTypeTag(zcu)) {
10953 .@"union" => item_val: {
10954 if (maybe_operand_opv) |operand_opv| {
10955 break :item_val zcu.intern_pool.indexToKey(operand_opv.toIntern()).un.val;
10956 }
10957 assert(union_originally); // operand type must be union, otherwise it would be an OPV type here
10958 assert(zir_switch.any_maybe_runtime_capture); // there's a payload capture
10959 const operand_val, const operand_ref = switch (operand) {
10960 .simple => unreachable,
10961 .loop => |l| load_operand: {
10962 const loaded = try sema.analyzeLoad(block, src, l.operand_alloc, src);
10963 if (l.operand_is_ref) {
10964 const by_val = try sema.analyzeLoad(block, src, loaded, src);
10965 break :load_operand .{ by_val, loaded };
10966 } else {
10967 break :load_operand .{ loaded, undefined };
10968 }
10969 },
10970 };
10971 break :payload_ref try sema.analyzeSwitchPayloadCapture(
10972 &case_block,
10973 operand,
10974 operand_val,
10975 operand_ref,
10976 operand_ty,
10977 operand_src,
10978 block.src(.{ .switch_capture = .{
10979 .switch_node_offset = src_node_offset,
10980 .case_idx = index,
10981 } }),
10982 capture == .by_ref,
10983 is_special,
10984 if (!is_special) case_vals else undefined,
10985 if (is_inline) .fromValue(item_opv) else .none,
10986 validated_switch.else_err_ty,
10987 );
10988 },
10989 else => item_opv.toIntern(),
10990 };
10991 break :payload_ref switch (capture) {
10992 .by_val => .fromIntern(item_val),
10993 .by_ref => try sema.uavRef(item_val),
10994 .none => unreachable,
10995 };
10996 };
10997 assert(!sema.typeOf(payload_ref).isNoReturn(sema.pt.zcu));
10998 sema.inst_map.putAssumeCapacity(payload_inst, payload_ref);
10999 break :inst payload_inst;
11000 } else undefined;
11001 defer if (capture != .none) assert(sema.inst_map.remove(payload_inst));
1099311002
10994 const field_idx = field_indices[idx];
10995 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]);
10996 const uncoerced = try coerce_block.addStructFieldVal(operand_val, field_idx, field_ty);
10997 const coerced = try sema.coerce(&coerce_block, capture_ty, uncoerced, case_src);
10998 _ = try coerce_block.addBr(capture_block_inst, coerced);
10999
11000 try cases_extra.ensureUnusedCapacity(@typeInfo(Air.SwitchBr.Case).@"struct".fields.len +
11001 1 + // `item`, no ranges
11002 coerce_block.instructions.items.len);
11003 cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{
11004 .items_len = 1,
11005 .ranges_len = 0,
11006 .body_len = @intCast(coerce_block.instructions.items.len),
11007 }));
11008 cases_extra.appendAssumeCapacity(@intFromEnum(case_vals[idx])); // item
11009 cases_extra.appendSliceAssumeCapacity(@ptrCast(coerce_block.instructions.items)); // body
11010 }
11011 }
11012 const else_body_len = len: {
11013 // 'else' prong uses a bitcast
11014 var coerce_block = block.makeSubBlock();
11015 defer coerce_block.instructions.deinit(sema.gpa);
11003 const tag_inst: Zir.Inst.Index = if (has_tag_capture) inst: {
11004 const tag_inst = zir_switch.tag_capture_placeholder.unwrap() orelse switch_inst;
11005 sema.inst_map.putAssumeCapacity(tag_inst, .fromValue(item_opv));
11006 break :inst tag_inst;
11007 } else undefined;
11008 defer if (has_tag_capture) assert(sema.inst_map.remove(tag_inst));
1101611009
11017 const first_imc_item_idx = in_mem_coercible.findFirstSet().?;
11018 const first_imc_field_idx = field_indices[first_imc_item_idx];
11019 const first_imc_field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[first_imc_field_idx]);
11020 const uncoerced = try coerce_block.addStructFieldVal(operand_val, first_imc_field_idx, first_imc_field_ty);
11021 const coerced = try coerce_block.addBitCast(capture_ty, uncoerced);
11022 _ = try coerce_block.addBr(capture_block_inst, coerced);
11010 if (zir_switch.has_continue) sema.inst_map.putAssumeCapacity(switch_inst, .fromType(raw_operand_ty));
11011 defer if (zir_switch.has_continue) assert(sema.inst_map.remove(switch_inst));
1102311012
11024 try cases_extra.appendSlice(@ptrCast(coerce_block.instructions.items));
11025 break :len coerce_block.instructions.items.len;
11026 };
11013 _ = try sema.analyzeBodyRuntimeBreak(&case_block, body);
11014 }
1102711015
11028 try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.SwitchBr).@"struct".fields.len +
11029 cases_extra.items.len +
11030 @typeInfo(Air.Block).@"struct".fields.len +
11031 1);
11032
11033 const switch_br_inst: u32 = @intCast(sema.air_instructions.len);
11034 try sema.air_instructions.append(sema.gpa, .{
11035 .tag = .switch_br,
11036 .data = .{
11037 .pl_op = .{
11038 .operand = undefined, // set by switch below
11039 .payload = sema.addExtraAssumeCapacity(Air.SwitchBr{
11040 .cases_len = @intCast(prong_count),
11041 .else_body_len = @intCast(else_body_len),
11042 }),
11043 },
11044 },
11045 });
11046 sema.air_extra.appendSliceAssumeCapacity(cases_extra.items);
11047
11048 // Set up block body
11049 switch (spa.operand) {
11050 .simple => |s| {
11051 const air_datas = sema.air_instructions.items(.data);
11052 air_datas[switch_br_inst].pl_op.operand = s.cond;
11053 air_datas[@intFromEnum(capture_block_inst)].ty_pl.payload = sema.addExtraAssumeCapacity(Air.Block{
11054 .body_len = 1,
11055 });
11056 sema.air_extra.appendAssumeCapacity(switch_br_inst);
11057 },
11058 .loop => {
11059 // The block must first extract the tag from the loaded union.
11060 const tag_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len);
11061 try sema.air_instructions.append(sema.gpa, .{
11062 .tag = .get_union_tag,
11063 .data = .{ .ty_op = .{
11064 .ty = Air.internedToRef(union_obj.enum_tag_ty),
11065 .operand = operand_val,
11066 } },
11067 });
11068 const air_datas = sema.air_instructions.items(.data);
11069 air_datas[switch_br_inst].pl_op.operand = tag_inst.toRef();
11070 air_datas[@intFromEnum(capture_block_inst)].ty_pl.payload = sema.addExtraAssumeCapacity(Air.Block{
11071 .body_len = 2,
11072 });
11073 sema.air_extra.appendAssumeCapacity(@intFromEnum(tag_inst));
11074 sema.air_extra.appendAssumeCapacity(switch_br_inst);
11075 },
11076 }
11016 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).@"struct".fields.len +
11017 case_block.instructions.items.len);
11018 const payload_index = sema.addExtraAssumeCapacity(Air.Block{
11019 .body_len = @intCast(case_block.instructions.items.len),
11020 });
11021 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
1107711022
11078 return capture_block_inst.toRef();
11079 },
11080 .error_set => {
11081 if (capture_byref) {
11082 return sema.fail(
11083 block,
11084 capture_src,
11085 "error set cannot be captured by reference",
11086 .{},
11087 );
11088 }
11023 const air_tag: Air.Inst.Tag = if (merges.extra_insts.items.len > 0)
11024 .loop
11025 else
11026 .block;
11027 const air_loop_ref = try child_block.addInst(.{
11028 .tag = air_tag,
11029 .data = .{ .ty_pl = .{
11030 .ty = .noreturn_type,
11031 .payload = payload_index,
11032 } },
11033 });
11034 try sema.fixupSwitchContinues(
11035 block,
11036 src,
11037 air_loop_ref,
11038 operand,
11039 operand_is_ref,
11040 item_ty,
11041 .opv,
11042 zir_switch.any_maybe_runtime_capture,
11043 merges,
11044 );
11045 return null;
11046 }
1108911047
11090 if (case_vals.len == 1) {
11091 const item_val = sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, case_vals[0], undefined) catch unreachable;
11092 const item_ty = try pt.singleErrorSetType(item_val.getErrorName(zcu).unwrap().?);
11093 return sema.bitCast(block, item_ty, operand_val, operand_src, null);
11094 }
11048 assert(maybe_operand_opv == null); // `operand_ty` can only be an OPV type if `item_ty` is one too!
1109511049
11096 var names: InferredErrorSet.NameMap = .{};
11097 try names.ensureUnusedCapacity(sema.arena, case_vals.len);
11098 for (case_vals) |err| {
11099 const err_val = sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, err, undefined) catch unreachable;
11100 names.putAssumeCapacityNoClobber(err_val.getErrorName(zcu).unwrap().?, {});
11101 }
11102 const error_ty = try pt.errorSetFromUnsortedNames(names.keys());
11103 return sema.bitCast(block, error_ty, operand_val, operand_src, null);
11104 },
11105 else => {
11106 // In this case the capture value is just the passed-through value
11107 // of the switch condition.
11108 if (capture_byref) {
11109 return operand_ptr;
11110 } else {
11111 return operand_val;
11112 }
11113 },
11114 }
11115 }
11116};
11050 try sema.finishSwitchBr(
11051 block,
11052 child_block,
11053 operand,
11054 raw_operand_ty,
11055 operand_is_ref,
11056 merges,
11057 switch_inst,
11058 zir_switch,
11059 validated_switch,
11060 );
11061 return null;
11062}
1111711063
11118fn switchCond(
11064fn finishSwitchBr(
1111911065 sema: *Sema,
1112011066 block: *Block,
11121 src: LazySrcLoc,
11122 operand: Air.Inst.Ref,
11123) CompileError!Air.Inst.Ref {
11067 child_block: *Block,
11068 operand: SwitchOperand,
11069 raw_operand_ty: Type,
11070 operand_is_ref: bool,
11071 merges: *Block.Merges,
11072 switch_inst: Zir.Inst.Index,
11073 zir_switch: *const Zir.UnwrappedSwitchBlock,
11074 validated_switch: *const ValidatedSwitchBlock,
11075) CompileError!void {
1112411076 const pt = sema.pt;
1112511077 const zcu = pt.zcu;
11126 const operand_ty = sema.typeOf(operand);
11127 switch (operand_ty.zigTypeTag(zcu)) {
11128 .type,
11129 .void,
11130 .bool,
11131 .int,
11132 .float,
11133 .comptime_float,
11134 .comptime_int,
11135 .enum_literal,
11136 .pointer,
11137 .@"fn",
11138 .error_set,
11139 .@"enum",
11140 => {
11141 if (operand_ty.isSlice(zcu)) {
11142 return sema.fail(block, src, "switch on type '{f}'", .{operand_ty.fmt(pt)});
11143 }
11144 if ((try sema.typeHasOnePossibleValue(operand_ty))) |opv| {
11145 return Air.internedToRef(opv.toIntern());
11146 }
11147 return operand;
11148 },
11078 const ip = &zcu.intern_pool;
11079 const gpa = sema.gpa;
1114911080
11150 .@"union" => {
11151 try operand_ty.resolveFields(pt);
11152 const enum_ty = operand_ty.unionTagType(zcu) orelse {
11153 const msg = msg: {
11154 const msg = try sema.errMsg(src, "switch on union with no attached enum", .{});
11155 errdefer msg.destroy(sema.gpa);
11156 if (operand_ty.srcLocOrNull(zcu)) |union_src| {
11157 try sema.errNote(union_src, msg, "consider 'union(enum)' here", .{});
11158 }
11159 break :msg msg;
11160 };
11161 return sema.failWithOwnedErrorMsg(block, msg);
11162 };
11163 return sema.unionToTag(block, enum_ty, operand, src);
11164 },
11081 const src_node_offset = zir_switch.switch_src_node_offset;
11082 const src = block.nodeOffset(src_node_offset);
11083 const operand_src = block.src(.{ .node_offset_switch_operand = src_node_offset });
1116511084
11166 .error_union,
11167 .noreturn,
11168 .array,
11169 .@"struct",
11170 .undefined,
11171 .null,
11172 .optional,
11173 .@"opaque",
11174 .vector,
11175 .frame,
11176 .@"anyframe",
11177 => return sema.fail(block, src, "switch on type '{f}'", .{operand_ty.fmt(pt)}),
11178 }
11179}
11085 const has_else = zir_switch.else_case != null;
11086 const has_under = zir_switch.under_case != .none;
1118011087
11181const SwitchErrorSet = std.AutoHashMap(InternPool.NullTerminatedString, LazySrcLoc);
11088 const else_case = validated_switch.else_case;
11089 const under_case = validated_switch.under_case;
1118211090
11183fn zirSwitchBlockErrUnion(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
11184 const tracy = trace(@src());
11185 defer tracy.end();
11091 const scalar_cases_len = zir_switch.scalarCasesLen();
11092 const multi_cases_len = zir_switch.multiCasesLen();
1118611093
11187 const pt = sema.pt;
11188 const zcu = pt.zcu;
11189 const gpa = sema.gpa;
11190 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
11191 const switch_src = block.nodeOffset(inst_data.src_node);
11192 const switch_src_node_offset = inst_data.src_node;
11193 const switch_operand_src = block.src(.{ .node_offset_switch_operand = switch_src_node_offset });
11194 const else_prong_src = block.src(.{ .node_offset_switch_else_prong = switch_src_node_offset });
11195 const extra = sema.code.extraData(Zir.Inst.SwitchBlockErrUnion, inst_data.payload_index);
11196 const main_operand_src = block.src(.{ .node_offset_if_cond = extra.data.main_src_node_offset });
11197 const main_src = block.src(.{ .node_offset_main_token = extra.data.main_src_node_offset });
11094 const operand_ty = if (operand_is_ref)
11095 raw_operand_ty.childType(zcu)
11096 else
11097 raw_operand_ty;
1119811098
11199 const raw_operand_val = try sema.resolveInst(extra.data.operand);
11099 const cond_ref = switch (operand) {
11100 .simple => |s| s.cond,
11101 .loop => |l| l.init_cond,
11102 };
1120011103
1120111104 // AstGen guarantees that the instruction immediately preceding
11202 // switch_block_err_union is a dbg_stmt
11203 const cond_dbg_node_index: Zir.Inst.Index = @enumFromInt(@intFromEnum(inst) - 1);
11204
11205 var header_extra_index: usize = extra.end;
11206
11207 const scalar_cases_len = extra.data.bits.scalar_cases_len;
11208 const multi_cases_len = if (extra.data.bits.has_multi_cases) blk: {
11209 const multi_cases_len = sema.code.extra[header_extra_index];
11210 header_extra_index += 1;
11211 break :blk multi_cases_len;
11212 } else 0;
11213
11214 const err_capture_inst: Zir.Inst.Index = if (extra.data.bits.any_uses_err_capture) blk: {
11215 const err_capture_inst: Zir.Inst.Index = @enumFromInt(sema.code.extra[header_extra_index]);
11216 header_extra_index += 1;
11217 // SwitchProngAnalysis wants inst_map to have space for the tag capture.
11218 // Note that the normal capture is referred to via the switch block
11219 // index, which there is already necessarily space for.
11220 try sema.inst_map.ensureSpaceForInstructions(gpa, &.{err_capture_inst});
11221 break :blk err_capture_inst;
11222 } else undefined;
11105 // switch_block[_ref]/switch_block_err_union is a dbg_stmt.
11106 const cond_dbg_node_index: Zir.Inst.Index = @enumFromInt(@intFromEnum(switch_inst) - 1);
1122311107
11224 var case_vals = try std.ArrayList(Air.Inst.Ref).initCapacity(gpa, scalar_cases_len + 2 * multi_cases_len);
11225 defer case_vals.deinit(gpa);
11108 const else_is_named_only = has_else and has_under;
11109 const catch_all_case: CatchAllSwitchCase =
11110 if (has_under) .under else if (has_else) .@"else" else .none;
1122611111
11227 const NonError = struct {
11228 body: []const Zir.Inst.Index,
11229 end: usize,
11230 capture: Zir.Inst.SwitchBlock.ProngInfo.Capture,
11112 const item_ty = switch (operand_ty.zigTypeTag(zcu)) {
11113 .@"union" => operand_ty.unionTagType(zcu).?,
11114 else => operand_ty,
1123111115 };
11116 const union_originally = operand_ty.zigTypeTag(zcu) == .@"union";
11117 const err_set = operand_ty.zigTypeTag(zcu) == .error_set;
1123211118
11233 const non_error_case: NonError = non_error: {
11234 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[header_extra_index]);
11235 const extra_body_start = header_extra_index + 1;
11236 break :non_error .{
11237 .body = sema.code.bodySlice(extra_body_start, info.body_len),
11238 .end = extra_body_start + info.body_len,
11239 .capture = info.capture,
11240 };
11241 };
11119 const estimated_cases_len: u32 = scalar_cases_len + multi_cases_len +
11120 @intFromBool(has_else or has_under);
1124211121
11243 const Else = struct {
11244 body: []const Zir.Inst.Index,
11245 end: usize,
11246 is_inline: bool,
11247 has_capture: bool,
11248 };
11249
11250 const else_case: Else = if (!extra.data.bits.has_else) .{
11251 .body = &.{},
11252 .end = non_error_case.end,
11253 .is_inline = false,
11254 .has_capture = false,
11255 } else special: {
11256 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[non_error_case.end]);
11257 const extra_body_start = non_error_case.end + 1;
11258 assert(info.capture != .by_ref);
11259 assert(!info.has_tag_capture);
11260 break :special .{
11261 .body = sema.code.bodySlice(extra_body_start, info.body_len),
11262 .end = extra_body_start + info.body_len,
11263 .is_inline = info.is_inline,
11264 .has_capture = info.capture != .none,
11265 };
11266 };
11122 var cases_extra: std.ArrayList(u32) = try .initCapacity(gpa, estimated_cases_len *
11123 @typeInfo(Air.SwitchBr.Case).@"struct".fields.len);
11124 defer cases_extra.deinit(gpa);
11125 var branch_hints: Air.SwitchBr.BranchHints = try .initCapacity(gpa, estimated_cases_len);
11126 defer branch_hints.deinit(gpa);
1126711127
11268 var seen_errors = SwitchErrorSet.init(gpa);
11269 defer seen_errors.deinit();
11128 // We will reuse this block for each case.
11129 var case_block = child_block.makeSubBlock();
11130 case_block.runtime_loop = null;
11131 case_block.runtime_cond = operand_src;
11132 case_block.runtime_index.increment();
11133 case_block.need_debug_scope = null; // this body is emitted regardless
11134 defer case_block.instructions.deinit(gpa);
1127011135
11271 const operand_ty = sema.typeOf(raw_operand_val);
11272 const operand_err_set = if (extra.data.bits.payload_is_ref)
11273 operand_ty.childType(zcu)
11274 else
11275 operand_ty;
11136 const case_vals = validated_switch.case_vals;
11137 var case_val_idx: usize = 0;
11138 var case_it = zir_switch.iterateCases();
11139 var extra_index = zir_switch.end;
1127611140
11277 if (operand_err_set.zigTypeTag(zcu) != .error_union) {
11278 return sema.fail(block, switch_src, "expected error union type, found '{f}'", .{
11279 operand_ty.fmt(pt),
11280 });
11281 }
11141 var cases_len: u32 = 0;
11142 while (case_it.next()) |case| {
11143 if (case.isUnder()) { // we'll deal with this later
11144 extra_index += case.prong_info.body_len;
11145 for (case.item_infos) |item_info| {
11146 if (item_info.bodyLen()) |body_len| extra_index += body_len;
11147 }
11148 for (case.range_infos) |range_info| {
11149 if (range_info[0].bodyLen()) |body_len| extra_index += body_len;
11150 if (range_info[1].bodyLen()) |body_len| extra_index += body_len;
11151 }
11152 continue;
11153 }
1128211154
11283 const operand_err_set_ty = operand_err_set.errorUnionSet(zcu);
11155 const item_refs = case_vals[case_val_idx..][0..case.item_infos.len];
11156 case_val_idx += item_refs.len;
11157 const range_refs: []const [2]Air.Inst.Ref =
11158 @ptrCast(case_vals[case_val_idx..][0 .. 2 * case.range_infos.len]);
11159 case_val_idx += 2 * range_refs.len;
1128411160
11285 const block_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len);
11286 try sema.air_instructions.append(gpa, .{
11287 .tag = .block,
11288 .data = undefined,
11289 });
11290 var label: Block.Label = .{
11291 .zir_block = inst,
11292 .merges = .{
11293 .src_locs = .{},
11294 .results = .{},
11295 .br_list = .{},
11296 .block_inst = block_inst,
11297 },
11298 };
11161 const prong_info = case.prong_info;
11162 const prong_body = sema.code.bodySlice(extra_index, prong_info.body_len);
11163 extra_index += prong_body.len;
1129911164
11300 var child_block: Block = .{
11301 .parent = block,
11302 .sema = sema,
11303 .namespace = block.namespace,
11304 .instructions = .{},
11305 .label = &label,
11306 .inlining = block.inlining,
11307 .comptime_reason = block.comptime_reason,
11308 .is_typeof = block.is_typeof,
11309 .c_import_buf = block.c_import_buf,
11310 .runtime_cond = block.runtime_cond,
11311 .runtime_loop = block.runtime_loop,
11312 .runtime_index = block.runtime_index,
11313 .error_return_trace_index = block.error_return_trace_index,
11314 .want_safety = block.want_safety,
11315 .src_base_inst = block.src_base_inst,
11316 .type_name_ctx = block.type_name_ctx,
11317 };
11318 const merges = &child_block.label.?.merges;
11319 defer child_block.instructions.deinit(gpa);
11320 defer merges.deinit(gpa);
11165 // Enough capacity for inlining regular items, we can't really predict
11166 // how many range items we will end up with (at least not in a safe and
11167 // cheap manner) so we allocate on demand for those.
11168 if (prong_info.is_inline) {
11169 try branch_hints.ensureUnusedCapacity(gpa, @intCast(case.item_infos.len));
11170 }
1132111171
11322 const resolved_err_set = try sema.resolveInferredErrorSetTy(block, main_src, operand_err_set_ty.toIntern());
11323 if (Type.fromInterned(resolved_err_set).errorSetIsEmpty(zcu)) {
11324 return sema.resolveBlockBody(block, main_operand_src, &child_block, non_error_case.body, inst, merges);
11325 }
11172 var emit_bb = false;
11173 var any_analyze_body = false;
11174 for (case.item_infos, item_refs, 0..) |item_info, item_ref, item_i| {
11175 if (item_info.bodyLen()) |body_len| extra_index += body_len;
1132611176
11327 const else_error_ty: ?Type = try validateErrSetSwitch(
11328 sema,
11329 block,
11330 &seen_errors,
11331 &case_vals,
11332 operand_err_set_ty,
11333 inst_data,
11334 scalar_cases_len,
11335 multi_cases_len,
11336 .{ .body = else_case.body, .end = else_case.end, .src = else_prong_src },
11337 extra.data.bits.has_else,
11338 );
11177 const analyze_body = sema.wantSwitchProngBodyAnalysis(block, item_ref, operand_ty, union_originally, err_set, prong_info.is_comptime_unreach);
11178 if (analyze_body) any_analyze_body = true;
1133911179
11340 var spa: SwitchProngAnalysis = .{
11341 .sema = sema,
11342 .parent_block = block,
11343 .operand = .{
11344 .simple = .{
11345 .by_val = undefined, // must be set to the unwrapped error code before use
11346 .by_ref = undefined,
11347 .cond = raw_operand_val,
11348 },
11349 },
11350 .else_error_ty = else_error_ty,
11351 .switch_block_inst = inst,
11352 .tag_capture_inst = undefined,
11353 };
11180 if (prong_info.is_inline) {
11181 cases_len += 1;
11182 case_block.instructions.clearRetainingCapacity();
11183 case_block.error_return_trace_index = child_block.error_return_trace_index;
1135411184
11355 if (try sema.resolveDefinedValue(&child_block, main_src, raw_operand_val)) |ov| {
11356 const operand_val = if (extra.data.bits.payload_is_ref)
11357 (try sema.pointerDeref(&child_block, main_src, ov, operand_ty)).?
11358 else
11359 ov;
11185 if (emit_bb) {
11186 const bb_src = block.src(.{ .switch_case_item = .{
11187 .switch_node_offset = src_node_offset,
11188 .case_idx = case.index,
11189 .item_idx = .{ .kind = .single, .value = @intCast(item_i) },
11190 } });
11191 try sema.emitBackwardBranch(block, bb_src);
11192 }
11193 emit_bb = true;
1136011194
11361 if (operand_val.errorUnionIsPayload(zcu)) {
11362 return sema.resolveBlockBody(block, main_operand_src, &child_block, non_error_case.body, inst, merges);
11363 } else {
11364 const err_val = Value.fromInterned(try pt.intern(.{
11365 .err = .{
11366 .ty = operand_err_set_ty.toIntern(),
11367 .name = operand_val.getErrorName(zcu).unwrap().?,
11368 },
11369 }));
11370 spa.operand.simple.by_val = if (extra.data.bits.payload_is_ref)
11371 try sema.analyzeErrUnionCodePtr(block, switch_operand_src, raw_operand_val)
11372 else
11373 try sema.analyzeErrUnionCode(block, switch_operand_src, raw_operand_val);
11195 const prong_hint: std.builtin.BranchHint = hint: {
11196 if (analyze_body) break :hint try sema.analyzeSwitchProng(
11197 &case_block,
11198 operand,
11199 operand_ty,
11200 raw_operand_ty,
11201 prong_body,
11202 block.src(.{ .switch_capture = .{
11203 .switch_node_offset = src_node_offset,
11204 .case_idx = case.index,
11205 } }),
11206 prong_info.capture,
11207 prong_info.has_tag_capture,
11208 item_ref,
11209 .{ .item_refs = &.{item_ref} },
11210 validated_switch.else_err_ty,
11211 switch_inst,
11212 zir_switch,
11213 );
11214 _ = try case_block.addNoOp(.unreach);
11215 break :hint .cold; // unreachable branches are cold
11216 };
11217 branch_hints.appendAssumeCapacity(prong_hint);
1137411218
11375 if (extra.data.bits.any_uses_err_capture) {
11376 sema.inst_map.putAssumeCapacity(err_capture_inst, spa.operand.simple.by_val);
11219 try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len +
11220 1 + // `item`, no ranges
11221 case_block.instructions.items.len);
11222 cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{
11223 .items_len = 1,
11224 .ranges_len = 0,
11225 .body_len = @intCast(case_block.instructions.items.len),
11226 }));
11227 cases_extra.appendAssumeCapacity(@intFromEnum(item_ref));
11228 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
1137711229 }
11378 defer if (extra.data.bits.any_uses_err_capture) assert(sema.inst_map.remove(err_capture_inst));
11379
11380 return resolveSwitchComptime(
11381 sema,
11382 spa,
11383 &child_block,
11384 try sema.switchCond(block, switch_operand_src, spa.operand.simple.by_val),
11385 err_val,
11386 operand_err_set_ty,
11387 switch_src_node_offset,
11388 null,
11389 .{
11390 .body = else_case.body,
11391 .end = else_case.end,
11392 .capture = if (else_case.has_capture) .by_val else .none,
11393 .is_inline = else_case.is_inline,
11394 .has_tag_capture = false,
11395 },
11396 false,
11397 case_vals,
11398 scalar_cases_len,
11399 multi_cases_len,
11400 true,
11401 false,
11402 );
1140311230 }
11404 }
11231 for (case.range_infos, range_refs, 0..) |range_info, range_ref, range_i| {
11232 if (range_info[0].bodyLen()) |body_len| extra_index += body_len;
11233 if (range_info[1].bodyLen()) |body_len| extra_index += body_len;
1140511234
11406 if (scalar_cases_len + multi_cases_len == 0) {
11407 if (else_error_ty) |ty| if (ty.errorSetIsEmpty(zcu)) {
11408 return sema.resolveBlockBody(block, main_operand_src, &child_block, non_error_case.body, inst, merges);
11409 };
11410 }
11235 any_analyze_body = true; // always an integer range, always needs analysis
1141111236
11412 if (child_block.isComptime()) {
11413 _ = try sema.resolveConstDefinedValue(&child_block, main_operand_src, raw_operand_val, null);
11414 unreachable;
11415 }
11237 if (prong_info.is_inline) {
11238 var item = sema.resolveConstDefinedValue(block, .unneeded, range_ref[0], undefined) catch unreachable;
11239 const item_last = sema.resolveConstDefinedValue(block, .unneeded, range_ref[1], undefined) catch unreachable;
1141611240
11417 const cond = if (extra.data.bits.payload_is_ref) blk: {
11418 try sema.checkErrorType(block, main_src, sema.typeOf(raw_operand_val).elemType2(zcu));
11419 const loaded = try sema.analyzeLoad(block, main_src, raw_operand_val, main_src);
11420 break :blk try sema.analyzeIsNonErr(block, main_src, loaded);
11421 } else blk: {
11422 try sema.checkErrorType(block, main_src, sema.typeOf(raw_operand_val));
11423 break :blk try sema.analyzeIsNonErr(block, main_src, raw_operand_val);
11424 };
11241 if (try item.getUnsignedIntSema(pt)) |first_int| {
11242 if (try item_last.getUnsignedIntSema(pt)) |last_int| {
11243 if (std.math.cast(u32, last_int - first_int)) |range_len| {
11244 try branch_hints.ensureUnusedCapacity(gpa, range_len);
11245 }
11246 }
11247 }
1142511248
11426 var sub_block = child_block.makeSubBlock();
11427 sub_block.runtime_loop = null;
11428 sub_block.runtime_cond = main_operand_src;
11429 sub_block.runtime_index.increment();
11430 sub_block.need_debug_scope = null; // this body is emitted regardless
11431 defer sub_block.instructions.deinit(gpa);
11249 var prev_result_overflowed = false;
11250 while (item.compareScalar(.lte, item_last, operand_ty, zcu)) : ({
11251 // Previous validation has resolved any possible lazy values.
11252 const int_val: Value, const int_ty: Type = switch (operand_ty.zigTypeTag(zcu)) {
11253 .int => .{ item, operand_ty },
11254 .@"enum" => b: {
11255 const int_val: Value = .fromInterned(ip.indexToKey(item.toIntern()).enum_tag.int);
11256 break :b .{ int_val, int_val.typeOf(zcu) };
11257 },
11258 else => unreachable,
11259 };
11260 assert(!prev_result_overflowed);
11261 const result = try arith.incrementDefinedInt(sema, int_ty, int_val);
11262 prev_result_overflowed = result.overflow;
11263 item = switch (operand_ty.zigTypeTag(zcu)) {
11264 .int => result.val,
11265 .@"enum" => .fromInterned(try pt.intern(.{ .enum_tag = .{
11266 .ty = operand_ty.toIntern(),
11267 .int = result.val.toIntern(),
11268 } })),
11269 else => unreachable,
11270 };
11271 }) {
11272 cases_len += 1;
11273 case_block.instructions.clearRetainingCapacity();
11274 case_block.error_return_trace_index = child_block.error_return_trace_index;
1143211275
11433 const non_error_hint = try sema.analyzeBodyRuntimeBreak(&sub_block, non_error_case.body);
11434 const true_instructions = try sub_block.instructions.toOwnedSlice(gpa);
11435 defer gpa.free(true_instructions);
11276 const item_ref: Air.Inst.Ref = .fromValue(item);
1143611277
11437 spa.operand.simple.by_val = if (extra.data.bits.payload_is_ref)
11438 try sema.analyzeErrUnionCodePtr(&sub_block, switch_operand_src, raw_operand_val)
11439 else
11440 try sema.analyzeErrUnionCode(&sub_block, switch_operand_src, raw_operand_val);
11441
11442 if (extra.data.bits.any_uses_err_capture) {
11443 sema.inst_map.putAssumeCapacity(err_capture_inst, spa.operand.simple.by_val);
11444 }
11445 defer if (extra.data.bits.any_uses_err_capture) assert(sema.inst_map.remove(err_capture_inst));
11446 _ = try sema.analyzeSwitchRuntimeBlock(
11447 spa,
11448 &sub_block,
11449 switch_src,
11450 try sema.switchCond(block, switch_operand_src, spa.operand.simple.by_val),
11451 operand_err_set_ty,
11452 switch_operand_src,
11453 case_vals,
11454 .{
11455 .body = else_case.body,
11456 .end = else_case.end,
11457 .capture = if (else_case.has_capture) .by_val else .none,
11458 .is_inline = else_case.is_inline,
11459 .has_tag_capture = false,
11460 },
11461 scalar_cases_len,
11462 multi_cases_len,
11463 false,
11464 undefined,
11465 true,
11466 switch_src_node_offset,
11467 else_prong_src,
11468 false,
11469 undefined,
11470 seen_errors,
11471 undefined,
11472 undefined,
11473 undefined,
11474 cond_dbg_node_index,
11475 true,
11476 null,
11477 undefined,
11478 &.{},
11479 &.{},
11480 );
11278 if (emit_bb) {
11279 const bb_src = block.src(.{ .switch_case_item = .{
11280 .switch_node_offset = src_node_offset,
11281 .case_idx = case.index,
11282 .item_idx = .{ .kind = .range, .value = @intCast(range_i) },
11283 } });
11284 try sema.emitBackwardBranch(block, bb_src);
11285 }
11286 emit_bb = true;
1148111287
11482 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).@"struct".fields.len +
11483 true_instructions.len + sub_block.instructions.items.len);
11288 const prong_hint = try sema.analyzeSwitchProng(
11289 &case_block,
11290 operand,
11291 operand_ty,
11292 raw_operand_ty,
11293 prong_body,
11294 block.src(.{ .switch_capture = .{
11295 .switch_node_offset = src_node_offset,
11296 .case_idx = case.index,
11297 } }),
11298 prong_info.capture,
11299 prong_info.has_tag_capture,
11300 item_ref,
11301 .has_ranges,
11302 validated_switch.else_err_ty,
11303 switch_inst,
11304 zir_switch,
11305 );
11306 try branch_hints.append(gpa, prong_hint);
1148411307
11485 _ = try child_block.addInst(.{
11486 .tag = .cond_br,
11487 .data = .{
11488 .pl_op = .{
11489 .operand = cond,
11490 .payload = sema.addExtraAssumeCapacity(Air.CondBr{
11491 .then_body_len = @intCast(true_instructions.len),
11492 .else_body_len = @intCast(sub_block.instructions.items.len),
11493 .branch_hints = .{
11494 .true = non_error_hint,
11495 .false = .none,
11496 // Code coverage is desired for error handling.
11497 .then_cov = .poi,
11498 .else_cov = .poi,
11499 },
11500 }),
11501 },
11502 },
11503 });
11504 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(true_instructions));
11505 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(sub_block.instructions.items));
11308 try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len +
11309 1 + // `item`, no ranges
11310 case_block.instructions.items.len);
11311 cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{
11312 .items_len = 1,
11313 .ranges_len = 0,
11314 .body_len = @intCast(case_block.instructions.items.len),
11315 }));
11316 cases_extra.appendAssumeCapacity(@intFromEnum(item_ref));
11317 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
11318 }
11319 }
11320 }
1150611321
11507 return sema.resolveAnalyzedBlock(block, main_src, &child_block, merges, false);
11508}
11322 if (!prong_info.is_inline) {
11323 cases_len += 1;
11324 case_block.instructions.clearRetainingCapacity();
11325 case_block.error_return_trace_index = child_block.error_return_trace_index;
1150911326
11510fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_ref: bool) CompileError!Air.Inst.Ref {
11511 const tracy = trace(@src());
11512 defer tracy.end();
11327 const prong_hint: std.builtin.BranchHint = hint: {
11328 if (any_analyze_body) break :hint try sema.analyzeSwitchProng(
11329 &case_block,
11330 operand,
11331 operand_ty,
11332 raw_operand_ty,
11333 prong_body,
11334 block.src(.{ .switch_capture = .{
11335 .switch_node_offset = src_node_offset,
11336 .case_idx = case.index,
11337 } }),
11338 prong_info.capture,
11339 prong_info.has_tag_capture,
11340 .none,
11341 .{ .item_refs = item_refs },
11342 validated_switch.else_err_ty,
11343 switch_inst,
11344 zir_switch,
11345 );
11346 _ = try case_block.addNoOp(.unreach);
11347 break :hint .cold; // unreachable branches are cold
11348 };
11349 try branch_hints.append(gpa, prong_hint);
11350
11351 try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len +
11352 item_refs.len +
11353 2 * range_refs.len +
11354 case_block.instructions.items.len);
11355 cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{
11356 .items_len = @intCast(item_refs.len),
11357 .ranges_len = @intCast(range_refs.len),
11358 .body_len = @intCast(case_block.instructions.items.len),
11359 }));
11360 cases_extra.appendSliceAssumeCapacity(@ptrCast(item_refs));
11361 cases_extra.appendSliceAssumeCapacity(@ptrCast(range_refs));
11362 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
11363 }
11364 }
1151311365
11514 const pt = sema.pt;
11515 const zcu = pt.zcu;
11516 const ip = &zcu.intern_pool;
11517 const gpa = sema.gpa;
11518 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
11519 const src = block.nodeOffset(inst_data.src_node);
11520 const src_node_offset = inst_data.src_node;
11521 const operand_src = block.src(.{ .node_offset_switch_operand = src_node_offset });
11522 const else_prong_src = block.src(.{ .node_offset_switch_else_prong = src_node_offset });
11523 const under_prong_src = block.src(.{ .node_offset_switch_under_prong = src_node_offset });
11524 const extra = sema.code.extraData(Zir.Inst.SwitchBlock, inst_data.payload_index);
11366 const catch_all_extra: []const u32 = catch_all_extra: {
11367 if (catch_all_case == .none and !case_block.wantSafety()) {
11368 try branch_hints.append(gpa, .none);
11369 break :catch_all_extra &.{};
11370 }
11371 var emit_bb = false;
11372 if (has_else and else_case.is_inline) {
11373 const else_prong_src = block.src(.{ .node_offset_switch_else_prong = src_node_offset });
11374 var error_names: InternPool.NullTerminatedString.Slice = undefined;
11375 var min_int: Value = undefined;
11376 check_enumerable: {
11377 switch (item_ty.zigTypeTag(zcu)) {
11378 .@"union" => unreachable,
11379 .@"enum" => if (else_is_named_only or
11380 !item_ty.isNonexhaustiveEnum(zcu) or union_originally)
11381 {
11382 try branch_hints.ensureUnusedCapacity(gpa, @intCast(validated_switch.seen_enum_fields.len));
11383 break :check_enumerable;
11384 },
11385 .error_set => if (!operand_ty.isAnyError(zcu)) {
11386 error_names = item_ty.errorSetNames(zcu);
11387 try branch_hints.ensureUnusedCapacity(gpa, error_names.len);
11388 break :check_enumerable;
11389 },
11390 .int => {
11391 min_int = try item_ty.minInt(pt, item_ty);
11392 break :check_enumerable;
11393 },
11394 .bool, .void => break :check_enumerable,
11395 else => {},
11396 }
11397 return sema.fail(block, else_prong_src, "cannot enumerate values of type '{f}' for 'inline else'", .{
11398 item_ty.fmt(pt),
11399 });
11400 }
11401 var unhandled_it = validated_switch.iterateUnhandledItems(error_names, min_int);
11402 while (try unhandled_it.next(sema, item_ty)) |item_val| {
11403 cases_len += 1;
11404 case_block.instructions.clearRetainingCapacity();
11405 case_block.error_return_trace_index = child_block.error_return_trace_index;
1152511406
11526 const operand: SwitchProngAnalysis.Operand, const raw_operand_ty: Type = op: {
11527 const maybe_ptr = try sema.resolveInst(extra.data.operand);
11528 const val, const ref = if (operand_is_ref)
11529 .{ try sema.analyzeLoad(block, src, maybe_ptr, operand_src), maybe_ptr }
11530 else
11531 .{ maybe_ptr, undefined };
11407 const item_ref: Air.Inst.Ref = .fromValue(item_val);
1153211408
11533 const init_cond = try sema.switchCond(block, operand_src, val);
11409 const analyze_body = sema.wantSwitchProngBodyAnalysis(block, item_ref, operand_ty, union_originally, err_set, false);
1153411410
11535 const operand_ty = sema.typeOf(val);
11411 if (emit_bb) try sema.emitBackwardBranch(block, else_prong_src);
11412 emit_bb = true;
1153611413
11537 if (extra.data.bits.has_continue and !block.isComptime()) {
11538 // Even if the operand is comptime-known, this `switch` is runtime.
11539 if (try operand_ty.comptimeOnlySema(pt)) {
11540 return sema.failWithOwnedErrorMsg(block, msg: {
11541 const msg = try sema.errMsg(operand_src, "operand of switch loop has comptime-only type '{f}'", .{operand_ty.fmt(pt)});
11542 errdefer msg.destroy(gpa);
11543 try sema.errNote(operand_src, msg, "switch loops are evaluated at runtime outside of comptime scopes", .{});
11544 break :msg msg;
11545 });
11414 const prong_hint: std.builtin.BranchHint = hint: {
11415 if (analyze_body) break :hint try sema.analyzeSwitchProng(
11416 &case_block,
11417 operand,
11418 operand_ty,
11419 raw_operand_ty,
11420 else_case.body,
11421 block.src(.{ .switch_capture = .{
11422 .switch_node_offset = src_node_offset,
11423 .case_idx = else_case.index,
11424 } }),
11425 else_case.capture,
11426 else_case.has_tag_capture,
11427 item_ref,
11428 .special,
11429 validated_switch.else_err_ty,
11430 switch_inst,
11431 zir_switch,
11432 );
11433 _ = try case_block.addNoOp(.unreach);
11434 break :hint .cold; // unreachable branches are cold
11435 };
11436 try branch_hints.append(gpa, prong_hint);
11437
11438 try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len +
11439 1 + // `item`, no ranges
11440 case_block.instructions.items.len);
11441 cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{
11442 .items_len = 1,
11443 .ranges_len = 0,
11444 .body_len = @intCast(case_block.instructions.items.len),
11445 }));
11446 cases_extra.appendAssumeCapacity(@intFromEnum(item_ref));
11447 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
1154611448 }
11547 try sema.validateRuntimeValue(block, operand_src, maybe_ptr);
11548 const operand_alloc = if (extra.data.bits.any_non_inline_capture or
11549 extra.data.bits.any_has_tag_capture)
11550 a: {
11551 const operand_ptr_ty = try pt.singleMutPtrType(sema.typeOf(maybe_ptr));
11552 const operand_alloc = try block.addTy(.alloc, operand_ptr_ty);
11553 _ = try block.addBinOp(.store, operand_alloc, maybe_ptr);
11554 break :a operand_alloc;
11555 } else undefined;
11556 break :op .{
11557 .{ .loop = .{
11558 .operand_alloc = operand_alloc,
11559 .operand_is_ref = operand_is_ref,
11560 .init_cond = init_cond,
11561 } },
11562 operand_ty,
11563 };
1156411449 }
1156511450
11566 // We always use `simple` in the comptime case, because as far as the dispatching logic
11567 // is concerned, it really is dispatching a single prong. `resolveSwitchComptime` will
11568 // be resposible for recursively resolving different prongs as needed.
11569 break :op .{
11570 .{ .simple = .{
11571 .by_val = val,
11572 .by_ref = ref,
11573 .cond = init_cond,
11574 } },
11575 operand_ty,
11576 };
11577 };
11578
11579 const union_originally = raw_operand_ty.zigTypeTag(zcu) == .@"union";
11580 const err_set = raw_operand_ty.zigTypeTag(zcu) == .error_set;
11581 const cond_ty = switch (raw_operand_ty.zigTypeTag(zcu)) {
11582 .@"union" => raw_operand_ty.unionTagType(zcu).?, // validated by `switchCond` above
11583 else => raw_operand_ty,
11584 };
11585
11586 // AstGen guarantees that the instruction immediately preceding
11587 // switch_block(_ref) is a dbg_stmt
11588 const cond_dbg_node_index: Zir.Inst.Index = @enumFromInt(@intFromEnum(inst) - 1);
11451 case_block.instructions.clearRetainingCapacity();
11452 case_block.error_return_trace_index = child_block.error_return_trace_index;
1158911453
11590 var header_extra_index: usize = extra.end;
11454 if (zcu.backendSupportsFeature(.is_named_enum_value) and
11455 catch_all_case != .none and block.wantSafety() and
11456 item_ty.zigTypeTag(zcu) == .@"enum" and
11457 (!operand_ty.isNonexhaustiveEnum(zcu) or union_originally))
11458 {
11459 try sema.zirDbgStmt(&case_block, cond_dbg_node_index);
11460 const ok = try case_block.addUnOp(.is_named_enum_value, cond_ref);
11461 if (else_is_named_only) {} else {
11462 try sema.addSafetyCheck(&case_block, src, ok, .corrupt_switch);
11463 }
11464 }
1159111465
11592 const scalar_cases_len = extra.data.bits.scalar_cases_len;
11593 const multi_cases_len = if (extra.data.bits.has_multi_cases) blk: {
11594 const multi_cases_len = sema.code.extra[header_extra_index];
11595 header_extra_index += 1;
11596 break :blk multi_cases_len;
11597 } else 0;
11466 if (else_is_named_only and !else_case.is_inline) {
11467 // If we have both an `else` and an `_` prong, all named values go
11468 // into the `else` prong and all unnamed ones go into the `_` prong.
11469 // We will manually enumerate all named values which haven't been
11470 // encountered yet and create an extra prong for them, which will
11471 // evaulate to the `else` body.
1159811472
11599 const tag_capture_inst: Zir.Inst.Index = if (extra.data.bits.any_has_tag_capture) blk: {
11600 const tag_capture_inst: Zir.Inst.Index = @enumFromInt(sema.code.extra[header_extra_index]);
11601 header_extra_index += 1;
11602 // SwitchProngAnalysis wants inst_map to have space for the tag capture.
11603 // Note that the normal capture is referred to via the switch block
11604 // index, which there is already necessarily space for.
11605 try sema.inst_map.ensureSpaceForInstructions(gpa, &.{tag_capture_inst});
11606 break :blk tag_capture_inst;
11607 } else undefined;
11473 assert(operand_ty.isNonexhaustiveEnum(zcu));
1160811474
11609 var case_vals = try std.ArrayList(Air.Inst.Ref).initCapacity(gpa, scalar_cases_len + 2 * multi_cases_len);
11610 defer case_vals.deinit(gpa);
11611
11612 var single_absorbed_item: Zir.Inst.Ref = .none;
11613 var absorbed_items: []const Zir.Inst.Ref = &.{};
11614 var absorbed_ranges: []const Zir.Inst.Ref = &.{};
11615
11616 const special_prongs = extra.data.bits.special_prongs;
11617 const has_else = special_prongs.hasElse();
11618 const has_under = special_prongs.hasUnder();
11619 const special_else: SpecialProng = if (has_else) blk: {
11620 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[header_extra_index]);
11621 const extra_body_start = header_extra_index + 1;
11622 break :blk .{
11623 .body = sema.code.bodySlice(extra_body_start, info.body_len),
11624 .end = extra_body_start + info.body_len,
11625 .capture = info.capture,
11626 .is_inline = info.is_inline,
11627 .has_tag_capture = info.has_tag_capture,
11628 };
11629 } else .{
11630 .body = &.{},
11631 .end = header_extra_index,
11632 .capture = .none,
11633 .is_inline = false,
11634 .has_tag_capture = false,
11635 };
11636 const special_under: SpecialProng = if (has_under) blk: {
11637 var extra_index = special_else.end;
11638 var trailing_items_len: usize = 0;
11639 if (special_prongs.hasOneAdditionalItem()) {
11640 single_absorbed_item = @enumFromInt(sema.code.extra[extra_index]);
11641 extra_index += 1;
11642 absorbed_items = @ptrCast(&single_absorbed_item);
11643 } else if (special_prongs.hasManyAdditionalItems()) {
11644 const items_len = sema.code.extra[extra_index];
11645 extra_index += 1;
11646 const ranges_len = sema.code.extra[extra_index];
11647 extra_index += 1;
11648 absorbed_items = sema.code.refSlice(extra_index + 1, items_len);
11649 absorbed_ranges = sema.code.refSlice(extra_index + 1 + items_len, ranges_len * 2);
11650 trailing_items_len = items_len + ranges_len * 2;
11651 }
11652 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
11653 extra_index += 1 + trailing_items_len;
11654 break :blk .{
11655 .body = sema.code.bodySlice(extra_index, info.body_len),
11656 .end = extra_index + info.body_len,
11657 .capture = info.capture,
11658 .is_inline = info.is_inline,
11659 .has_tag_capture = info.has_tag_capture,
11660 };
11661 } else .{
11662 .body = &.{},
11663 .end = special_else.end,
11664 .capture = .none,
11665 .is_inline = false,
11666 .has_tag_capture = false,
11667 };
11668 const special_end = special_under.end;
11475 cases_len += 1;
1166911476
11670 // Duplicate checking variables later also used for `inline else`.
11671 var seen_enum_fields: []?LazySrcLoc = &.{};
11672 var seen_errors = SwitchErrorSet.init(gpa);
11673 var range_set = RangeSet.init(gpa, zcu);
11674 var true_count: u8 = 0;
11675 var false_count: u8 = 0;
11477 const prong_hint: std.builtin.BranchHint = hint: {
11478 if (!else_case.is_inline) break :hint try sema.analyzeSwitchProng(
11479 &case_block,
11480 operand,
11481 operand_ty,
11482 raw_operand_ty,
11483 else_case.body,
11484 block.src(.{ .switch_capture = .{
11485 .switch_node_offset = src_node_offset,
11486 .case_idx = else_case.index,
11487 } }),
11488 else_case.capture,
11489 else_case.has_tag_capture,
11490 .none,
11491 .special,
11492 validated_switch.else_err_ty,
11493 switch_inst,
11494 zir_switch,
11495 );
11496 _ = try case_block.addNoOp(.unreach);
11497 break :hint .cold; // unreachable branches are cold
11498 };
11499 try branch_hints.append(gpa, prong_hint);
1167611500
11677 defer {
11678 range_set.deinit();
11679 gpa.free(seen_enum_fields);
11680 seen_errors.deinit();
11681 }
11501 try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len +
11502 (validated_switch.seen_enum_fields.len - zir_switch.totalItemsLen()) +
11503 case_block.instructions.items.len);
11504 const extra_case = cases_extra.addManyAsArrayAssumeCapacity(
11505 @typeInfo(Air.SwitchBr.Case).@"struct".fields.len,
11506 );
11507 var items_len: u32 = 0;
11508 for (validated_switch.seen_enum_fields, 0..) |seen_field, field_i| {
11509 if (seen_field != null) continue;
11510 const item_val = try pt.enumValueFieldIndex(item_ty, @intCast(field_i));
11511 const item_ref: Air.Inst.Ref = .fromValue(item_val);
11512 cases_extra.appendAssumeCapacity(@intFromEnum(item_ref));
11513 items_len += 1;
11514 }
11515 assert(items_len > 0); // `else` must be reachable at this point
11516 extra_case.* = payloadToExtraItems(Air.SwitchBr.Case{
11517 .items_len = items_len,
11518 .ranges_len = 0,
11519 .body_len = @intCast(case_block.instructions.items.len),
11520 });
11521 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
1168211522
11683 var empty_enum = false;
11523 // We fall through to the regular catch-all prong generation.
1168411524
11685 var else_error_ty: ?Type = null;
11525 case_block.instructions.clearRetainingCapacity();
11526 case_block.error_return_trace_index = child_block.error_return_trace_index;
11527 }
1168611528
11687 // Validate usage of '_' prongs.
11688 if (has_under and !raw_operand_ty.isNonexhaustiveEnum(zcu)) {
11689 const msg = msg: {
11690 const msg = try sema.errMsg(
11691 src,
11692 "'_' prong only allowed when switching on non-exhaustive enums",
11693 .{},
11694 );
11695 errdefer msg.destroy(gpa);
11696 try sema.errNote(
11697 under_prong_src,
11698 msg,
11699 "'_' prong here",
11700 .{},
11701 );
11702 try sema.errNote(
11703 src,
11704 msg,
11705 "consider using 'else'",
11706 .{},
11707 );
11708 break :msg msg;
11529 const analyze_catch_all_body = analyze_body: {
11530 switch (catch_all_case) {
11531 .none => break :analyze_body false, // we still may want a safety check!
11532 .under => break :analyze_body true, // can't be a union anyway
11533 .@"else" => if (else_case.is_inline) break :analyze_body false,
11534 }
11535 if (union_originally) {
11536 const union_obj = zcu.typeToUnion(operand_ty).?;
11537 for (validated_switch.seen_enum_fields, 0..) |seen_field, field_i| {
11538 if (seen_field != null) continue;
11539 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_i]);
11540 if (!field_ty.isNoReturn(zcu)) break :analyze_body true;
11541 }
11542 break :analyze_body false;
11543 }
11544 if (err_set) {
11545 const else_err_ty = validated_switch.else_err_ty orelse {
11546 assert(else_case.is_simple_noreturn);
11547 break :analyze_body false;
11548 };
11549 if (else_err_ty.errorSetIsEmpty(zcu)) break :analyze_body false;
11550 }
11551 break :analyze_body true;
1170911552 };
11710 return sema.failWithOwnedErrorMsg(block, msg);
11711 }
1171211553
11713 // Validate for duplicate items, missing else prong, and invalid range.
11714 switch (cond_ty.zigTypeTag(zcu)) {
11715 .@"union" => unreachable, // handled in `switchCond`
11716 .@"enum" => {
11717 seen_enum_fields = try gpa.alloc(?LazySrcLoc, cond_ty.enumFieldCount(zcu));
11718 empty_enum = seen_enum_fields.len == 0 and !cond_ty.isNonexhaustiveEnum(zcu);
11719 @memset(seen_enum_fields, null);
11720 // `range_set` is used for non-exhaustive enum values that do not correspond to any tags.
11721
11722 for (absorbed_items, 0..) |item_ref, item_i| {
11723 _ = try sema.validateSwitchItemEnum(
11724 block,
11725 seen_enum_fields,
11726 &range_set,
11727 item_ref,
11728 cond_ty,
11729 block.src(.{ .switch_case_item = .{
11554 const catch_all_hint = hint: {
11555 if (analyze_catch_all_body) {
11556 const index, const body, const capture, const has_tag_capture = switch (catch_all_case) {
11557 .@"else" => .{ else_case.index, else_case.body, else_case.capture, else_case.has_tag_capture },
11558 .under => .{ under_case.index, under_case.body, under_case.capture, under_case.has_tag_capture },
11559 .none => unreachable,
11560 };
11561 break :hint try sema.analyzeSwitchProng(
11562 &case_block,
11563 operand,
11564 operand_ty,
11565 raw_operand_ty,
11566 body,
11567 block.src(.{ .switch_capture = .{
1173011568 .switch_node_offset = src_node_offset,
11731 .case_idx = .special_under,
11732 .item_idx = .{ .kind = .single, .index = @intCast(item_i) },
11569 .case_idx = index,
1173311570 } }),
11571 capture,
11572 has_tag_capture,
11573 .none,
11574 .special,
11575 validated_switch.else_err_ty,
11576 switch_inst,
11577 zir_switch,
1173411578 );
1173511579 }
11736 try sema.validateSwitchNoRange(block, @intCast(absorbed_ranges.len), cond_ty, src_node_offset);
11737
11738 var extra_index: usize = special_end;
11739 {
11740 var scalar_i: u32 = 0;
11741 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
11742 const item_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]);
11743 extra_index += 1;
11744 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
11745 extra_index += 1 + info.body_len;
11746
11747 case_vals.appendAssumeCapacity(try sema.validateSwitchItemEnum(
11748 block,
11749 seen_enum_fields,
11750 &range_set,
11751 item_ref,
11752 cond_ty,
11753 block.src(.{ .switch_case_item = .{
11754 .switch_node_offset = src_node_offset,
11755 .case_idx = .{ .kind = .scalar, .index = @intCast(scalar_i) },
11756 .item_idx = .{ .kind = .single, .index = 0 },
11757 } }),
11758 ));
11759 }
11580 // We still need a terminator in this block, but we have proven
11581 // that it is unreachable.
11582 if (case_block.wantSafety()) {
11583 try sema.zirDbgStmt(&case_block, cond_dbg_node_index);
11584 try sema.safetyPanic(&case_block, src, .corrupt_switch);
11585 } else {
11586 _ = try case_block.addNoOp(.unreach);
1176011587 }
11761 {
11762 var multi_i: u32 = 0;
11763 while (multi_i < multi_cases_len) : (multi_i += 1) {
11764 const items_len = sema.code.extra[extra_index];
11765 extra_index += 1;
11766 const ranges_len = sema.code.extra[extra_index];
11767 extra_index += 1;
11768 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
11769 extra_index += 1;
11770 const items = sema.code.refSlice(extra_index, items_len);
11771 extra_index += items_len + info.body_len;
11772
11773 try case_vals.ensureUnusedCapacity(gpa, items.len);
11774 for (items, 0..) |item_ref, item_i| {
11775 case_vals.appendAssumeCapacity(try sema.validateSwitchItemEnum(
11776 block,
11777 seen_enum_fields,
11778 &range_set,
11779 item_ref,
11780 cond_ty,
11781 block.src(.{ .switch_case_item = .{
11782 .switch_node_offset = src_node_offset,
11783 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) },
11784 .item_idx = .{ .kind = .single, .index = @intCast(item_i) },
11785 } }),
11786 ));
11787 }
11588 break :hint .cold; // Safety check / unreachable branches are cold.
11589 };
11590 try branch_hints.append(gpa, catch_all_hint);
11591 break :catch_all_extra @ptrCast(case_block.instructions.items);
11592 };
1178811593
11789 try sema.validateSwitchNoRange(block, ranges_len, cond_ty, src_node_offset);
11790 }
11594 assert(branch_hints.count == cases_len + 1); // +1 for catch-all hint
11595
11596 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr).@"struct".fields.len +
11597 branch_hints.bags.items.len +
11598 cases_extra.items.len +
11599 catch_all_extra.len);
11600 const payload_index = sema.addExtraAssumeCapacity(Air.SwitchBr{
11601 .cases_len = @intCast(cases_len),
11602 .else_body_len = @intCast(catch_all_extra.len),
11603 });
11604 sema.air_extra.appendSliceAssumeCapacity(branch_hints.bags.items);
11605 sema.air_extra.appendSliceAssumeCapacity(cases_extra.items);
11606 sema.air_extra.appendSliceAssumeCapacity(catch_all_extra);
11607
11608 const air_tag: Air.Inst.Tag = if (operand == .loop and merges.extra_insts.items.len > 0)
11609 .loop_switch_br
11610 else
11611 .switch_br;
11612 const air_switch_ref = try child_block.addInst(.{
11613 .tag = air_tag,
11614 .data = .{ .pl_op = .{
11615 .operand = cond_ref,
11616 .payload = payload_index,
11617 } },
11618 });
11619 try sema.fixupSwitchContinues(
11620 block,
11621 src,
11622 air_switch_ref,
11623 operand,
11624 operand_is_ref,
11625 item_ty,
11626 .normal,
11627 zir_switch.any_maybe_runtime_capture,
11628 merges,
11629 );
11630}
11631
11632/// This is the counterpart to `zirSwitchContinue`; replaces placeholder `br` insts
11633/// with their respective finalized inst pointing back at `switch_ref`.
11634fn fixupSwitchContinues(
11635 sema: *Sema,
11636 block: *Block,
11637 switch_src: LazySrcLoc,
11638 switch_ref: Air.Inst.Ref,
11639 operand: SwitchOperand,
11640 operand_is_ref: bool,
11641 item_ty: Type,
11642 mode: enum { normal, opv },
11643 any_non_inline_capture: bool,
11644 merges: *const Block.Merges,
11645) CompileError!void {
11646 const pt = sema.pt;
11647 const zcu = pt.zcu;
11648 const gpa = sema.gpa;
11649
11650 const air_tag = sema.air_instructions.items(.tag)[@intFromEnum(switch_ref.toIndex().?)];
11651 switch (air_tag) {
11652 .loop_switch_br, .switch_br => assert(mode == .normal),
11653 .loop, .block => assert(mode == .opv),
11654 else => unreachable,
11655 }
11656 switch (air_tag) {
11657 .loop_switch_br, .loop => assert(merges.extra_insts.items.len > 0),
11658 .switch_br, .block => assert(merges.extra_insts.items.len == 0),
11659 else => unreachable,
11660 }
11661
11662 for (merges.extra_insts.items, merges.extra_src_locs.items) |placeholder_inst, dispatch_src| {
11663 var replacement_block = block.makeSubBlock();
11664 defer replacement_block.instructions.deinit(gpa);
11665
11666 assert(sema.air_instructions.items(.tag)[@intFromEnum(placeholder_inst)] == .br);
11667 const new_operand_maybe_ref = sema.air_instructions.items(.data)[@intFromEnum(placeholder_inst)].br.operand;
11668
11669 if (any_non_inline_capture and mode != .opv) {
11670 _ = try replacement_block.addBinOp(.store, operand.loop.operand_alloc, new_operand_maybe_ref);
11671 }
11672
11673 const new_operand_val = if (operand_is_ref)
11674 try sema.analyzeLoad(&replacement_block, dispatch_src, new_operand_maybe_ref, dispatch_src)
11675 else
11676 new_operand_maybe_ref;
11677
11678 const new_cond = try sema.coerce(&replacement_block, item_ty, new_operand_val, dispatch_src);
11679
11680 if (zcu.backendSupportsFeature(.is_named_enum_value) and block.wantSafety() and
11681 item_ty.zigTypeTag(zcu) == .@"enum" and !item_ty.isNonexhaustiveEnum(zcu) and
11682 mode == .normal and !try sema.isComptimeKnown(new_cond))
11683 {
11684 const ok = try replacement_block.addUnOp(.is_named_enum_value, new_cond);
11685 try sema.addSafetyCheck(&replacement_block, switch_src, ok, .corrupt_switch);
11686 }
11687
11688 switch (mode) {
11689 .normal => {
11690 _ = try replacement_block.addInst(.{
11691 .tag = .switch_dispatch,
11692 .data = .{ .br = .{
11693 .block_inst = switch_ref.toIndex().?,
11694 .operand = new_cond,
11695 } },
11696 });
11697 },
11698 .opv => {
11699 _ = try replacement_block.addInst(.{
11700 .tag = .repeat,
11701 .data = .{ .repeat = .{
11702 .loop_inst = switch_ref.toIndex().?,
11703 } },
11704 });
11705 },
11706 }
11707
11708 if (replacement_block.instructions.items.len == 1) {
11709 // Optimization: we don't need a block!
11710 sema.air_instructions.set(
11711 @intFromEnum(placeholder_inst),
11712 sema.air_instructions.get(@intFromEnum(replacement_block.instructions.items[0])),
11713 );
11714 continue;
11715 }
11716
11717 // Replace placeholder with a block.
11718 // No `br` is needed as the block is a switch dispatch so necessarily `noreturn`.
11719 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).@"struct".fields.len +
11720 replacement_block.instructions.items.len);
11721 sema.air_instructions.set(@intFromEnum(placeholder_inst), .{
11722 .tag = .block,
11723 .data = .{ .ty_pl = .{
11724 .ty = .noreturn_type,
11725 .payload = sema.addExtraAssumeCapacity(Air.Block{
11726 .body_len = @intCast(replacement_block.instructions.items.len),
11727 }),
11728 } },
11729 });
11730 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(replacement_block.instructions.items));
11731 }
11732}
11733
11734const ValidatedSwitchBlock = struct {
11735 seen_enum_fields: []const ?LazySrcLoc,
11736 seen_errors: std.AutoHashMapUnmanaged(InternPool.NullTerminatedString, LazySrcLoc),
11737 seen_sparse_values: std.AutoHashMapUnmanaged(InternPool.Index, LazySrcLoc),
11738 seen_ranges: []const RangeSet.Range,
11739 true_src: ?LazySrcLoc,
11740 false_src: ?LazySrcLoc,
11741 void_src: ?LazySrcLoc,
11742
11743 case_vals: []const Air.Inst.Ref,
11744 else_case: Zir.UnwrappedSwitchBlock.Case.Else,
11745 under_case: Zir.UnwrappedSwitchBlock.Case.Under.Resolved,
11746 else_err_ty: ?Type,
11747
11748 fn iterateUnhandledItems(
11749 validated_switch: *const ValidatedSwitchBlock,
11750 /// May be `undefined` if `item_ty` isn't an `error_set`.
11751 error_names: InternPool.NullTerminatedString.Slice,
11752 /// May be `undefined` if `item_ty` isn't an `int`.
11753 min_int: Value,
11754 ) UnhandledIterator {
11755 return .{
11756 .next_idx = 0,
11757 .next_val = min_int,
11758 .error_names = error_names,
11759 .seen_enum_fields = validated_switch.seen_enum_fields,
11760 .seen_errors = &validated_switch.seen_errors,
11761 .seen_ranges = validated_switch.seen_ranges,
11762 .seen_true = validated_switch.true_src != null,
11763 .seen_false = validated_switch.false_src != null,
11764 .seen_void = validated_switch.void_src != null,
11765 };
11766 }
11767
11768 const UnhandledIterator = struct {
11769 next_idx: u32,
11770 next_val: ?Value,
11771 error_names: InternPool.NullTerminatedString.Slice,
11772 seen_enum_fields: []const ?LazySrcLoc,
11773 seen_errors: *const std.AutoHashMapUnmanaged(InternPool.NullTerminatedString, LazySrcLoc),
11774 seen_ranges: []const RangeSet.Range,
11775 seen_true: bool,
11776 seen_false: bool,
11777 seen_void: bool,
11778
11779 fn next(it: *UnhandledIterator, sema: *Sema, item_ty: Type) CompileError!?Value {
11780 const pt = sema.pt;
11781 const zcu = pt.zcu;
11782 const ip = &zcu.intern_pool;
11783 switch (item_ty.zigTypeTag(zcu)) {
11784 .@"union" => unreachable,
11785 .@"enum" => {
11786 for (it.seen_enum_fields[it.next_idx..], it.next_idx..) |seen_field, field_i| {
11787 if (seen_field != null) continue;
11788 it.next_idx = @intCast(field_i + 1);
11789 return try pt.enumValueFieldIndex(item_ty, @intCast(field_i));
11790 }
11791 return null;
11792 },
11793 .error_set => {
11794 for (it.error_names.get(ip)[it.next_idx..], it.next_idx..) |err_name, name_i| {
11795 if (it.seen_errors.contains(err_name)) continue;
11796 it.next_idx = @intCast(name_i + 1);
11797 return .fromInterned(try pt.intern(.{ .err = .{
11798 .ty = item_ty.toIntern(),
11799 .name = err_name,
11800 } }));
11801 }
11802 return null;
11803 },
11804 .int => {
11805 var cur = it.next_val orelse return null;
11806 while (it.next_idx < it.seen_ranges.len and
11807 cur.eql(it.seen_ranges[it.next_idx].first, item_ty, zcu))
11808 {
11809 defer it.next_idx += 1;
11810 const incr = try arith.incrementDefinedInt(
11811 sema,
11812 item_ty,
11813 it.seen_ranges[it.next_idx].last,
11814 );
11815 if (incr.overflow) {
11816 it.next_val = null;
11817 return null;
11818 }
11819 cur = incr.val;
11820 }
11821 const incr = try arith.incrementDefinedInt(sema, item_ty, cur);
11822 it.next_val = if (incr.overflow) null else incr.val;
11823 return cur;
11824 },
11825 .bool => {
11826 if (!it.seen_true) {
11827 it.seen_true = true;
11828 return .true;
11829 }
11830 if (!it.seen_false) {
11831 it.seen_false = true;
11832 return .false;
11833 }
11834 return null;
11835 },
11836 .void => {
11837 if (!it.seen_void) {
11838 it.seen_void = true;
11839 return .void;
11840 }
11841 return null;
11842 },
11843 else => unreachable, // item type is not enumerable
11844 }
11845 }
11846 };
11847};
11848
11849/// Validates operand type and `else`/`_` prong usage, resolves all prong items
11850/// and checks them for duplicates/invalid ranges. Does not emit into `block`.
11851/// Reserves inst map space for all placeholders associated with `zir_switch`.
11852/// Contents of returned `ValidatedSwitchBlock` belong to `sema.arena`.
11853fn validateSwitchBlock(
11854 sema: *Sema,
11855 block: *Block,
11856 raw_operand: Air.Inst.Ref,
11857 operand_is_ref: bool,
11858 switch_inst: Zir.Inst.Index,
11859 zir_switch: *const Zir.UnwrappedSwitchBlock,
11860) CompileError!ValidatedSwitchBlock {
11861 const pt = sema.pt;
11862 const zcu = pt.zcu;
11863 const ip = &zcu.intern_pool;
11864 const gpa = sema.gpa;
11865 const arena = sema.arena;
11866
11867 const src_node_offset = zir_switch.switch_src_node_offset;
11868 const src = block.nodeOffset(src_node_offset);
11869 const operand_src = block.src(.{ .node_offset_switch_operand = src_node_offset });
11870 const else_prong_src = block.src(.{ .node_offset_switch_else_prong = src_node_offset });
11871 const under_prong_src = block.src(.{ .node_offset_switch_under_prong = src_node_offset });
11872 var extra_index = zir_switch.end;
11873
11874 // We want to map values to our placeholders later on.
11875 if (zir_switch.payload_capture_placeholder.unwrap()) |payload_capture_inst| {
11876 assert(payload_capture_inst != switch_inst); // malformed zir
11877 try sema.inst_map.ensureSpaceForInstructions(gpa, &.{payload_capture_inst});
11878 }
11879 if (zir_switch.tag_capture_placeholder.unwrap()) |tag_capture_inst| {
11880 assert(tag_capture_inst != switch_inst); // malformed zir
11881 try sema.inst_map.ensureSpaceForInstructions(gpa, &.{tag_capture_inst});
11882 }
11883
11884 const operand_ty: Type, const item_ty: Type = check_operand: {
11885 const operand_ty = operand_ty: {
11886 const raw_operand_ty = sema.typeOf(raw_operand);
11887 if (operand_is_ref) {
11888 try sema.checkPtrType(block, operand_src, raw_operand_ty, false);
11889 break :operand_ty raw_operand_ty.childType(zcu);
11890 }
11891 break :operand_ty raw_operand_ty;
11892 };
11893
11894 const item_ty: Type = item_ty: {
11895 switch (operand_ty.zigTypeTag(zcu)) {
11896 .@"enum",
11897 .error_set,
11898 .int,
11899 .comptime_int,
11900 .type,
11901 .enum_literal,
11902 .@"fn",
11903 .bool,
11904 .void,
11905 => break :item_ty operand_ty,
11906
11907 .@"union" => {
11908 try operand_ty.resolveFields(pt);
11909 const enum_ty = operand_ty.unionTagType(zcu) orelse {
11910 return sema.failWithOwnedErrorMsg(block, msg: {
11911 const msg = try sema.errMsg(operand_src, "switch on union with no attached enum", .{});
11912 errdefer msg.destroy(sema.gpa);
11913 if (operand_ty.srcLocOrNull(zcu)) |union_src| {
11914 try sema.errNote(union_src, msg, "consider 'union(enum)' here", .{});
11915 }
11916 break :msg msg;
11917 });
11918 };
11919 break :item_ty enum_ty;
11920 },
11921
11922 .pointer => {
11923 if (!operand_ty.isSlice(zcu)) {
11924 break :item_ty operand_ty;
11925 }
11926 },
11927
11928 else => {},
11929 }
11930 return sema.fail(block, operand_src, "switch on type '{f}'", .{operand_ty.fmt(pt)});
11931 };
11932
11933 if (zir_switch.has_continue and !block.isComptime()) {
11934 if (try operand_ty.comptimeOnlySema(pt)) {
11935 // Even if the operand is comptime-known, this `switch` is runtime.
11936 return sema.failWithOwnedErrorMsg(block, msg: {
11937 const msg = try sema.errMsg(operand_src, "operand of switch loop has comptime-only type '{f}'", .{operand_ty.fmt(pt)});
11938 errdefer msg.destroy(gpa);
11939 try sema.errNote(operand_src, msg, "switch loops are evaluated at runtime outside of comptime scopes", .{});
11940 try sema.explainWhyTypeIsComptime(msg, operand_src, operand_ty);
11941 break :msg msg;
11942 });
1179111943 }
11944 try sema.validateRuntimeValue(block, operand_src, raw_operand);
11945 }
11946
11947 break :check_operand .{ operand_ty, item_ty };
11948 };
11949
11950 const has_else = zir_switch.else_case != null;
11951 const has_under = zir_switch.under_case != .none;
11952
11953 // Validate usage of '_' prongs.
11954 if (has_under and !operand_ty.isNonexhaustiveEnum(zcu)) {
11955 const msg = msg: {
11956 const msg = try sema.errMsg(
11957 src,
11958 "'_' prong only allowed when switching on non-exhaustive enums",
11959 .{},
11960 );
11961 errdefer msg.destroy(gpa);
11962 try sema.errNote(
11963 under_prong_src,
11964 msg,
11965 "'_' prong here",
11966 .{},
11967 );
11968 try sema.errNote(
11969 src,
11970 msg,
11971 "consider using 'else'",
11972 .{},
11973 );
11974 break :msg msg;
11975 };
11976 return sema.failWithOwnedErrorMsg(block, msg);
11977 }
11978
11979 var case_vals: std.ArrayList(Air.Inst.Ref) = .empty;
11980 try case_vals.ensureUnusedCapacity(arena, zir_switch.item_infos.len);
11981
11982 // Duplicate checking variables later also used for `inline else`.
11983 var seen_enum_fields: []?LazySrcLoc = &.{};
11984 var seen_errors: std.AutoHashMapUnmanaged(InternPool.NullTerminatedString, LazySrcLoc) = .empty;
11985 var seen_sparse_values: std.AutoHashMapUnmanaged(InternPool.Index, LazySrcLoc) = .empty;
11986 var range_set: RangeSet = .empty;
11987 var true_src: ?LazySrcLoc = null;
11988 var false_src: ?LazySrcLoc = null;
11989 var void_src: ?LazySrcLoc = null;
11990
11991 var else_err_ty: ?Type = null;
11992
11993 const else_case = zir_switch.else_case orelse undefined;
11994 var under_case = zir_switch.under_case.resolve() orelse undefined;
11995
11996 switch (item_ty.zigTypeTag(zcu)) {
11997 .@"union" => unreachable,
11998 .@"enum" => {
11999 seen_enum_fields = try arena.alloc(?LazySrcLoc, item_ty.enumFieldCount(zcu));
12000 @memset(seen_enum_fields, null);
12001 // `range_set` is used for non-exhaustive enum values that do not
12002 // correspond to any tags. Since this is rare, we only allocate on
12003 // demand in `validateSwitchItem`.
12004 },
12005 .error_set => {
12006 try seen_errors.ensureUnusedCapacity(arena, zir_switch.totalItemsLen());
12007 },
12008 .int, .comptime_int => {
12009 try range_set.ensureUnusedCapacity(arena, zir_switch.totalItemsLen());
12010 },
12011 .enum_literal, .@"fn", .pointer, .type => {
12012 try seen_sparse_values.ensureUnusedCapacity(arena, zir_switch.totalItemsLen());
12013 },
12014 .bool, .void => {},
12015
12016 else => unreachable,
12017 }
12018
12019 // Validate for duplicate items and invalid ranges.
12020 var case_it = zir_switch.iterateCases();
12021 while (case_it.next()) |case| {
12022 const prong_info = case.prong_info;
12023 const is_under = case.isUnder();
12024 if (is_under) {
12025 assert(!prong_info.is_inline);
12026 under_case = .{
12027 .index = case.index,
12028 .body = sema.code.bodySlice(extra_index, prong_info.body_len),
12029 .capture = prong_info.capture,
12030 .has_tag_capture = prong_info.has_tag_capture,
12031 };
12032 }
12033 extra_index += prong_info.body_len;
12034 for (case.item_infos, 0..) |item_info, item_i| {
12035 const item_src = block.src(.{ .switch_case_item = .{
12036 .switch_node_offset = src_node_offset,
12037 .case_idx = case.index,
12038 .item_idx = .{ .kind = .single, .value = @intCast(item_i) },
12039 } });
12040 const item, extra_index = try sema.resolveSwitchItem(block, item_src, item_ty, item_info, extra_index, switch_inst, prong_info.is_comptime_unreach, prong_info.is_inline);
12041 try sema.validateSwitchItemOrRange(block, item_src, item.val, null, item_ty, seen_enum_fields, &seen_errors, &seen_sparse_values, &range_set, &true_src, &false_src, &void_src);
12042 if (!is_under) case_vals.appendAssumeCapacity(item.ref);
12043 }
12044 for (case.range_infos, 0..) |range_info, range_i| {
12045 const range_offset: LazySrcLoc.Offset.SwitchItem = .{
12046 .switch_node_offset = src_node_offset,
12047 .case_idx = case.index,
12048 .item_idx = .{ .kind = .range, .value = @intCast(range_i) },
12049 };
12050 const range_src = block.src(.{ .switch_case_item = range_offset });
12051 const first_src = block.src(.{ .switch_case_item_range_first = range_offset });
12052 const last_src = block.src(.{ .switch_case_item_range_last = range_offset });
12053 const first_item, extra_index = try sema.resolveSwitchItem(block, first_src, item_ty, range_info[0], extra_index, switch_inst, prong_info.is_comptime_unreach, prong_info.is_inline);
12054 const last_item, extra_index = try sema.resolveSwitchItem(block, last_src, item_ty, range_info[1], extra_index, switch_inst, prong_info.is_comptime_unreach, prong_info.is_inline);
12055 try sema.validateSwitchItemOrRange(block, range_src, first_item.val, last_item.val, item_ty, seen_enum_fields, &seen_errors, &seen_sparse_values, &range_set, &true_src, &false_src, &void_src);
12056 if (!is_under) case_vals.appendSliceAssumeCapacity(&.{ first_item.ref, last_item.ref });
12057 }
12058 }
12059
12060 switch (item_ty.zigTypeTag(zcu)) {
12061 .@"union" => unreachable,
12062 .int, .comptime_int => {},
12063 else => if (zir_switch.anyRanges()) {
12064 const range_src = block.src(.{ .node_offset_switch_range = src_node_offset });
12065 const msg = msg: {
12066 const msg = try sema.errMsg(
12067 operand_src,
12068 "ranges not allowed when switching on type '{f}'",
12069 .{operand_ty.fmt(sema.pt)},
12070 );
12071 errdefer msg.destroy(sema.gpa);
12072 try sema.errNote(
12073 range_src,
12074 msg,
12075 "range here",
12076 .{},
12077 );
12078 break :msg msg;
12079 };
12080 return sema.failWithOwnedErrorMsg(block, msg);
12081 },
12082 }
12083
12084 // Validate for missing special prongs.
12085 switch (item_ty.zigTypeTag(zcu)) {
12086 .@"union" => unreachable,
12087 .@"enum" => {
1179212088 const all_tags_handled = for (seen_enum_fields) |seen_src| {
1179312089 if (seen_src == null) break false;
1179412090 } else true;
1179512091
1179612092 if (has_else) {
1179712093 if (all_tags_handled) {
11798 if (cond_ty.isNonexhaustiveEnum(zcu)) {
12094 if (item_ty.isNonexhaustiveEnum(zcu)) {
1179912095 if (has_under) return sema.fail(
1180012096 block,
1180112097 else_prong_src,
......@@ -11820,9 +12116,9 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
1182012116 for (seen_enum_fields, 0..) |seen_src, i| {
1182112117 if (seen_src != null) continue;
1182212118
11823 const field_name = cond_ty.enumFieldName(i, zcu);
12119 const field_name = item_ty.enumFieldName(i, zcu);
1182412120 try sema.addFieldErrNote(
11825 cond_ty,
12121 item_ty,
1182612122 i,
1182712123 msg,
1182812124 "unhandled enumeration value: '{f}'",
......@@ -11830,15 +12126,17 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
1183012126 );
1183112127 }
1183212128 try sema.errNote(
11833 cond_ty.srcLoc(zcu),
12129 item_ty.srcLoc(zcu),
1183412130 msg,
1183512131 "enum '{f}' declared here",
11836 .{cond_ty.fmt(pt)},
12132 .{item_ty.fmt(pt)},
1183712133 );
1183812134 break :msg msg;
1183912135 };
1184012136 return sema.failWithOwnedErrorMsg(block, msg);
11841 } else if (special_prongs == .none and cond_ty.isNonexhaustiveEnum(zcu) and !union_originally) {
12137 } else if (!has_else and !has_under and
12138 item_ty.isNonexhaustiveEnum(zcu) and operand_ty.zigTypeTag(zcu) != .@"union")
12139 {
1184212140 return sema.fail(
1184312141 block,
1184412142 src,
......@@ -11847,101 +12145,83 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
1184712145 );
1184812146 }
1184912147 },
11850 .error_set => else_error_ty = try validateErrSetSwitch(
11851 sema,
11852 block,
11853 &seen_errors,
11854 &case_vals,
11855 cond_ty,
11856 inst_data,
11857 scalar_cases_len,
11858 multi_cases_len,
11859 .{ .body = special_else.body, .end = special_else.end, .src = else_prong_src },
11860 has_else,
11861 ),
11862 .int, .comptime_int => {
11863 var extra_index: usize = special_end;
11864 {
11865 var scalar_i: u32 = 0;
11866 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
11867 const item_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]);
11868 extra_index += 1;
11869 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
11870 extra_index += 1 + info.body_len;
11871
11872 case_vals.appendAssumeCapacity(try sema.validateSwitchItemInt(
11873 block,
11874 &range_set,
11875 item_ref,
11876 cond_ty,
11877 block.src(.{ .switch_case_item = .{
11878 .switch_node_offset = src_node_offset,
11879 .case_idx = .{ .kind = .scalar, .index = @intCast(scalar_i) },
11880 .item_idx = .{ .kind = .single, .index = 0 },
11881 } }),
11882 ));
11883 }
11884 }
11885 {
11886 var multi_i: u32 = 0;
11887 while (multi_i < multi_cases_len) : (multi_i += 1) {
11888 const items_len = sema.code.extra[extra_index];
11889 extra_index += 1;
11890 const ranges_len = sema.code.extra[extra_index];
11891 extra_index += 1;
11892 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
11893 extra_index += 1;
11894 const items = sema.code.refSlice(extra_index, items_len);
11895 extra_index += items_len;
11896
11897 try case_vals.ensureUnusedCapacity(gpa, items.len);
11898 for (items, 0..) |item_ref, item_i| {
11899 case_vals.appendAssumeCapacity(try sema.validateSwitchItemInt(
12148 .error_set => {
12149 else_err_ty = ty: switch (try sema.resolveInferredErrorSetTy(block, src, item_ty.toIntern())) {
12150 .anyerror_type => {
12151 if (!has_else) {
12152 return sema.fail(
1190012153 block,
11901 &range_set,
11902 item_ref,
11903 cond_ty,
11904 block.src(.{ .switch_case_item = .{
11905 .switch_node_offset = src_node_offset,
11906 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) },
11907 .item_idx = .{ .kind = .single, .index = @intCast(item_i) },
11908 } }),
11909 ));
12154 src,
12155 "else prong required when switching on type 'anyerror'",
12156 .{},
12157 );
1191012158 }
11911
11912 try case_vals.ensureUnusedCapacity(gpa, 2 * ranges_len);
11913 var range_i: u32 = 0;
11914 while (range_i < ranges_len) : (range_i += 1) {
11915 const item_first: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]);
11916 extra_index += 1;
11917 const item_last: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]);
11918 extra_index += 1;
11919
11920 const vals = try sema.validateSwitchRange(
12159 break :ty .anyerror;
12160 },
12161 else => |err_set_ty_index| {
12162 const error_names = ip.indexToKey(err_set_ty_index).error_set_type.names;
12163 var maybe_msg: ?*Zcu.ErrorMsg = null;
12164 errdefer if (maybe_msg) |msg| msg.destroy(sema.gpa);
12165
12166 var seen_errors_from_set: u32 = 0;
12167 for (error_names.get(ip)) |error_name| {
12168 if (seen_errors.contains(error_name)) {
12169 seen_errors_from_set += 1;
12170 } else if (!has_else) {
12171 const msg = maybe_msg orelse blk: {
12172 maybe_msg = try sema.errMsg(
12173 src,
12174 "switch must handle all possibilities",
12175 .{},
12176 );
12177 break :blk maybe_msg.?;
12178 };
12179
12180 try sema.errNote(
12181 src,
12182 msg,
12183 "unhandled error value: 'error.{f}'",
12184 .{error_name.fmt(ip)},
12185 );
12186 }
12187 }
12188
12189 if (maybe_msg) |msg| {
12190 maybe_msg = null;
12191 try sema.addDeclaredHereNote(msg, operand_ty);
12192 return sema.failWithOwnedErrorMsg(block, msg);
12193 }
12194
12195 if (has_else and seen_errors_from_set == error_names.len) {
12196 // This prong is unreachable anyway so we don't need its
12197 // error set type, but we still allow it to exist.
12198 if (else_case.is_simple_noreturn) break :ty null;
12199 return sema.fail(
1192112200 block,
11922 &range_set,
11923 item_first,
11924 item_last,
11925 cond_ty,
11926 block.src(.{ .switch_case_item = .{
11927 .switch_node_offset = src_node_offset,
11928 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) },
11929 .item_idx = .{ .kind = .range, .index = @intCast(range_i) },
11930 } }),
12201 else_prong_src,
12202 "unreachable else prong; all cases already handled",
12203 .{},
1193112204 );
11932 case_vals.appendAssumeCapacity(vals[0]);
11933 case_vals.appendAssumeCapacity(vals[1]);
1193412205 }
1193512206
11936 extra_index += info.body_len;
11937 }
11938 }
11939
12207 var names: InferredErrorSet.NameMap = .{};
12208 try names.ensureUnusedCapacity(sema.arena, error_names.len);
12209 for (error_names.get(ip)) |error_name| {
12210 if (seen_errors.contains(error_name)) continue;
12211 names.putAssumeCapacityNoClobber(error_name, {});
12212 }
12213 // No need to keep the hash map metadata correct; here we
12214 // extract the (sorted) keys only.
12215 break :ty try pt.errorSetFromUnsortedNames(names.keys());
12216 },
12217 };
12218 },
12219 .int, .comptime_int => |type_tag| {
1194012220 check_range: {
11941 if (cond_ty.zigTypeTag(zcu) == .int) {
11942 const min_int = try cond_ty.minInt(pt, cond_ty);
11943 const max_int = try cond_ty.maxInt(pt, cond_ty);
11944 if (try range_set.spans(min_int.toIntern(), max_int.toIntern())) {
12221 if (type_tag == .int) {
12222 const min_int = try item_ty.minInt(pt, item_ty);
12223 const max_int = try item_ty.maxInt(pt, item_ty);
12224 if (try range_set.spans(arena, min_int, max_int, item_ty, zcu)) {
1194512225 if (has_else) {
1194612226 return sema.fail(
1194712227 block,
......@@ -11953,7 +12233,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
1195312233 break :check_range;
1195412234 }
1195512235 }
11956 if (special_prongs == .none) {
12236 if (!has_else) {
1195712237 return sema.fail(
1195812238 block,
1195912239 src,
......@@ -11963,61 +12243,24 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
1196312243 }
1196412244 }
1196512245 },
11966 .bool => {
11967 var extra_index: usize = special_end;
11968 {
11969 var scalar_i: u32 = 0;
11970 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
11971 const item_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]);
11972 extra_index += 1;
11973 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
11974 extra_index += 1 + info.body_len;
11975
11976 case_vals.appendAssumeCapacity(try sema.validateSwitchItemBool(
11977 block,
11978 &true_count,
11979 &false_count,
11980 item_ref,
11981 block.src(.{ .switch_case_item = .{
11982 .switch_node_offset = src_node_offset,
11983 .case_idx = .{ .kind = .scalar, .index = @intCast(scalar_i) },
11984 .item_idx = .{ .kind = .single, .index = 0 },
11985 } }),
11986 ));
11987 }
11988 }
11989 {
11990 var multi_i: u32 = 0;
11991 while (multi_i < multi_cases_len) : (multi_i += 1) {
11992 const items_len = sema.code.extra[extra_index];
11993 extra_index += 1;
11994 const ranges_len = sema.code.extra[extra_index];
11995 extra_index += 1;
11996 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
11997 extra_index += 1;
11998 const items = sema.code.refSlice(extra_index, items_len);
11999 extra_index += items_len + info.body_len;
12000
12001 try case_vals.ensureUnusedCapacity(gpa, items.len);
12002 for (items, 0..) |item_ref, item_i| {
12003 case_vals.appendAssumeCapacity(try sema.validateSwitchItemBool(
12004 block,
12005 &true_count,
12006 &false_count,
12007 item_ref,
12008 block.src(.{ .switch_case_item = .{
12009 .switch_node_offset = src_node_offset,
12010 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) },
12011 .item_idx = .{ .kind = .single, .index = @intCast(item_i) },
12012 } }),
12013 ));
12014 }
12015
12016 try sema.validateSwitchNoRange(block, ranges_len, cond_ty, src_node_offset);
12017 }
12246 .enum_literal, .@"fn", .pointer, .type => {
12247 if (!has_else) {
12248 return sema.fail(
12249 block,
12250 src,
12251 "else prong required when switching on type '{f}'",
12252 .{item_ty.fmt(pt)},
12253 );
1201812254 }
12255 },
12256 .bool, .void => |type_tag| {
12257 const all_values_handled = switch (type_tag) {
12258 .bool => true_src != null and false_src != null,
12259 .void => void_src != null,
12260 else => unreachable,
12261 };
1201912262 if (has_else) {
12020 if (true_count + false_count == 2) {
12263 if (all_values_handled) {
1202112264 return sema.fail(
1202212265 block,
1202312266 else_prong_src,
......@@ -12026,7 +12269,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
1202612269 );
1202712270 }
1202812271 } else {
12029 if (true_count + false_count < 2) {
12272 if (!all_values_handled) {
1203012273 return sema.fail(
1203112274 block,
1203212275 src,
......@@ -12036,1775 +12279,1073 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
1203612279 }
1203712280 }
1203812281 },
12039 .enum_literal, .void, .@"fn", .pointer, .type => {
12040 if (!has_else) {
12041 return sema.fail(
12042 block,
12043 src,
12044 "else prong required when switching on type '{f}'",
12045 .{cond_ty.fmt(pt)},
12046 );
12047 }
12048
12049 var seen_values = ValueSrcMap{};
12050 defer seen_values.deinit(gpa);
12051
12052 var extra_index: usize = special_end;
12053 {
12054 var scalar_i: u32 = 0;
12055 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
12056 const item_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]);
12057 extra_index += 1;
12058 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
12059 extra_index += 1;
12060 extra_index += info.body_len;
12061
12062 case_vals.appendAssumeCapacity(try sema.validateSwitchItemSparse(
12063 block,
12064 &seen_values,
12065 item_ref,
12066 cond_ty,
12067 block.src(.{ .switch_case_item = .{
12068 .switch_node_offset = src_node_offset,
12069 .case_idx = .{ .kind = .scalar, .index = @intCast(scalar_i) },
12070 .item_idx = .{ .kind = .single, .index = 0 },
12071 } }),
12072 ));
12073 }
12074 }
12075 {
12076 var multi_i: u32 = 0;
12077 while (multi_i < multi_cases_len) : (multi_i += 1) {
12078 const items_len = sema.code.extra[extra_index];
12079 extra_index += 1;
12080 const ranges_len = sema.code.extra[extra_index];
12081 extra_index += 1;
12082 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
12083 extra_index += 1;
12084 const items = sema.code.refSlice(extra_index, items_len);
12085 extra_index += items_len + info.body_len;
12086
12087 try case_vals.ensureUnusedCapacity(gpa, items.len);
12088 for (items, 0..) |item_ref, item_i| {
12089 case_vals.appendAssumeCapacity(try sema.validateSwitchItemSparse(
12090 block,
12091 &seen_values,
12092 item_ref,
12093 cond_ty,
12094 block.src(.{ .switch_case_item = .{
12095 .switch_node_offset = src_node_offset,
12096 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) },
12097 .item_idx = .{ .kind = .single, .index = @intCast(item_i) },
12098 } }),
12099 ));
12100 }
12101
12102 try sema.validateSwitchNoRange(block, ranges_len, cond_ty, src_node_offset);
12103 }
12104 }
12105 },
12106
12107 .error_union,
12108 .noreturn,
12109 .array,
12110 .@"struct",
12111 .undefined,
12112 .null,
12113 .optional,
12114 .@"opaque",
12115 .vector,
12116 .frame,
12117 .@"anyframe",
12118 .comptime_float,
12119 .float,
12120 => return sema.fail(block, operand_src, "invalid switch operand type '{f}'", .{
12121 raw_operand_ty.fmt(pt),
12122 }),
12282 else => unreachable,
1212312283 }
1212412284
12125 var special_members_only: ?SpecialProng = null;
12126 var special_members_only_src: LazySrcLoc = undefined;
12127 const special_generic, const special_generic_src = if (has_under) b: {
12128 if (has_else) {
12129 special_members_only = special_else;
12130 special_members_only_src = else_prong_src;
12131 }
12132 break :b .{ special_under, under_prong_src };
12133 } else .{ special_else, else_prong_src };
12285 return .{
12286 .seen_enum_fields = seen_enum_fields,
12287 .seen_errors = seen_errors,
12288 .seen_sparse_values = seen_sparse_values,
12289 .seen_ranges = range_set.ranges.items,
12290 .true_src = true_src,
12291 .false_src = false_src,
12292 .void_src = void_src,
1213412293
12135 const spa: SwitchProngAnalysis = .{
12136 .sema = sema,
12137 .parent_block = block,
12138 .operand = operand,
12139 .else_error_ty = else_error_ty,
12140 .switch_block_inst = inst,
12141 .tag_capture_inst = tag_capture_inst,
12294 .case_vals = case_vals.items,
12295 .else_case = else_case,
12296 .under_case = under_case,
12297 .else_err_ty = else_err_ty,
1214212298 };
12299}
1214312300
12144 const block_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len);
12145 try sema.air_instructions.append(gpa, .{
12146 .tag = .block,
12147 .data = undefined,
12148 });
12149 var label: Block.Label = .{
12150 .zir_block = inst,
12151 .merges = .{
12152 .src_locs = .{},
12153 .results = .{},
12154 .br_list = .{},
12155 .block_inst = block_inst,
12156 },
12157 };
12301fn resolveSwitchBlock(
12302 sema: *Sema,
12303 block: *Block,
12304 child_block: *Block,
12305 operand: SwitchOperand,
12306 raw_operand_ty: Type,
12307 maybe_lazy_cond_val: Value,
12308 catch_all_case: CatchAllSwitchCase,
12309 else_is_named_only: bool,
12310 merges: *Block.Merges,
12311 switch_inst: Zir.Inst.Index,
12312 zir_switch: *const Zir.UnwrappedSwitchBlock,
12313 validated_switch: *const ValidatedSwitchBlock,
12314) CompileError!Air.Inst.Ref {
12315 const pt = sema.pt;
12316 const zcu = pt.zcu;
1215812317
12159 var child_block: Block = .{
12160 .parent = block,
12161 .sema = sema,
12162 .namespace = block.namespace,
12163 .instructions = .{},
12164 .label = &label,
12165 .inlining = block.inlining,
12166 .comptime_reason = block.comptime_reason,
12167 .is_typeof = block.is_typeof,
12168 .c_import_buf = block.c_import_buf,
12169 .runtime_cond = block.runtime_cond,
12170 .runtime_loop = block.runtime_loop,
12171 .runtime_index = block.runtime_index,
12172 .want_safety = block.want_safety,
12173 .error_return_trace_index = block.error_return_trace_index,
12174 .src_base_inst = block.src_base_inst,
12175 .type_name_ctx = block.type_name_ctx,
12318 const switch_node_offset = zir_switch.switch_src_node_offset;
12319
12320 const operand_ty = sema.typeOf(operand.simple.by_val);
12321 const item_ty = switch (operand_ty.zigTypeTag(zcu)) {
12322 .@"union" => operand_ty.unionTagType(zcu).?,
12323 else => operand_ty,
1217612324 };
12177 const merges = &child_block.label.?.merges;
12178 defer child_block.instructions.deinit(gpa);
12179 defer merges.deinit(gpa);
12325 const union_originally = operand_ty.zigTypeTag(zcu) == .@"union";
12326 const err_set = item_ty.zigTypeTag(zcu) == .error_set;
1218012327
12181 if (scalar_cases_len + multi_cases_len == 0 and
12182 special_members_only == null and
12183 !special_generic.is_inline)
12184 {
12185 if (empty_enum) {
12186 return .void_value;
12187 }
12188 if (special_prongs == .none) {
12189 return sema.fail(block, src, "switch must handle all possibilities", .{});
12328 const cond_ref = operand.simple.cond;
12329 // We have to resolve lazy values to ensure that comparisons with switch
12330 // prong items don't produce false negatives.
12331 const cond_val = try sema.resolveLazyValue(maybe_lazy_cond_val);
12332
12333 const case_vals = validated_switch.case_vals;
12334 var case_val_idx: usize = 0;
12335 var extra_index = zir_switch.end;
12336 var case_it = zir_switch.iterateCases();
12337 while (case_it.next()) |case| {
12338 if (case.isUnder()) { // we'll deal with this later
12339 extra_index += case.prong_info.body_len;
12340 for (case.item_infos) |item_info| {
12341 if (item_info.bodyLen()) |body_len| extra_index += body_len;
12342 }
12343 for (case.range_infos) |range_info| {
12344 if (range_info[0].bodyLen()) |body_len| extra_index += body_len;
12345 if (range_info[1].bodyLen()) |body_len| extra_index += body_len;
12346 }
12347 continue;
1219012348 }
12191 const init_cond = switch (operand) {
12192 .simple => |s| s.cond,
12193 .loop => |l| l.init_cond,
12194 };
12195 if (zcu.backendSupportsFeature(.is_named_enum_value) and block.wantSafety() and
12196 raw_operand_ty.zigTypeTag(zcu) == .@"enum" and !raw_operand_ty.isNonexhaustiveEnum(zcu))
12197 {
12198 try sema.zirDbgStmt(block, cond_dbg_node_index);
12199 const ok = try block.addUnOp(.is_named_enum_value, init_cond);
12200 try sema.addSafetyCheck(block, src, ok, .corrupt_switch);
12349
12350 const prong_info = case.prong_info;
12351 const prong_body = sema.code.bodySlice(extra_index, prong_info.body_len);
12352 extra_index += prong_body.len;
12353 for (case.item_infos) |item_info| {
12354 if (item_info.bodyLen()) |body_len| extra_index += body_len;
1220112355 }
12202 if (err_set and try sema.maybeErrorUnwrap(block, special_generic.body, init_cond, operand_src, false)) {
12203 return .unreachable_value;
12356 for (case.range_infos) |range_info| {
12357 if (range_info[0].bodyLen()) |body_len| extra_index += body_len;
12358 if (range_info[1].bodyLen()) |body_len| extra_index += body_len;
1220412359 }
12205 }
1220612360
12207 switch (operand) {
12208 .loop => {}, // always runtime; evaluation in comptime scope uses `simple`
12209 .simple => |s| {
12210 if (try sema.resolveDefinedValue(&child_block, src, s.cond)) |cond_val| {
12211 return resolveSwitchComptimeLoop(
12212 sema,
12213 spa,
12214 &child_block,
12215 if (operand_is_ref)
12216 sema.typeOf(s.by_ref)
12217 else
12218 raw_operand_ty,
12219 cond_ty,
12220 cond_val,
12221 src_node_offset,
12222 special_members_only,
12223 special_generic,
12224 has_under,
12225 case_vals,
12226 scalar_cases_len,
12227 multi_cases_len,
12228 err_set,
12229 empty_enum,
12230 operand_is_ref,
12361 const item_refs = case_vals[case_val_idx..][0..case.item_infos.len];
12362 case_val_idx += item_refs.len;
12363 const range_refs: []const [2]Air.Inst.Ref = @ptrCast(case_vals[case_val_idx..][0 .. 2 * case.range_infos.len]);
12364 case_val_idx += 2 * range_refs.len;
12365 for (item_refs) |item_ref| {
12366 const item_val = sema.resolveConstDefinedValue(child_block, .unneeded, item_ref, undefined) catch unreachable;
12367 if (cond_val.eql(item_val, item_ty, zcu)) {
12368 if (err_set) try sema.maybeErrorUnwrapComptime(child_block, prong_body, cond_ref);
12369 if (union_originally and operand_ty.unionFieldType(item_val, zcu).?.isNoReturn(zcu)) {
12370 // This prong should be unreachable!
12371 return .unreachable_value;
12372 }
12373 return sema.resolveSwitchProng(
12374 block,
12375 child_block,
12376 operand,
12377 raw_operand_ty,
12378 prong_body,
12379 block.src(.{ .switch_capture = .{
12380 .switch_node_offset = switch_node_offset,
12381 .case_idx = case.index,
12382 } }),
12383 prong_info.capture,
12384 prong_info.has_tag_capture,
12385 if (prong_info.is_inline) cond_ref else .none,
12386 .{ .item_refs = item_refs },
12387 validated_switch.else_err_ty,
12388 merges,
12389 switch_inst,
12390 zir_switch,
1223112391 );
1223212392 }
12233
12234 if (scalar_cases_len + multi_cases_len == 0 and
12235 special_members_only == null and
12236 !special_generic.is_inline and
12237 !extra.data.bits.has_continue)
12393 }
12394 for (range_refs) |range_ref| {
12395 const first_val = sema.resolveConstDefinedValue(child_block, .unneeded, range_ref[0], undefined) catch unreachable;
12396 const last_val = sema.resolveConstDefinedValue(child_block, .unneeded, range_ref[1], undefined) catch unreachable;
12397 if ((try sema.compareAll(cond_val, .gte, first_val, item_ty)) and
12398 (try sema.compareAll(cond_val, .lte, last_val, item_ty)))
1223812399 {
12239 return spa.resolveProngComptime(
12240 &child_block,
12241 .special,
12242 special_generic.body,
12243 special_generic.capture,
12400 return sema.resolveSwitchProng(
12401 block,
12402 child_block,
12403 operand,
12404 raw_operand_ty,
12405 prong_body,
1224412406 block.src(.{ .switch_capture = .{
12245 .switch_node_offset = src_node_offset,
12246 .case_idx = if (has_under) .special_under else .special_else,
12407 .switch_node_offset = switch_node_offset,
12408 .case_idx = case.index,
1224712409 } }),
12248 undefined, // case_vals may be undefined for special prongs
12249 .none,
12250 false,
12410 prong_info.capture,
12411 prong_info.has_tag_capture,
12412 if (prong_info.is_inline) cond_ref else .none,
12413 .has_ranges,
12414 validated_switch.else_err_ty,
1225112415 merges,
12416 switch_inst,
12417 zir_switch,
1225212418 );
1225312419 }
12254 },
12255 }
12256
12257 if (child_block.isComptime()) {
12258 _ = try sema.resolveConstDefinedValue(&child_block, operand_src, operand.simple.cond, null);
12259 unreachable;
12260 }
12261
12262 var extra_case_vals: struct {
12263 items: std.ArrayList(Air.Inst.Ref),
12264 ranges: std.ArrayList([2]Air.Inst.Ref),
12265 } = .{ .items = .empty, .ranges = .empty };
12266 defer {
12267 extra_case_vals.items.deinit(gpa);
12268 extra_case_vals.ranges.deinit(gpa);
12269 }
12270
12271 // Runtime switch, if we have a special_members_only prong we need to unroll
12272 // it to a prong with explicit items.
12273 // Although this is potentially the same as `inline else` it does not count
12274 // towards the backward branch quota because it's an implementation detail.
12275 if (special_members_only != null) gen: {
12276 assert(cond_ty.isNonexhaustiveEnum(zcu));
12277
12278 var min_i: usize = math.maxInt(usize);
12279 var max_i: usize = 0;
12280 var seen_field_count: usize = 0;
12281 for (seen_enum_fields, 0..) |seen, enum_i| {
12282 if (seen != null) {
12283 seen_field_count += 1;
12284 } else {
12285 min_i = @min(min_i, enum_i);
12286 max_i = @max(max_i, enum_i);
12287 }
12288 }
12289 if (min_i == max_i) {
12290 seen_enum_fields[min_i] = special_members_only_src;
12291 const item_val = try pt.enumValueFieldIndex(cond_ty, @intCast(min_i));
12292 const item_ref = Air.internedToRef(item_val.toIntern());
12293 try extra_case_vals.items.append(gpa, item_ref);
12294 break :gen;
12295 }
12296 const missing_field_count = seen_enum_fields.len - seen_field_count;
12297
12298 extra_case_vals.items = try .initCapacity(gpa, missing_field_count / 2);
12299 extra_case_vals.ranges = try .initCapacity(gpa, missing_field_count / 4);
12300 const int_ty = cond_ty.intTagType(zcu);
12301
12302 var last_val = try pt.enumValueFieldIndex(cond_ty, @intCast(min_i));
12303 var first_ref = Air.internedToRef(last_val.toIntern());
12304 seen_enum_fields[min_i] = special_members_only_src;
12305 for (seen_enum_fields[(min_i + 1)..(max_i + 1)], (min_i + 1)..) |seen, enum_i| {
12306 if (seen != null) continue;
12307 seen_enum_fields[enum_i] = special_members_only_src;
12308
12309 const item_val = try pt.enumValueFieldIndex(cond_ty, @intCast(enum_i));
12310 const item_ref = Air.internedToRef(item_val.toIntern());
12311
12312 const is_next = is_next: {
12313 const prev_int = ip.indexToKey(last_val.toIntern()).enum_tag.int;
12314
12315 const result = try arith.incrementDefinedInt(sema, int_ty, .fromInterned(prev_int));
12316 if (result.overflow) break :is_next false;
12317
12318 const item_int = ip.indexToKey(item_val.toIntern()).enum_tag.int;
12319 break :is_next try sema.valuesEqual(.fromInterned(item_int), result.val, int_ty);
12320 };
12321
12322 if (is_next) {
12323 last_val = item_val;
12324 } else {
12325 const last_ref = Air.internedToRef(last_val.toIntern());
12326 if (first_ref == last_ref) {
12327 try extra_case_vals.items.append(gpa, first_ref);
12328 } else {
12329 try extra_case_vals.ranges.append(gpa, .{ first_ref, last_ref });
12330 }
12331 first_ref = item_ref;
12332 last_val = item_val;
12333 }
12334 }
12335 const last_ref = Air.internedToRef(last_val.toIntern());
12336 if (first_ref == last_ref) {
12337 try extra_case_vals.items.append(gpa, first_ref);
12338 } else {
12339 try extra_case_vals.ranges.append(gpa, .{ first_ref, last_ref });
1234012420 }
1234112421 }
1234212422
12343 const air_switch_ref = try sema.analyzeSwitchRuntimeBlock(
12344 spa,
12345 &child_block,
12346 src,
12347 switch (operand) {
12348 .simple => |s| s.cond,
12349 .loop => |l| l.init_cond,
12350 },
12351 cond_ty,
12352 operand_src,
12353 case_vals,
12354 special_generic,
12355 scalar_cases_len,
12356 multi_cases_len,
12357 union_originally,
12358 raw_operand_ty,
12359 err_set,
12360 src_node_offset,
12361 special_generic_src,
12362 has_under,
12363 seen_enum_fields,
12364 seen_errors,
12365 range_set,
12366 true_count,
12367 false_count,
12368 cond_dbg_node_index,
12369 false,
12370 special_members_only,
12371 special_members_only_src,
12372 extra_case_vals.items.items,
12373 extra_case_vals.ranges.items,
12374 );
12375
12376 for (merges.extra_insts.items, merges.extra_src_locs.items) |placeholder_inst, dispatch_src| {
12377 var replacement_block = block.makeSubBlock();
12378 defer replacement_block.instructions.deinit(gpa);
12379
12380 assert(sema.air_instructions.items(.tag)[@intFromEnum(placeholder_inst)] == .br);
12381 const new_operand_maybe_ref = sema.air_instructions.items(.data)[@intFromEnum(placeholder_inst)].br.operand;
12382
12383 if (extra.data.bits.any_non_inline_capture) {
12384 _ = try replacement_block.addBinOp(.store, operand.loop.operand_alloc, new_operand_maybe_ref);
12385 }
12423 const else_case = validated_switch.else_case;
12424 const under_case = validated_switch.under_case;
1238612425
12387 const new_operand_val = if (operand_is_ref)
12388 try sema.analyzeLoad(&replacement_block, dispatch_src, new_operand_maybe_ref, dispatch_src)
12389 else
12390 new_operand_maybe_ref;
12391
12392 const new_cond = try sema.switchCond(&replacement_block, dispatch_src, new_operand_val);
12393
12394 if (zcu.backendSupportsFeature(.is_named_enum_value) and block.wantSafety() and
12395 cond_ty.zigTypeTag(zcu) == .@"enum" and !cond_ty.isNonexhaustiveEnum(zcu) and
12396 !try sema.isComptimeKnown(new_cond))
12397 {
12398 const ok = try replacement_block.addUnOp(.is_named_enum_value, new_cond);
12399 try sema.addSafetyCheck(&replacement_block, src, ok, .corrupt_switch);
12400 }
12401
12402 _ = try replacement_block.addInst(.{
12403 .tag = .switch_dispatch,
12404 .data = .{ .br = .{
12405 .block_inst = air_switch_ref.toIndex().?,
12406 .operand = new_cond,
12407 } },
12408 });
12426 // named-only prong
1240912427
12410 if (replacement_block.instructions.items.len == 1) {
12411 // Optimization: we don't need a block!
12412 sema.air_instructions.set(
12413 @intFromEnum(placeholder_inst),
12414 sema.air_instructions.get(@intFromEnum(replacement_block.instructions.items[0])),
12415 );
12416 continue;
12417 }
12418
12419 // Replace placeholder with a block.
12420 // No `br` is needed as the block is a switch dispatch so necessarily `noreturn`.
12421 try sema.air_extra.ensureUnusedCapacity(
12422 gpa,
12423 @typeInfo(Air.Block).@"struct".fields.len + replacement_block.instructions.items.len,
12428 if (else_is_named_only and item_ty.enumTagFieldIndex(cond_val, zcu) != null) {
12429 assert(item_ty.isNonexhaustiveEnum(zcu));
12430 return sema.resolveSwitchProng(
12431 block,
12432 child_block,
12433 operand,
12434 raw_operand_ty,
12435 else_case.body,
12436 block.src(.{ .switch_capture = .{
12437 .switch_node_offset = switch_node_offset,
12438 .case_idx = else_case.index,
12439 } }),
12440 else_case.capture,
12441 else_case.has_tag_capture,
12442 if (else_case.is_inline) cond_ref else .none,
12443 .special,
12444 validated_switch.else_err_ty,
12445 merges,
12446 switch_inst,
12447 zir_switch,
1242412448 );
12425 sema.air_instructions.set(@intFromEnum(placeholder_inst), .{
12426 .tag = .block,
12427 .data = .{ .ty_pl = .{
12428 .ty = .noreturn_type,
12429 .payload = sema.addExtraAssumeCapacity(Air.Block{
12430 .body_len = @intCast(replacement_block.instructions.items.len),
12431 }),
12432 } },
12433 });
12434 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(replacement_block.instructions.items));
1243512449 }
1243612450
12437 return sema.resolveAnalyzedBlock(block, src, &child_block, merges, false);
12438}
12439
12440const SpecialProng = struct {
12441 body: []const Zir.Inst.Index,
12442 end: usize,
12443 capture: Zir.Inst.SwitchBlock.ProngInfo.Capture,
12444 is_inline: bool,
12445 has_tag_capture: bool,
12446};
12447
12448fn analyzeSwitchRuntimeBlock(
12449 sema: *Sema,
12450 spa: SwitchProngAnalysis,
12451 child_block: *Block,
12452 src: LazySrcLoc,
12453 operand: Air.Inst.Ref,
12454 operand_ty: Type,
12455 operand_src: LazySrcLoc,
12456 case_vals: std.ArrayList(Air.Inst.Ref),
12457 else_prong: SpecialProng,
12458 scalar_cases_len: usize,
12459 multi_cases_len: usize,
12460 union_originally: bool,
12461 maybe_union_ty: Type,
12462 err_set: bool,
12463 switch_node_offset: std.zig.Ast.Node.Offset,
12464 else_prong_src: LazySrcLoc,
12465 else_prong_is_underscore: bool,
12466 seen_enum_fields: []?LazySrcLoc,
12467 seen_errors: SwitchErrorSet,
12468 range_set: RangeSet,
12469 true_count: u8,
12470 false_count: u8,
12471 cond_dbg_node_index: Zir.Inst.Index,
12472 allow_err_code_unwrap: bool,
12473 extra_prong: ?SpecialProng,
12474 /// May be `undefined` if `extra_prong` is `null`
12475 extra_prong_src: LazySrcLoc,
12476 extra_prong_items: []const Air.Inst.Ref,
12477 extra_prong_ranges: []const [2]Air.Inst.Ref,
12478) CompileError!Air.Inst.Ref {
12479 const pt = sema.pt;
12480 const zcu = pt.zcu;
12481 const gpa = sema.gpa;
12482 const ip = &zcu.intern_pool;
12483
12484 const block = child_block.parent.?;
12485
12486 const estimated_cases_extra = (scalar_cases_len + multi_cases_len) *
12487 @typeInfo(Air.SwitchBr.Case).@"struct".fields.len + 2;
12488 var cases_extra = try std.ArrayList(u32).initCapacity(gpa, estimated_cases_extra);
12489 defer cases_extra.deinit(gpa);
12490
12491 var branch_hints = try std.ArrayList(std.builtin.BranchHint).initCapacity(gpa, scalar_cases_len);
12492 defer branch_hints.deinit(gpa);
12493
12494 var case_block = child_block.makeSubBlock();
12495 case_block.runtime_loop = null;
12496 case_block.runtime_cond = operand_src;
12497 case_block.runtime_index.increment();
12498 case_block.need_debug_scope = null; // this body is emitted regardless
12499 defer case_block.instructions.deinit(gpa);
12500
12501 var extra_index: usize = else_prong.end;
12502
12503 var scalar_i: usize = 0;
12504 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
12505 extra_index += 1;
12506 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
12507 extra_index += 1;
12508 const body = sema.code.bodySlice(extra_index, info.body_len);
12509 extra_index += info.body_len;
12510
12511 case_block.instructions.shrinkRetainingCapacity(0);
12512 case_block.error_return_trace_index = child_block.error_return_trace_index;
12451 // catch-all prong
1251312452
12514 const item = case_vals.items[scalar_i];
12515 // `item` is already guaranteed to be constant known.
12516
12517 const analyze_body = if (union_originally) blk: {
12518 const unresolved_item_val = sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, item, undefined) catch unreachable;
12519 const item_val = sema.resolveLazyValue(unresolved_item_val) catch unreachable;
12520 const field_ty = maybe_union_ty.unionFieldType(item_val, zcu).?;
12521 break :blk field_ty.zigTypeTag(zcu) != .noreturn;
12522 } else true;
12523
12524 const prong_hint: std.builtin.BranchHint = if (err_set and
12525 try sema.maybeErrorUnwrap(&case_block, body, operand, operand_src, allow_err_code_unwrap))
12526 h: {
12527 // nothing to do here. weight against error branch
12528 break :h .unlikely;
12529 } else if (analyze_body) h: {
12530 break :h try spa.analyzeProngRuntime(
12531 &case_block,
12532 .normal,
12533 body,
12534 info.capture,
12535 child_block.src(.{ .switch_capture = .{
12536 .switch_node_offset = switch_node_offset,
12537 .case_idx = .{ .kind = .scalar, .index = @intCast(scalar_i) },
12538 } }),
12539 &.{item},
12540 if (info.is_inline) item else .none,
12541 info.has_tag_capture,
12542 );
12543 } else h: {
12544 _ = try case_block.addNoOp(.unreach);
12545 break :h .none;
12546 };
12547
12548 try branch_hints.append(gpa, prong_hint);
12549 try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len +
12550 1 + // `item`, no ranges
12551 case_block.instructions.items.len);
12552 cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{
12553 .items_len = 1,
12554 .ranges_len = 0,
12555 .body_len = @intCast(case_block.instructions.items.len),
12556 }));
12557 cases_extra.appendAssumeCapacity(@intFromEnum(item));
12558 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
12559 }
12560
12561 var cases_len = scalar_cases_len;
12562 var case_val_idx: usize = scalar_cases_len;
12563 const multi_cases_len_with_extra_prong = multi_cases_len + @intFromBool(extra_prong != null);
12564 var multi_i: u32 = 0;
12565 while (multi_i < multi_cases_len_with_extra_prong) : (multi_i += 1) {
12566 const is_extra_prong = multi_i == multi_cases_len;
12567 var items: []const Air.Inst.Ref = undefined;
12568 var info: Zir.Inst.SwitchBlock.ProngInfo = undefined;
12569 var ranges: []const [2]Air.Inst.Ref = undefined;
12570 var body: []const Zir.Inst.Index = undefined;
12571 if (is_extra_prong) {
12572 const prong = extra_prong.?;
12573 items = extra_prong_items;
12574 ranges = extra_prong_ranges;
12575 body = prong.body;
12576 info = .{
12577 .body_len = undefined,
12578 .capture = prong.capture,
12579 .is_inline = prong.is_inline,
12580 .has_tag_capture = prong.has_tag_capture,
12581 };
12453 const index, const body, const capture, const has_tag_capture, const is_inline = switch (catch_all_case) {
12454 .@"else" => .{ else_case.index, else_case.body, else_case.capture, else_case.has_tag_capture, else_case.is_inline },
12455 .under => .{ under_case.index, under_case.body, under_case.capture, under_case.has_tag_capture, false },
12456 .none => unreachable,
12457 };
12458 if (err_set) try sema.maybeErrorUnwrapComptime(child_block, body, cond_ref);
12459 if (union_originally) {
12460 for (validated_switch.seen_enum_fields, 0..) |maybe_seen, field_i| {
12461 if (maybe_seen != null) continue;
12462 if (!operand_ty.unionFieldTypeByIndex(field_i, zcu).isNoReturn(zcu)) break;
1258212463 } else {
12583 @branchHint(.likely);
12584 const items_len = sema.code.extra[extra_index];
12585 extra_index += 1;
12586 const ranges_len = sema.code.extra[extra_index];
12587 extra_index += 1;
12588 info = @bitCast(sema.code.extra[extra_index]);
12589 extra_index += 1 + items_len + ranges_len * 2;
12590
12591 items = case_vals.items[case_val_idx..][0..items_len];
12592 case_val_idx += items_len;
12593 ranges = @ptrCast(case_vals.items[case_val_idx..][0 .. ranges_len * 2]);
12594 case_val_idx += ranges_len * 2;
12595
12596 body = sema.code.bodySlice(extra_index, info.body_len);
12597 extra_index += info.body_len;
12598 }
12599
12600 case_block.instructions.shrinkRetainingCapacity(0);
12601 case_block.error_return_trace_index = child_block.error_return_trace_index;
12602
12603 // Generate all possible cases as scalar prongs.
12604 if (info.is_inline) {
12605 var emit_bb = false;
12606
12607 for (ranges, 0..) |range_items, range_i| {
12608 var item = sema.resolveConstDefinedValue(block, .unneeded, range_items[0], undefined) catch unreachable;
12609 const item_last = sema.resolveConstDefinedValue(block, .unneeded, range_items[1], undefined) catch unreachable;
12610
12611 while (item.compareScalar(.lte, item_last, operand_ty, zcu)) : ({
12612 // Previous validation has resolved any possible lazy values.
12613 const int_val: Value, const int_ty: Type = switch (operand_ty.zigTypeTag(zcu)) {
12614 .int => .{ item, operand_ty },
12615 .@"enum" => b: {
12616 const int_val = Value.fromInterned(ip.indexToKey(item.toIntern()).enum_tag.int);
12617 break :b .{ int_val, int_val.typeOf(zcu) };
12618 },
12619 else => unreachable,
12620 };
12621 const result = try arith.incrementDefinedInt(sema, int_ty, int_val);
12622 assert(!result.overflow);
12623 item = switch (operand_ty.zigTypeTag(zcu)) {
12624 .int => result.val,
12625 .@"enum" => .fromInterned(try pt.intern(.{ .enum_tag = .{
12626 .ty = operand_ty.toIntern(),
12627 .int = result.val.toIntern(),
12628 } })),
12629 else => unreachable,
12630 };
12631 }) {
12632 cases_len += 1;
12633
12634 const item_ref = Air.internedToRef(item.toIntern());
12635
12636 case_block.instructions.shrinkRetainingCapacity(0);
12637 case_block.error_return_trace_index = child_block.error_return_trace_index;
12638
12639 if (emit_bb) {
12640 const bb_src = if (is_extra_prong) extra_prong_src else block.src(.{ .switch_case_item = .{
12641 .switch_node_offset = switch_node_offset,
12642 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) },
12643 .item_idx = .{ .kind = .range, .index = @intCast(range_i) },
12644 } });
12645 try sema.emitBackwardBranch(block, bb_src);
12646 }
12647 emit_bb = true;
12648
12649 const prong_hint = try spa.analyzeProngRuntime(
12650 &case_block,
12651 .normal,
12652 body,
12653 info.capture,
12654 child_block.src(.{ .switch_capture = .{
12655 .switch_node_offset = switch_node_offset,
12656 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) },
12657 } }),
12658 undefined, // case_vals may be undefined for ranges
12659 item_ref,
12660 info.has_tag_capture,
12661 );
12662 try branch_hints.append(gpa, prong_hint);
12663
12664 try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len +
12665 1 + // `item`, no ranges
12666 case_block.instructions.items.len);
12667 cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{
12668 .items_len = 1,
12669 .ranges_len = 0,
12670 .body_len = @intCast(case_block.instructions.items.len),
12671 }));
12672 cases_extra.appendAssumeCapacity(@intFromEnum(item_ref));
12673 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
12674
12675 if (item.compareScalar(.eq, item_last, operand_ty, zcu)) break;
12676 }
12677 }
12678
12679 for (items, 0..) |item, item_i| {
12680 cases_len += 1;
12681
12682 case_block.instructions.shrinkRetainingCapacity(0);
12683 case_block.error_return_trace_index = child_block.error_return_trace_index;
12684
12685 const analyze_body = if (union_originally) blk: {
12686 const item_val = sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, item, undefined) catch unreachable;
12687 const field_ty = maybe_union_ty.unionFieldType(item_val, zcu).?;
12688 break :blk field_ty.zigTypeTag(zcu) != .noreturn;
12689 } else true;
12690
12691 if (emit_bb) {
12692 const bb_src = if (is_extra_prong) extra_prong_src else block.src(.{ .switch_case_item = .{
12693 .switch_node_offset = switch_node_offset,
12694 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) },
12695 .item_idx = .{ .kind = .single, .index = @intCast(item_i) },
12696 } });
12697 try sema.emitBackwardBranch(block, bb_src);
12698 }
12699 emit_bb = true;
12700
12701 const prong_hint: std.builtin.BranchHint = if (analyze_body) h: {
12702 break :h try spa.analyzeProngRuntime(
12703 &case_block,
12704 .normal,
12705 body,
12706 info.capture,
12707 child_block.src(.{ .switch_capture = .{
12708 .switch_node_offset = switch_node_offset,
12709 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) },
12710 } }),
12711 &.{item},
12712 item,
12713 info.has_tag_capture,
12714 );
12715 } else h: {
12716 _ = try case_block.addNoOp(.unreach);
12717 break :h .none;
12718 };
12719 try branch_hints.append(gpa, prong_hint);
12720
12721 try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len +
12722 1 + // `item`, no ranges
12723 case_block.instructions.items.len);
12724 cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{
12725 .items_len = 1,
12726 .ranges_len = 0,
12727 .body_len = @intCast(case_block.instructions.items.len),
12728 }));
12729 cases_extra.appendAssumeCapacity(@intFromEnum(item));
12730 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
12731 }
12732
12733 continue;
12734 }
12735
12736 cases_len += 1;
12737
12738 const analyze_body = if (union_originally)
12739 for (items) |item| {
12740 const item_val = sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, item, undefined) catch unreachable;
12741 const field_ty = maybe_union_ty.unionFieldType(item_val, zcu).?;
12742 if (field_ty.zigTypeTag(zcu) != .noreturn) break true;
12743 } else false
12744 else
12745 true;
12746
12747 const prong_hint: std.builtin.BranchHint = if (err_set and
12748 try sema.maybeErrorUnwrap(&case_block, body, operand, operand_src, allow_err_code_unwrap))
12749 h: {
12750 // nothing to do here. weight against error branch
12751 break :h .unlikely;
12752 } else if (analyze_body) h: {
12753 break :h try spa.analyzeProngRuntime(
12754 &case_block,
12755 .normal,
12756 body,
12757 info.capture,
12758 child_block.src(.{ .switch_capture = .{
12759 .switch_node_offset = switch_node_offset,
12760 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) },
12761 } }),
12762 items,
12763 .none,
12764 false,
12765 );
12766 } else h: {
12767 _ = try case_block.addNoOp(.unreach);
12768 break :h .none;
12769 };
12770
12771 try branch_hints.append(gpa, prong_hint);
12772
12773 try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len +
12774 items.len + ranges.len * 2 +
12775 case_block.instructions.items.len);
12776 cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{
12777 .items_len = @intCast(items.len),
12778 .ranges_len = @intCast(ranges.len),
12779 .body_len = @intCast(case_block.instructions.items.len),
12780 }));
12781
12782 for (items) |item| {
12783 cases_extra.appendAssumeCapacity(@intFromEnum(item));
12784 }
12785 for (ranges) |range| {
12786 cases_extra.appendSliceAssumeCapacity(&.{
12787 @intFromEnum(range[0]),
12788 @intFromEnum(range[1]),
12789 });
12464 // This prong should be unreachable!
12465 return .unreachable_value;
1279012466 }
12791
12792 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
1279312467 }
12468 return sema.resolveSwitchProng(
12469 block,
12470 child_block,
12471 operand,
12472 raw_operand_ty,
12473 body,
12474 block.src(.{ .switch_capture = .{
12475 .switch_node_offset = switch_node_offset,
12476 .case_idx = index,
12477 } }),
12478 capture,
12479 has_tag_capture,
12480 if (is_inline) cond_ref else .none,
12481 .special,
12482 validated_switch.else_err_ty,
12483 merges,
12484 switch_inst,
12485 zir_switch,
12486 );
12487}
1279412488
12795 const else_body: []const Air.Inst.Index = if (else_prong.body.len != 0 or case_block.wantSafety()) else_body: {
12796 var emit_bb = false;
12797 // If this is true we must have a 'true' else prong and not an underscore because
12798 // underscore prongs can never be inlined. We've already checked for this.
12799 if (else_prong.is_inline) switch (operand_ty.zigTypeTag(zcu)) {
12800 .@"enum" => {
12801 if (operand_ty.isNonexhaustiveEnum(zcu) and !union_originally) {
12802 return sema.fail(block, else_prong_src, "cannot enumerate values of type '{f}' for 'inline else'", .{
12803 operand_ty.fmt(pt),
12804 });
12805 }
12806 for (seen_enum_fields, 0..) |f, i| {
12807 if (f != null) continue;
12808 cases_len += 1;
12809
12810 const item_val = try pt.enumValueFieldIndex(operand_ty, @intCast(i));
12811 const item_ref = Air.internedToRef(item_val.toIntern());
12812
12813 case_block.instructions.shrinkRetainingCapacity(0);
12814 case_block.error_return_trace_index = child_block.error_return_trace_index;
12815
12816 const analyze_body = if (union_originally) blk: {
12817 const field_ty = maybe_union_ty.unionFieldType(item_val, zcu).?;
12818 break :blk field_ty.zigTypeTag(zcu) != .noreturn;
12819 } else true;
12820
12821 if (emit_bb) try sema.emitBackwardBranch(block, else_prong_src);
12822 emit_bb = true;
12823
12824 const prong_hint: std.builtin.BranchHint = if (analyze_body) h: {
12825 break :h try spa.analyzeProngRuntime(
12826 &case_block,
12827 .special,
12828 else_prong.body,
12829 else_prong.capture,
12830 child_block.src(.{ .switch_capture = .{
12831 .switch_node_offset = switch_node_offset,
12832 .case_idx = .special_else,
12833 } }),
12834 &.{item_ref},
12835 item_ref,
12836 else_prong.has_tag_capture,
12837 );
12838 } else h: {
12839 _ = try case_block.addNoOp(.unreach);
12840 break :h .none;
12841 };
12842 try branch_hints.append(gpa, prong_hint);
12843
12844 try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len +
12845 1 + // `item`, no ranges
12846 case_block.instructions.items.len);
12847 cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{
12848 .items_len = 1,
12849 .ranges_len = 0,
12850 .body_len = @intCast(case_block.instructions.items.len),
12851 }));
12852 cases_extra.appendAssumeCapacity(@intFromEnum(item_ref));
12853 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
12854 }
12855 },
12856 .error_set => {
12857 if (operand_ty.isAnyError(zcu)) {
12858 return sema.fail(block, else_prong_src, "cannot enumerate values of type '{f}' for 'inline else'", .{
12859 operand_ty.fmt(pt),
12860 });
12861 }
12862 const error_names = operand_ty.errorSetNames(zcu);
12863 for (0..error_names.len) |name_index| {
12864 const error_name = error_names.get(ip)[name_index];
12865 if (seen_errors.contains(error_name)) continue;
12866 cases_len += 1;
12867
12868 const item_val = try pt.intern(.{ .err = .{
12869 .ty = operand_ty.toIntern(),
12870 .name = error_name,
12871 } });
12872 const item_ref = Air.internedToRef(item_val);
12873
12874 case_block.instructions.shrinkRetainingCapacity(0);
12875 case_block.error_return_trace_index = child_block.error_return_trace_index;
12876
12877 if (emit_bb) try sema.emitBackwardBranch(block, else_prong_src);
12878 emit_bb = true;
12879
12880 const prong_hint = try spa.analyzeProngRuntime(
12881 &case_block,
12882 .special,
12883 else_prong.body,
12884 else_prong.capture,
12885 child_block.src(.{ .switch_capture = .{
12886 .switch_node_offset = switch_node_offset,
12887 .case_idx = .special_else,
12888 } }),
12889 &.{item_ref},
12890 item_ref,
12891 else_prong.has_tag_capture,
12892 );
12893 try branch_hints.append(gpa, prong_hint);
12894
12895 try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len +
12896 1 + // `item`, no ranges
12897 case_block.instructions.items.len);
12898 cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{
12899 .items_len = 1,
12900 .ranges_len = 0,
12901 .body_len = @intCast(case_block.instructions.items.len),
12902 }));
12903 cases_extra.appendAssumeCapacity(@intFromEnum(item_ref));
12904 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
12905 }
12906 },
12907 .int => {
12908 var it = try RangeSetUnhandledIterator.init(sema, operand_ty, range_set);
12909 while (try it.next()) |cur| {
12910 cases_len += 1;
12911
12912 const item_ref = Air.internedToRef(cur);
12913
12914 case_block.instructions.shrinkRetainingCapacity(0);
12915 case_block.error_return_trace_index = child_block.error_return_trace_index;
12916
12917 if (emit_bb) try sema.emitBackwardBranch(block, else_prong_src);
12918 emit_bb = true;
12919
12920 const prong_hint = try spa.analyzeProngRuntime(
12921 &case_block,
12922 .special,
12923 else_prong.body,
12924 else_prong.capture,
12925 child_block.src(.{ .switch_capture = .{
12926 .switch_node_offset = switch_node_offset,
12927 .case_idx = .special_else,
12928 } }),
12929 &.{item_ref},
12930 item_ref,
12931 else_prong.has_tag_capture,
12932 );
12933 try branch_hints.append(gpa, prong_hint);
12934
12935 try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len +
12936 1 + // `item`, no ranges
12937 case_block.instructions.items.len);
12938 cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{
12939 .items_len = 1,
12940 .ranges_len = 0,
12941 .body_len = @intCast(case_block.instructions.items.len),
12942 }));
12943 cases_extra.appendAssumeCapacity(@intFromEnum(item_ref));
12944 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
12945 }
12946 },
12947 .bool => {
12948 if (true_count == 0) {
12949 cases_len += 1;
12950
12951 case_block.instructions.shrinkRetainingCapacity(0);
12952 case_block.error_return_trace_index = child_block.error_return_trace_index;
12489const SwitchOperand = union(enum) {
12490 /// This switch will be dispatched only once, with the given operand.
12491 simple: struct {
12492 /// The raw switch operand value. Always defined.
12493 by_val: Air.Inst.Ref,
12494 /// The switch operand *pointer*. Defined only if there is a prong
12495 /// with a by-ref capture.
12496 by_ref: Air.Inst.Ref,
12497 /// The switch condition value. For unions, `operand` is the union
12498 /// and `cond` is its enum tag value.
12499 cond: Air.Inst.Ref,
12500 },
12501 /// This switch may be dispatched multiple times with `continue` syntax.
12502 /// As such, the operand is stored in an alloc if needed.
12503 loop: struct {
12504 /// The `alloc` containing the `switch` operand for the active dispatch.
12505 /// Each prong must load from this `alloc` to get captures.
12506 /// If there are no captures, this may be undefined.
12507 operand_alloc: Air.Inst.Ref,
12508 /// Whether `operand_alloc` contains a by-val operand or a by-ref
12509 /// operand.
12510 operand_is_ref: bool,
12511 /// The switch condition value for the *initial* dispatch. For
12512 /// unions, this is the enum tag value.
12513 init_cond: Air.Inst.Ref,
12514 },
12515};
1295312516
12954 if (emit_bb) try sema.emitBackwardBranch(block, else_prong_src);
12955 emit_bb = true;
12517const CatchAllSwitchCase = enum { none, @"else", under };
1295612518
12957 const prong_hint = try spa.analyzeProngRuntime(
12958 &case_block,
12959 .special,
12960 else_prong.body,
12961 else_prong.capture,
12962 child_block.src(.{ .switch_capture = .{
12963 .switch_node_offset = switch_node_offset,
12964 .case_idx = .special_else,
12965 } }),
12966 &.{.bool_true},
12967 .bool_true,
12968 else_prong.has_tag_capture,
12969 );
12970 try branch_hints.append(gpa, prong_hint);
12519const SwitchProngKind = union(enum) {
12520 item_refs: []const Air.Inst.Ref,
12521 has_ranges,
12522 special,
12523};
1297112524
12972 try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len +
12973 1 + // `item`, no ranges
12974 case_block.instructions.items.len);
12975 cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{
12976 .items_len = 1,
12977 .ranges_len = 0,
12978 .body_len = @intCast(case_block.instructions.items.len),
12979 }));
12980 cases_extra.appendAssumeCapacity(@intFromEnum(Air.Inst.Ref.bool_true));
12981 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
12982 }
12983 if (false_count == 0) {
12984 cases_len += 1;
12525/// Resolve a switch prong which is determined at comptime to have no peers.
12526/// Sets up captures as needed. Uses `analyzeBodyRuntimeBreak`.
12527fn resolveSwitchProng(
12528 sema: *Sema,
12529 block: *Block,
12530 child_block: *Block,
12531 operand: SwitchOperand,
12532 raw_operand_ty: Type,
12533 prong_body: []const Zir.Inst.Index,
12534 /// Must use the `switch_capture` field in `offset`.
12535 capture_src: LazySrcLoc,
12536 capture: Zir.Inst.SwitchBlock.ProngInfo.Capture,
12537 has_tag_capture: bool,
12538 inline_case_capture: Air.Inst.Ref,
12539 kind: SwitchProngKind,
12540 else_err_ty: ?Type,
12541 merges: *Block.Merges,
12542 switch_inst: Zir.Inst.Index,
12543 zir_switch: *const Zir.UnwrappedSwitchBlock,
12544) CompileError!Air.Inst.Ref {
12545 const src_node_offset = zir_switch.switch_src_node_offset;
12546 const src = block.nodeOffset(src_node_offset);
12547 const operand_src = block.src(.{ .node_offset_switch_operand = src_node_offset });
1298512548
12986 case_block.instructions.shrinkRetainingCapacity(0);
12987 case_block.error_return_trace_index = child_block.error_return_trace_index;
12549 // We can propagate `.cold` hints from this branch since it's comptime-known
12550 // to be taken from the parent branch.
12551 const parent_hint = sema.branch_hint;
12552 defer sema.branch_hint = parent_hint orelse if (sema.branch_hint == .cold) .cold else null;
1298812553
12989 if (emit_bb) try sema.emitBackwardBranch(block, else_prong_src);
12990 emit_bb = true;
12554 const payload_inst: Zir.Inst.Index = if (capture != .none) inst: {
12555 const payload_inst = zir_switch.payload_capture_placeholder.unwrap() orelse switch_inst;
12556 const payload_ref = try sema.analyzeSwitchPayloadCapture(
12557 child_block,
12558 operand,
12559 operand.simple.by_val,
12560 operand.simple.by_ref,
12561 sema.typeOf(operand.simple.by_val),
12562 operand_src,
12563 capture_src,
12564 capture == .by_ref,
12565 kind == .special,
12566 switch (kind) {
12567 .item_refs => |item_refs| item_refs,
12568 .has_ranges, .special => undefined,
12569 },
12570 inline_case_capture,
12571 else_err_ty,
12572 );
12573 assert(!sema.typeOf(payload_ref).isNoReturn(sema.pt.zcu));
12574 sema.inst_map.putAssumeCapacity(payload_inst, payload_ref);
12575 break :inst payload_inst;
12576 } else undefined;
12577 defer if (capture != .none) assert(sema.inst_map.remove(payload_inst));
1299112578
12992 const prong_hint = try spa.analyzeProngRuntime(
12993 &case_block,
12994 .special,
12995 else_prong.body,
12996 else_prong.capture,
12997 child_block.src(.{ .switch_capture = .{
12998 .switch_node_offset = switch_node_offset,
12999 .case_idx = .special_else,
13000 } }),
13001 &.{.bool_false},
13002 .bool_false,
13003 else_prong.has_tag_capture,
13004 );
13005 try branch_hints.append(gpa, prong_hint);
12579 const tag_inst: Zir.Inst.Index = if (has_tag_capture) inst: {
12580 const tag_inst = zir_switch.tag_capture_placeholder.unwrap() orelse switch_inst;
12581 const tag_ref = try sema.analyzeSwitchTagCapture(
12582 child_block,
12583 operand.simple.by_val,
12584 sema.typeOf(operand.simple.by_val),
12585 capture_src,
12586 inline_case_capture,
12587 );
12588 sema.inst_map.putAssumeCapacity(tag_inst, tag_ref);
12589 break :inst tag_inst;
12590 } else undefined;
12591 defer if (has_tag_capture) assert(sema.inst_map.remove(tag_inst));
1300612592
13007 try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len +
13008 1 + // `item`, no ranges
13009 case_block.instructions.items.len);
13010 cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{
13011 .items_len = 1,
13012 .ranges_len = 0,
13013 .body_len = @intCast(case_block.instructions.items.len),
13014 }));
13015 cases_extra.appendAssumeCapacity(@intFromEnum(Air.Inst.Ref.bool_false));
13016 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
13017 }
13018 },
13019 else => return sema.fail(block, else_prong_src, "cannot enumerate values of type '{f}' for 'inline else'", .{
13020 operand_ty.fmt(pt),
13021 }),
13022 };
12593 if (zir_switch.has_continue) sema.inst_map.putAssumeCapacity(switch_inst, .fromType(raw_operand_ty));
12594 defer if (zir_switch.has_continue) assert(sema.inst_map.remove(switch_inst));
1302312595
13024 case_block.instructions.shrinkRetainingCapacity(0);
13025 case_block.error_return_trace_index = child_block.error_return_trace_index;
12596 return sema.resolveBlockBody(block, src, child_block, prong_body, switch_inst, merges);
12597}
1302612598
13027 if (zcu.backendSupportsFeature(.is_named_enum_value) and
13028 else_prong.body.len != 0 and block.wantSafety() and
13029 operand_ty.zigTypeTag(zcu) == .@"enum" and
13030 (!operand_ty.isNonexhaustiveEnum(zcu) or union_originally))
13031 {
13032 try sema.zirDbgStmt(&case_block, cond_dbg_node_index);
13033 const ok = try case_block.addUnOp(.is_named_enum_value, operand);
13034 try sema.addSafetyCheck(&case_block, src, ok, .corrupt_switch);
13035 }
12599fn wantSwitchProngBodyAnalysis(
12600 sema: *Sema,
12601 block: *Block,
12602 item_ref: Air.Inst.Ref,
12603 operand_ty: Type,
12604 union_originally: bool,
12605 err_set: bool,
12606 prong_is_comptime_unreach: bool,
12607) bool {
12608 const zcu = sema.pt.zcu;
12609 if (union_originally) {
12610 const unresolved_item_val = sema.resolveConstDefinedValue(block, .unneeded, item_ref, undefined) catch unreachable;
12611 const item_val = sema.resolveLazyValue(unresolved_item_val) catch unreachable;
12612 const field_ty = operand_ty.unionFieldType(item_val, zcu).?;
12613 if (field_ty.isNoReturn(zcu)) return false;
12614 }
12615 if (err_set and prong_is_comptime_unreach) {
12616 const unresolved_item_val = sema.resolveConstDefinedValue(block, .unneeded, item_ref, undefined) catch unreachable;
12617 const item_val = sema.resolveLazyValue(unresolved_item_val) catch unreachable;
12618 const err_name = item_val.getErrorName(zcu).unwrap().?;
12619 if (!Type.errorSetHasFieldIp(&zcu.intern_pool, operand_ty.toIntern(), err_name)) return false;
12620 }
12621 return true;
12622}
1303612623
13037 const else_src_idx: LazySrcLoc.Offset.SwitchCaseIndex = if (else_prong_is_underscore)
13038 .special_under
13039 else
13040 .special_else;
12624/// Assumes that `operand_ty` has more than one possible value.
12625/// Sets up captures as needed. Uses `analyzeBodyRuntimeBreak`.
12626fn analyzeSwitchProng(
12627 sema: *Sema,
12628 case_block: *Block,
12629 operand: SwitchOperand,
12630 operand_ty: Type,
12631 raw_operand_ty: Type,
12632 prong_body: []const Zir.Inst.Index,
12633 /// Must use the `switch_capture` field in `offset`.
12634 capture_src: LazySrcLoc,
12635 capture: Zir.Inst.SwitchBlock.ProngInfo.Capture,
12636 has_tag_capture: bool,
12637 inline_case_capture: Air.Inst.Ref,
12638 kind: SwitchProngKind,
12639 else_err_ty: ?Type,
12640 switch_inst: Zir.Inst.Index,
12641 zir_switch: *const Zir.UnwrappedSwitchBlock,
12642) CompileError!std.builtin.BranchHint {
12643 const pt = sema.pt;
12644 const zcu = pt.zcu;
1304112645
13042 const analyze_body = if (union_originally and !else_prong.is_inline)
13043 for (seen_enum_fields, 0..) |seen_field, index| {
13044 if (seen_field != null) continue;
13045 const union_obj = zcu.typeToUnion(maybe_union_ty).?;
13046 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[index]);
13047 if (field_ty.zigTypeTag(zcu) != .noreturn) break true;
13048 } else false
13049 else
13050 true;
13051 const else_hint: std.builtin.BranchHint = if (else_prong.body.len != 0 and err_set and
13052 try sema.maybeErrorUnwrap(&case_block, else_prong.body, operand, operand_src, allow_err_code_unwrap))
13053 h: {
13054 // nothing to do here. weight against error branch
13055 break :h .unlikely;
13056 } else if (else_prong.body.len != 0 and analyze_body and !else_prong.is_inline) h: {
13057 break :h try spa.analyzeProngRuntime(
13058 &case_block,
13059 .special,
13060 else_prong.body,
13061 else_prong.capture,
13062 child_block.src(.{ .switch_capture = .{
13063 .switch_node_offset = switch_node_offset,
13064 .case_idx = else_src_idx,
13065 } }),
13066 undefined, // case_vals may be undefined for special prongs
13067 .none,
13068 false,
13069 );
13070 } else h: {
13071 // We still need a terminator in this block, but we have proven
13072 // that it is unreachable.
13073 if (case_block.wantSafety()) {
13074 try sema.zirDbgStmt(&case_block, cond_dbg_node_index);
13075 try sema.safetyPanic(&case_block, src, .corrupt_switch);
13076 } else {
13077 _ = try case_block.addNoOp(.unreach);
13078 }
13079 // Safety check / unreachable branches are cold.
13080 break :h .cold;
12646 const operand_src = case_block.src(.{ .node_offset_switch_operand = zir_switch.switch_src_node_offset });
12647
12648 if (operand_ty.zigTypeTag(zcu) == .error_set) {
12649 const cond_ref = switch (operand) {
12650 .simple => |s| s.cond,
12651 .loop => |l| l.init_cond,
1308112652 };
12653 if (try sema.maybeErrorUnwrap(case_block, prong_body, cond_ref, operand_src, true)) {
12654 // nothing to do here. weight against error branch
12655 return .unlikely;
12656 }
12657 }
1308212658
13083 try branch_hints.append(gpa, else_hint);
13084 break :else_body case_block.instructions.items;
13085 } else else_body: {
13086 try branch_hints.append(gpa, .none);
13087 break :else_body &.{};
12659 const operand_val, const operand_ptr = load_operand: {
12660 if (capture == .none and !has_tag_capture) {
12661 // No need to load the operand for this prong!
12662 break :load_operand .{ undefined, undefined };
12663 }
12664 if (inline_case_capture != .none and
12665 !(capture != .none and operand_ty.zigTypeTag(zcu) == .@"union"))
12666 {
12667 // We only need to load the operand if there's a union payload capture
12668 // since it's always runtime-known; only the tag is comptime-known here.
12669 break :load_operand .{ undefined, undefined };
12670 }
12671 assert(zir_switch.any_maybe_runtime_capture); // should have caught everything else by now
12672 switch (operand) {
12673 .simple => |s| break :load_operand .{ s.by_val, s.by_ref },
12674 .loop => |l| {
12675 const loaded = try sema.analyzeLoad(case_block, operand_src, l.operand_alloc, operand_src);
12676 if (l.operand_is_ref) {
12677 const by_val = try sema.analyzeLoad(case_block, operand_src, loaded, operand_src);
12678 break :load_operand .{ by_val, loaded };
12679 } else {
12680 break :load_operand .{ loaded, undefined };
12681 }
12682 },
12683 }
1308812684 };
1308912685
13090 assert(branch_hints.items.len == cases_len + 1);
13091
13092 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr).@"struct".fields.len +
13093 cases_extra.items.len + else_body.len +
13094 (std.math.divCeil(usize, branch_hints.items.len, 10) catch unreachable)); // branch hints
13095
13096 const payload_index = sema.addExtraAssumeCapacity(Air.SwitchBr{
13097 .cases_len = @intCast(cases_len),
13098 .else_body_len = @intCast(else_body.len),
13099 });
12686 const payload_inst: Zir.Inst.Index = if (capture != .none) inst: {
12687 const payload_inst = zir_switch.payload_capture_placeholder.unwrap() orelse switch_inst;
12688 const payload_ref = try sema.analyzeSwitchPayloadCapture(
12689 case_block,
12690 operand,
12691 operand_val,
12692 operand_ptr,
12693 operand_ty,
12694 operand_src,
12695 capture_src,
12696 capture == .by_ref,
12697 kind == .special,
12698 switch (kind) {
12699 .item_refs => |item_refs| item_refs,
12700 .has_ranges, .special => undefined,
12701 },
12702 inline_case_capture,
12703 else_err_ty,
12704 );
12705 assert(!sema.typeOf(payload_ref).isNoReturn(sema.pt.zcu));
12706 sema.inst_map.putAssumeCapacity(payload_inst, payload_ref);
12707 break :inst payload_inst;
12708 } else undefined;
12709 defer if (capture != .none) assert(sema.inst_map.remove(payload_inst));
1310012710
13101 {
13102 // Add branch hints.
13103 var cur_bag: u32 = 0;
13104 for (branch_hints.items, 0..) |hint, idx| {
13105 const idx_in_bag = idx % 10;
13106 cur_bag |= @as(u32, @intFromEnum(hint)) << @intCast(idx_in_bag * 3);
13107 if (idx_in_bag == 9) {
13108 sema.air_extra.appendAssumeCapacity(cur_bag);
13109 cur_bag = 0;
13110 }
13111 }
13112 if (branch_hints.items.len % 10 != 0) {
13113 sema.air_extra.appendAssumeCapacity(cur_bag);
13114 }
13115 }
13116 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(cases_extra.items));
13117 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(else_body));
12711 const tag_inst: Zir.Inst.Index = if (has_tag_capture) inst: {
12712 const tag_inst = zir_switch.tag_capture_placeholder.unwrap() orelse switch_inst;
12713 const tag_ref = try sema.analyzeSwitchTagCapture(
12714 case_block,
12715 operand_val,
12716 operand_ty,
12717 capture_src,
12718 inline_case_capture,
12719 );
12720 sema.inst_map.putAssumeCapacity(tag_inst, tag_ref);
12721 break :inst tag_inst;
12722 } else undefined;
12723 defer if (has_tag_capture) assert(sema.inst_map.remove(tag_inst));
1311812724
13119 const has_any_continues = spa.operand == .loop and child_block.label.?.merges.extra_insts.items.len > 0;
12725 if (zir_switch.has_continue) sema.inst_map.putAssumeCapacity(switch_inst, .fromType(raw_operand_ty));
12726 defer if (zir_switch.has_continue) assert(sema.inst_map.remove(switch_inst));
1312012727
13121 return try child_block.addInst(.{
13122 .tag = if (has_any_continues) .loop_switch_br else .switch_br,
13123 .data = .{ .pl_op = .{
13124 .operand = operand,
13125 .payload = payload_index,
13126 } },
13127 });
12728 return sema.analyzeBodyRuntimeBreak(case_block, prong_body);
1312812729}
1312912730
13130fn resolveSwitchComptimeLoop(
12731fn analyzeSwitchTagCapture(
1313112732 sema: *Sema,
13132 init_spa: SwitchProngAnalysis,
13133 child_block: *Block,
13134 maybe_ptr_operand_ty: Type,
13135 cond_ty: Type,
13136 init_cond_val: Value,
13137 switch_node_offset: std.zig.Ast.Node.Offset,
13138 special_members_only: ?SpecialProng,
13139 special_generic: SpecialProng,
13140 special_generic_is_under: bool,
13141 case_vals: std.ArrayList(Air.Inst.Ref),
13142 scalar_cases_len: u32,
13143 multi_cases_len: u32,
13144 err_set: bool,
13145 empty_enum: bool,
13146 operand_is_ref: bool,
12733 case_block: *Block,
12734 /// May be `undefined` if `inline_case_capture` is not `.none`.
12735 operand_val: Air.Inst.Ref,
12736 operand_ty: Type,
12737 capture_src: LazySrcLoc,
12738 inline_case_capture: Air.Inst.Ref,
1314712739) CompileError!Air.Inst.Ref {
13148 var spa = init_spa;
13149 var cond_val = init_cond_val;
13150
13151 while (true) {
13152 if (resolveSwitchComptime(
13153 sema,
13154 spa,
13155 child_block,
13156 spa.operand.simple.cond,
13157 cond_val,
13158 cond_ty,
13159 switch_node_offset,
13160 special_members_only,
13161 special_generic,
13162 special_generic_is_under,
13163 case_vals,
13164 scalar_cases_len,
13165 multi_cases_len,
13166 err_set,
13167 empty_enum,
13168 )) |result| {
13169 return result;
13170 } else |err| switch (err) {
13171 error.ComptimeBreak => {
13172 const break_inst = sema.code.instructions.get(@intFromEnum(sema.comptime_break_inst));
13173 if (break_inst.tag != .switch_continue) return error.ComptimeBreak;
13174 const extra = sema.code.extraData(Zir.Inst.Break, break_inst.data.@"break".payload_index).data;
13175 if (extra.block_inst != spa.switch_block_inst) return error.ComptimeBreak;
13176 // This is a `switch_continue` targeting this block. Change the operand and start over.
13177 const src = child_block.nodeOffset(extra.operand_src_node.unwrap().?);
13178 const new_operand_uncoerced = try sema.resolveInst(break_inst.data.@"break".operand);
13179 const new_operand = try sema.coerce(child_block, maybe_ptr_operand_ty, new_operand_uncoerced, src);
13180
13181 try sema.emitBackwardBranch(child_block, src);
13182
13183 const val, const ref = if (operand_is_ref)
13184 .{ try sema.analyzeLoad(child_block, src, new_operand, src), new_operand }
13185 else
13186 .{ new_operand, undefined };
12740 const pt = sema.pt;
12741 const zcu = pt.zcu;
1318712742
13188 const cond_ref = try sema.switchCond(child_block, src, val);
12743 const tag_capture_src: LazySrcLoc = .{
12744 .base_node_inst = capture_src.base_node_inst,
12745 .offset = .{ .switch_tag_capture = capture_src.offset.switch_capture },
12746 };
1318912747
13190 cond_val = try sema.resolveConstDefinedValue(child_block, src, cond_ref, null);
13191 spa.operand = .{ .simple = .{
13192 .by_val = val,
13193 .by_ref = ref,
13194 .cond = cond_ref,
13195 } };
13196 },
13197 else => |e| return e,
13198 }
12748 if (operand_ty.zigTypeTag(zcu) != .@"union") {
12749 return sema.fail(case_block, tag_capture_src, "cannot capture tag of non-union type '{f}'", .{
12750 operand_ty.fmt(pt),
12751 });
12752 }
12753 if (inline_case_capture != .none) {
12754 return inline_case_capture; // this already is the tag, it's what we're switching on!
1319912755 }
12756 const tag_ty = operand_ty.unionTagType(zcu).?;
12757 return sema.unionToTag(case_block, tag_ty, operand_val, tag_capture_src);
1320012758}
1320112759
13202fn resolveSwitchComptime(
12760fn analyzeSwitchPayloadCapture(
1320312761 sema: *Sema,
13204 spa: SwitchProngAnalysis,
13205 child_block: *Block,
13206 cond_operand: Air.Inst.Ref,
13207 operand_val: Value,
12762 case_block: *Block,
12763 operand: SwitchOperand,
12764 /// May be `undefined` if this is an inline capture and operand is not a union.
12765 operand_val: Air.Inst.Ref,
12766 /// May be `undefined` if `capture_by_ref` is `false` or if `operand_val` is also `undefined`.
12767 operand_ptr: Air.Inst.Ref,
1320812768 operand_ty: Type,
13209 switch_node_offset: std.zig.Ast.Node.Offset,
13210 special_members_only: ?SpecialProng,
13211 special_generic: SpecialProng,
13212 special_generic_is_under: bool,
13213 case_vals: std.ArrayList(Air.Inst.Ref),
13214 scalar_cases_len: u32,
13215 multi_cases_len: u32,
13216 err_set: bool,
13217 empty_enum: bool,
12769 operand_src: LazySrcLoc,
12770 capture_src: LazySrcLoc,
12771 capture_by_ref: bool,
12772 is_special_prong: bool,
12773 /// May be `undefined` if `is_special_prong` is `true`.
12774 case_vals: []const Air.Inst.Ref,
12775 /// If this is not `.none`, this is an inline capture.
12776 inline_case_capture: Air.Inst.Ref,
12777 else_err_ty: ?Type,
1321812778) CompileError!Air.Inst.Ref {
13219 const zcu = sema.pt.zcu;
13220 const merges = &child_block.label.?.merges;
13221 const resolved_operand_val = try sema.resolveLazyValue(operand_val);
12779 const pt = sema.pt;
12780 const zcu = pt.zcu;
12781 const ip = &zcu.intern_pool;
1322212782
13223 var extra_index: usize = special_generic.end;
13224 {
13225 var scalar_i: usize = 0;
13226 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
13227 extra_index += 1;
13228 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
13229 extra_index += 1;
13230 const body = sema.code.bodySlice(extra_index, info.body_len);
13231 extra_index += info.body_len;
13232
13233 const item = case_vals.items[scalar_i];
13234 const item_val = sema.resolveConstDefinedValue(child_block, LazySrcLoc.unneeded, item, undefined) catch unreachable;
13235 if (operand_val.eql(item_val, operand_ty, sema.pt.zcu)) {
13236 if (err_set) try sema.maybeErrorUnwrapComptime(child_block, body, cond_operand);
13237 return spa.resolveProngComptime(
13238 child_block,
13239 .normal,
13240 body,
13241 info.capture,
13242 child_block.src(.{ .switch_capture = .{
13243 .switch_node_offset = switch_node_offset,
13244 .case_idx = .{ .kind = .scalar, .index = @intCast(scalar_i) },
13245 } }),
13246 &.{item},
13247 if (info.is_inline) cond_operand else .none,
13248 info.has_tag_capture,
13249 merges,
13250 );
12783 const switch_node_offset = operand_src.offset.node_offset_switch_operand;
12784
12785 if (inline_case_capture != .none) {
12786 const item_val = sema.resolveConstDefinedValue(case_block, .unneeded, inline_case_capture, undefined) catch unreachable;
12787 if (operand_ty.zigTypeTag(zcu) == .@"union") {
12788 const field_index: u32 = @intCast(operand_ty.unionTagFieldIndex(item_val, zcu).?);
12789 const union_obj = zcu.typeToUnion(operand_ty).?;
12790 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]);
12791 if (capture_by_ref) {
12792 const operand_ptr_info = sema.typeOf(operand_ptr).ptrInfo(zcu);
12793 const ptr_field_ty = try pt.ptrTypeSema(.{
12794 .child = field_ty.toIntern(),
12795 .flags = .{
12796 .is_const = operand_ptr_info.flags.is_const,
12797 .is_volatile = operand_ptr_info.flags.is_volatile,
12798 .address_space = operand_ptr_info.flags.address_space,
12799 },
12800 });
12801 return case_block.addStructFieldPtr(operand_ptr, field_index, ptr_field_ty);
12802 } else {
12803 if (try sema.resolveDefinedValue(case_block, operand_src, operand_val)) |union_val| {
12804 const tag_and_val = ip.indexToKey(union_val.toIntern()).un;
12805 return .fromIntern(tag_and_val.val);
12806 }
12807 return case_block.addStructFieldVal(operand_val, field_index, field_ty);
1325112808 }
12809 } else if (capture_by_ref) {
12810 return sema.uavRef(item_val.toIntern());
12811 } else {
12812 return inline_case_capture;
1325212813 }
1325312814 }
13254 {
13255 var multi_i: usize = 0;
13256 var case_val_idx: usize = scalar_cases_len;
13257 while (multi_i < multi_cases_len) : (multi_i += 1) {
13258 const items_len = sema.code.extra[extra_index];
13259 extra_index += 1;
13260 const ranges_len = sema.code.extra[extra_index];
13261 extra_index += 1;
13262 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
13263 extra_index += 1 + items_len;
13264 const body = sema.code.bodySlice(extra_index + 2 * ranges_len, info.body_len);
13265
13266 const items = case_vals.items[case_val_idx..][0..items_len];
13267 case_val_idx += items_len;
13268
13269 for (items) |item| {
13270 // Validation above ensured these will succeed.
13271 const item_val = sema.resolveConstDefinedValue(child_block, LazySrcLoc.unneeded, item, undefined) catch unreachable;
13272 if (operand_val.eql(item_val, operand_ty, sema.pt.zcu)) {
13273 if (err_set) try sema.maybeErrorUnwrapComptime(child_block, body, cond_operand);
13274 return spa.resolveProngComptime(
13275 child_block,
13276 .normal,
13277 body,
13278 info.capture,
13279 child_block.src(.{ .switch_capture = .{
13280 .switch_node_offset = switch_node_offset,
13281 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) },
13282 } }),
13283 items,
13284 if (info.is_inline) cond_operand else .none,
13285 info.has_tag_capture,
13286 merges,
13287 );
13288 }
13289 }
1329012815
13291 var range_i: usize = 0;
13292 while (range_i < ranges_len) : (range_i += 1) {
13293 const range_items = case_vals.items[case_val_idx..][0..2];
13294 extra_index += 2;
13295 case_val_idx += 2;
12816 const operand_ptr_ty = if (capture_by_ref) sema.typeOf(operand_ptr) else undefined;
1329612817
13297 // Validation above ensured these will succeed.
13298 const first_val = sema.resolveConstDefinedValue(child_block, LazySrcLoc.unneeded, range_items[0], undefined) catch unreachable;
13299 const last_val = sema.resolveConstDefinedValue(child_block, LazySrcLoc.unneeded, range_items[1], undefined) catch unreachable;
13300 if ((try sema.compareAll(resolved_operand_val, .gte, first_val, operand_ty)) and
13301 (try sema.compareAll(resolved_operand_val, .lte, last_val, operand_ty)))
13302 {
13303 if (err_set) try sema.maybeErrorUnwrapComptime(child_block, body, cond_operand);
13304 return spa.resolveProngComptime(
13305 child_block,
13306 .normal,
13307 body,
13308 info.capture,
13309 child_block.src(.{ .switch_capture = .{
13310 .switch_node_offset = switch_node_offset,
13311 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) },
13312 } }),
13313 undefined, // case_vals may be undefined for ranges
13314 if (info.is_inline) cond_operand else .none,
13315 info.has_tag_capture,
13316 merges,
13317 );
12818 if (is_special_prong) {
12819 if (capture_by_ref) return operand_ptr;
12820 return switch (operand_ty.zigTypeTag(zcu)) {
12821 .error_set => e: {
12822 if (else_err_ty) |err_ty| {
12823 break :e sema.bitCast(case_block, err_ty, operand_val, operand_src, null);
12824 } else {
12825 try sema.analyzeUnreachable(case_block, operand_src, false);
12826 break :e .unreachable_value;
1331812827 }
13319 }
13320
13321 extra_index += info.body_len;
13322 }
13323 }
13324 if (err_set) try sema.maybeErrorUnwrapComptime(child_block, special_generic.body, cond_operand);
13325 if (empty_enum) {
13326 return .void_value;
13327 }
13328 if (special_members_only) |special| {
13329 assert(operand_ty.isNonexhaustiveEnum(zcu));
13330 if (operand_ty.enumTagFieldIndex(operand_val, zcu)) |_| {
13331 return spa.resolveProngComptime(
13332 child_block,
13333 .special,
13334 special.body,
13335 special.capture,
13336 child_block.src(.{ .switch_capture = .{
13337 .switch_node_offset = switch_node_offset,
13338 .case_idx = .special_else,
13339 } }),
13340 undefined, // case_vals may be undefined for special prongs
13341 if (special.is_inline) cond_operand else .none,
13342 special.has_tag_capture,
13343 merges,
13344 );
13345 }
12828 },
12829 else => operand_val,
12830 };
1334612831 }
1334712832
13348 return spa.resolveProngComptime(
13349 child_block,
13350 .special,
13351 special_generic.body,
13352 special_generic.capture,
13353 child_block.src(.{ .switch_capture = .{
13354 .switch_node_offset = switch_node_offset,
13355 .case_idx = if (special_generic_is_under)
13356 .special_under
13357 else
13358 .special_else,
13359 } }),
13360 undefined, // case_vals may be undefined for special prongs
13361 if (special_generic.is_inline) cond_operand else .none,
13362 special_generic.has_tag_capture,
13363 merges,
13364 );
13365}
12833 switch (operand_ty.zigTypeTag(zcu)) {
12834 .@"union" => {
12835 const union_obj = zcu.typeToUnion(operand_ty).?;
12836 const first_item_val = sema.resolveConstDefinedValue(case_block, .unneeded, case_vals[0], undefined) catch unreachable;
1336612837
13367const RangeSetUnhandledIterator = struct {
13368 pt: Zcu.PerThread,
13369 cur: ?InternPool.Index,
13370 max: InternPool.Index,
13371 range_i: usize,
13372 ranges: []const RangeSet.Range,
13373 limbs: []math.big.Limb,
12838 const first_field_index: u32 = zcu.unionTagFieldIndex(union_obj, first_item_val).?;
12839 const first_field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[first_field_index]);
1337412840
13375 const preallocated_limbs = math.big.int.calcTwosCompLimbCount(128);
12841 const field_indices = try sema.arena.alloc(u32, case_vals.len);
12842 for (case_vals, field_indices) |item, *field_idx| {
12843 const item_val = sema.resolveConstDefinedValue(case_block, .unneeded, item, undefined) catch unreachable;
12844 field_idx.* = zcu.unionTagFieldIndex(union_obj, item_val).?;
12845 }
1337612846
13377 fn init(sema: *Sema, ty: Type, range_set: RangeSet) !RangeSetUnhandledIterator {
13378 const pt = sema.pt;
13379 const int_type = pt.zcu.intern_pool.indexToKey(ty.toIntern()).int_type;
13380 const needed_limbs = math.big.int.calcTwosCompLimbCount(int_type.bits);
13381 return .{
13382 .pt = pt,
13383 .cur = (try ty.minInt(pt, ty)).toIntern(),
13384 .max = (try ty.maxInt(pt, ty)).toIntern(),
13385 .range_i = 0,
13386 .ranges = range_set.ranges.items,
13387 .limbs = if (needed_limbs > preallocated_limbs)
13388 try sema.arena.alloc(math.big.Limb, needed_limbs)
13389 else
13390 &.{},
13391 };
13392 }
12847 // Fast path: if all the operands are the same type already, we don't need to hit
12848 // PTR! This will also allow us to emit simpler code.
12849 const same_types = for (field_indices[1..]) |field_idx| {
12850 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]);
12851 if (!field_ty.eql(first_field_ty, zcu)) break false;
12852 } else true;
1339312853
13394 fn addOne(it: *const RangeSetUnhandledIterator, val: InternPool.Index) !?InternPool.Index {
13395 if (val == it.max) return null;
13396 const int = it.pt.zcu.intern_pool.indexToKey(val).int;
12854 const capture_ty: Type = capture_ty: {
12855 if (same_types) break :capture_ty first_field_ty;
12856 // We need values to run PTR on, so make a bunch of undef constants.
12857 const dummy_captures = try sema.arena.alloc(Air.Inst.Ref, case_vals.len);
12858 for (dummy_captures, field_indices) |*dummy, field_idx| {
12859 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]);
12860 dummy.* = try pt.undefRef(field_ty);
12861 }
1339712862
13398 switch (int.storage) {
13399 inline .u64, .i64 => |val_int| {
13400 const next_int = @addWithOverflow(val_int, 1);
13401 if (next_int[1] == 0)
13402 return (try it.pt.intValue(.fromInterned(int.ty), next_int[0])).toIntern();
13403 },
13404 .big_int => {},
13405 .lazy_align, .lazy_size => unreachable,
13406 }
12863 const case_srcs = try sema.arena.alloc(?LazySrcLoc, case_vals.len);
12864 for (case_srcs, 0..) |*case_src, item_i| {
12865 case_src.* = .{
12866 .base_node_inst = capture_src.base_node_inst,
12867 .offset = .{ .switch_case_item = .{
12868 .switch_node_offset = switch_node_offset,
12869 .case_idx = capture_src.offset.switch_capture.case_idx,
12870 .item_idx = .{ .kind = .single, .value = @intCast(item_i) },
12871 } },
12872 };
12873 }
12874
12875 break :capture_ty sema.resolvePeerTypes(
12876 case_block,
12877 capture_src,
12878 dummy_captures,
12879 .{ .override = case_srcs },
12880 ) catch |err| switch (err) {
12881 error.AnalysisFail => {
12882 const msg = sema.err orelse return error.AnalysisFail;
12883 try sema.reparentOwnedErrorMsg(capture_src, msg, "capture group with incompatible types", .{});
12884 return error.AnalysisFail;
12885 },
12886 else => |e| return e,
12887 };
12888 };
1340712889
13408 var val_space: InternPool.Key.Int.Storage.BigIntSpace = undefined;
13409 const val_bigint = int.storage.toBigInt(&val_space);
12890 // By-reference captures have some further restrictions which make them easier to emit
12891 if (capture_by_ref) {
12892 const operand_ptr_info = operand_ptr_ty.ptrInfo(zcu);
12893 const capture_ptr_ty = resolve: {
12894 // By-ref captures of hetereogeneous types are only allowed if all field
12895 // pointer types are peer resolvable to each other.
12896 // We need values to run PTR on, so make a bunch of undef constants.
12897 const dummy_captures = try sema.arena.alloc(Air.Inst.Ref, case_vals.len);
12898 for (field_indices, dummy_captures) |field_idx, *dummy| {
12899 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]);
12900 const field_ptr_ty = try pt.ptrTypeSema(.{
12901 .child = field_ty.toIntern(),
12902 .flags = .{
12903 .is_const = operand_ptr_info.flags.is_const,
12904 .is_volatile = operand_ptr_info.flags.is_volatile,
12905 .address_space = operand_ptr_info.flags.address_space,
12906 .alignment = union_obj.fieldAlign(ip, field_idx),
12907 },
12908 });
12909 dummy.* = try pt.undefRef(field_ptr_ty);
12910 }
12911 const case_srcs = try sema.arena.alloc(?LazySrcLoc, case_vals.len);
12912 for (case_srcs, 0..) |*case_src, item_i| {
12913 case_src.* = .{
12914 .base_node_inst = capture_src.base_node_inst,
12915 .offset = .{ .switch_case_item = .{
12916 .switch_node_offset = switch_node_offset,
12917 .case_idx = capture_src.offset.switch_capture.case_idx,
12918 .item_idx = .{ .kind = .single, .value = @intCast(item_i) },
12919 } },
12920 };
12921 }
1341012922
13411 var result_limbs: [preallocated_limbs]math.big.Limb = undefined;
13412 var result_bigint = math.big.int.Mutable.init(
13413 if (it.limbs.len > 0) it.limbs else &result_limbs,
13414 0,
13415 );
12923 break :resolve sema.resolvePeerTypes(
12924 case_block,
12925 capture_src,
12926 dummy_captures,
12927 .{ .override = case_srcs },
12928 ) catch |err| switch (err) {
12929 error.AnalysisFail => {
12930 const msg = sema.err orelse return error.AnalysisFail;
12931 try sema.errNote(capture_src, msg, "this coercion is only possible when capturing by value", .{});
12932 try sema.reparentOwnedErrorMsg(capture_src, msg, "capture group with incompatible types", .{});
12933 return error.AnalysisFail;
12934 },
12935 else => |e| return e,
12936 };
12937 };
1341612938
13417 result_bigint.addScalar(val_bigint, 1);
13418 return (try it.pt.intValue_big(.fromInterned(int.ty), result_bigint.toConst())).toIntern();
13419 }
12939 if (try sema.resolveDefinedValue(case_block, operand_src, operand_ptr)) |op_ptr_val| {
12940 if (op_ptr_val.isUndef(zcu)) return pt.undefRef(capture_ptr_ty);
12941 const field_ptr_val = try op_ptr_val.ptrField(first_field_index, pt);
12942 return .fromValue(try pt.getCoerced(field_ptr_val, capture_ptr_ty));
12943 }
1342012944
13421 fn next(it: *RangeSetUnhandledIterator) !?InternPool.Index {
13422 var cur = it.cur orelse return null;
13423 while (it.range_i < it.ranges.len and cur == it.ranges[it.range_i].first) {
13424 defer it.range_i += 1;
13425 cur = (try it.addOne(it.ranges[it.range_i].last)) orelse {
13426 it.cur = null;
13427 return null;
13428 };
13429 }
13430 it.cur = try it.addOne(cur);
13431 return cur;
13432 }
13433};
12945 try sema.requireRuntimeBlock(case_block, operand_src, null);
12946 return case_block.addStructFieldPtr(operand_ptr, first_field_index, capture_ptr_ty);
12947 }
1343412948
13435const ResolvedSwitchItem = struct {
13436 ref: Air.Inst.Ref,
13437 val: InternPool.Index,
13438};
13439fn resolveSwitchItemVal(
13440 sema: *Sema,
13441 block: *Block,
13442 item_ref: Zir.Inst.Ref,
13443 /// Coerce `item_ref` to this type.
13444 coerce_ty: Type,
13445 item_src: LazySrcLoc,
13446) CompileError!ResolvedSwitchItem {
13447 const uncoerced_item = try sema.resolveInst(item_ref);
12949 if (try sema.resolveDefinedValue(case_block, operand_src, operand_val)) |operand_val_val| {
12950 if (operand_val_val.isUndef(zcu)) return pt.undefRef(capture_ty);
12951 const union_val = ip.indexToKey(operand_val_val.toIntern()).un;
12952 if (Value.fromInterned(union_val.tag).isUndef(zcu)) return pt.undefRef(capture_ty);
12953 const uncoerced: Air.Inst.Ref = .fromIntern(union_val.val);
12954 return sema.coerce(case_block, capture_ty, uncoerced, operand_src);
12955 }
1344812956
13449 // Constructing a LazySrcLoc is costly because we only have the switch AST node.
13450 // Only if we know for sure we need to report a compile error do we resolve the
13451 // full source locations.
12957 try sema.requireRuntimeBlock(case_block, operand_src, null);
1345212958
13453 const item = try sema.coerce(block, coerce_ty, uncoerced_item, item_src);
12959 if (same_types) {
12960 return case_block.addStructFieldVal(operand_val, first_field_index, capture_ty);
12961 }
1345412962
13455 const maybe_lazy = try sema.resolveConstDefinedValue(block, item_src, item, .{ .simple = .switch_item });
12963 // We may have to emit a switch block which coerces the operand to the capture type.
12964 // If we can, try to avoid that using in-memory coercions.
12965 const first_non_imc = in_mem: {
12966 for (field_indices, 0..) |field_idx, i| {
12967 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]);
12968 if (.ok != try sema.coerceInMemoryAllowed(case_block, capture_ty, field_ty, false, zcu.getTarget(), .unneeded, .unneeded, null)) {
12969 break :in_mem i;
12970 }
12971 }
12972 // All fields are in-memory coercible to the resolved type!
12973 // Just take the first field and bitcast the result.
12974 const uncoerced = try case_block.addStructFieldVal(operand_val, first_field_index, first_field_ty);
12975 return case_block.addBitCast(capture_ty, uncoerced);
12976 };
1345612977
13457 const val = try sema.resolveLazyValue(maybe_lazy);
13458 const new_item = if (val.toIntern() != maybe_lazy.toIntern()) blk: {
13459 break :blk Air.internedToRef(val.toIntern());
13460 } else item;
12978 // By-val capture with heterogeneous types which are not all in-memory coercible to
12979 // the resolved capture type. We finally have to fall back to the ugly method.
1346112980
13462 return .{ .ref = new_item, .val = val.toIntern() };
13463}
12981 // However, let's first track which operands are in-memory coercible. There may well
12982 // be several, and we can squash all of these cases into the same switch prong using
12983 // a simple bitcast. We'll make this the 'else' prong.
1346412984
13465fn validateErrSetSwitch(
13466 sema: *Sema,
13467 block: *Block,
13468 seen_errors: *SwitchErrorSet,
13469 case_vals: *std.ArrayList(Air.Inst.Ref),
13470 operand_ty: Type,
13471 inst_data: @FieldType(Zir.Inst.Data, "pl_node"),
13472 scalar_cases_len: u32,
13473 multi_cases_len: u32,
13474 else_case: struct { body: []const Zir.Inst.Index, end: usize, src: LazySrcLoc },
13475 has_else: bool,
13476) CompileError!?Type {
13477 const gpa = sema.gpa;
13478 const pt = sema.pt;
13479 const zcu = pt.zcu;
13480 const ip = &zcu.intern_pool;
12985 var in_mem_coercible: std.DynamicBitSet = try .initFull(sema.arena, field_indices.len);
12986 in_mem_coercible.unset(first_non_imc);
12987 {
12988 const next = first_non_imc + 1;
12989 for (field_indices[next..], next..) |field_idx, i| {
12990 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]);
12991 if (.ok != try sema.coerceInMemoryAllowed(case_block, capture_ty, field_ty, false, zcu.getTarget(), .unneeded, .unneeded, null)) {
12992 in_mem_coercible.unset(i);
12993 }
12994 }
12995 }
1348112996
13482 const src_node_offset = inst_data.src_node;
13483 const src = block.nodeOffset(src_node_offset);
12997 const capture_block_inst = try case_block.addInstAsIndex(.{
12998 .tag = .block,
12999 .data = .{
13000 .ty_pl = .{
13001 .ty = .fromType(capture_ty),
13002 .payload = undefined, // updated below
13003 },
13004 },
13005 });
1348413006
13485 var extra_index: usize = else_case.end;
13486 {
13487 var scalar_i: u32 = 0;
13488 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
13489 const item_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]);
13490 extra_index += 1;
13491 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
13492 extra_index += 1 + info.body_len;
13007 const prong_count = field_indices.len - in_mem_coercible.count();
1349313008
13494 case_vals.appendAssumeCapacity(try sema.validateSwitchItemError(
13495 block,
13496 seen_errors,
13497 item_ref,
13498 operand_ty,
13499 block.src(.{ .switch_case_item = .{
13500 .switch_node_offset = src_node_offset,
13501 .case_idx = .{ .kind = .scalar, .index = @intCast(scalar_i) },
13502 .item_idx = .{ .kind = .single, .index = 0 },
13503 } }),
13504 ));
13505 }
13506 }
13507 {
13508 var multi_i: u32 = 0;
13509 while (multi_i < multi_cases_len) : (multi_i += 1) {
13510 const items_len = sema.code.extra[extra_index];
13511 extra_index += 1;
13512 const ranges_len = sema.code.extra[extra_index];
13513 extra_index += 1;
13514 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
13515 extra_index += 1;
13516 const items = sema.code.refSlice(extra_index, items_len);
13517 extra_index += items_len + info.body_len;
13009 const estimated_extra = prong_count * 6 + (prong_count / 10); // 2 for Case, 1 item, probably 3 insts; plus hints
13010 var cases_extra = try std.array_list.Managed(u32).initCapacity(sema.gpa, estimated_extra);
13011 defer cases_extra.deinit();
1351813012
13519 try case_vals.ensureUnusedCapacity(gpa, items.len);
13520 for (items, 0..) |item_ref, item_i| {
13521 case_vals.appendAssumeCapacity(try sema.validateSwitchItemError(
13522 block,
13523 seen_errors,
13524 item_ref,
13525 operand_ty,
13526 block.src(.{ .switch_case_item = .{
13527 .switch_node_offset = src_node_offset,
13528 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) },
13529 .item_idx = .{ .kind = .single, .index = @intCast(item_i) },
13530 } }),
13531 ));
13013 {
13014 // All branch hints are `.none`, so just add zero elems.
13015 comptime assert(@intFromEnum(std.builtin.BranchHint.none) == 0);
13016 const need_elems = std.math.divCeil(usize, prong_count + 1, 10) catch unreachable;
13017 try cases_extra.appendNTimes(0, need_elems);
1353213018 }
1353313019
13534 try sema.validateSwitchNoRange(block, ranges_len, operand_ty, src_node_offset);
13535 }
13536 }
13537
13538 switch (try sema.resolveInferredErrorSetTy(block, src, operand_ty.toIntern())) {
13539 .anyerror_type => {
13540 if (!has_else) {
13541 return sema.fail(
13542 block,
13543 src,
13544 "else prong required when switching on type 'anyerror'",
13545 .{},
13546 );
13547 }
13548 return .anyerror;
13549 },
13550 else => |err_set_ty_index| else_validation: {
13551 const error_names = ip.indexToKey(err_set_ty_index).error_set_type.names;
13552 var maybe_msg: ?*Zcu.ErrorMsg = null;
13553 errdefer if (maybe_msg) |msg| msg.destroy(sema.gpa);
13020 {
13021 // Non-bitcast cases
13022 var it = in_mem_coercible.iterator(.{ .kind = .unset });
13023 while (it.next()) |idx| {
13024 var coerce_block = case_block.makeSubBlock();
13025 defer coerce_block.instructions.deinit(sema.gpa);
1355413026
13555 for (error_names.get(ip)) |error_name| {
13556 if (!seen_errors.contains(error_name) and !has_else) {
13557 const msg = maybe_msg orelse blk: {
13558 maybe_msg = try sema.errMsg(
13559 src,
13560 "switch must handle all possibilities",
13561 .{},
13562 );
13563 break :blk maybe_msg.?;
13027 const case_src: LazySrcLoc = .{
13028 .base_node_inst = capture_src.base_node_inst,
13029 .offset = .{ .switch_case_item = .{
13030 .switch_node_offset = switch_node_offset,
13031 .case_idx = capture_src.offset.switch_capture.case_idx,
13032 .item_idx = .{ .kind = .single, .value = @intCast(idx) },
13033 } },
1356413034 };
1356513035
13566 try sema.errNote(
13567 src,
13568 msg,
13569 "unhandled error value: 'error.{f}'",
13570 .{error_name.fmt(ip)},
13571 );
13036 const field_idx = field_indices[idx];
13037 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]);
13038 const uncoerced = try coerce_block.addStructFieldVal(operand_val, field_idx, field_ty);
13039 const coerced = try sema.coerce(&coerce_block, capture_ty, uncoerced, case_src);
13040 _ = try coerce_block.addBr(capture_block_inst, coerced);
13041
13042 try cases_extra.ensureUnusedCapacity(@typeInfo(Air.SwitchBr.Case).@"struct".fields.len +
13043 1 + // `item`, no ranges
13044 coerce_block.instructions.items.len);
13045 cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{
13046 .items_len = 1,
13047 .ranges_len = 0,
13048 .body_len = @intCast(coerce_block.instructions.items.len),
13049 }));
13050 cases_extra.appendAssumeCapacity(@intFromEnum(case_vals[idx])); // item
13051 cases_extra.appendSliceAssumeCapacity(@ptrCast(coerce_block.instructions.items)); // body
1357213052 }
1357313053 }
13054 const else_body_len = len: {
13055 // 'else' prong uses a bitcast
13056 var coerce_block = case_block.makeSubBlock();
13057 defer coerce_block.instructions.deinit(sema.gpa);
1357413058
13575 if (maybe_msg) |msg| {
13576 maybe_msg = null;
13577 try sema.addDeclaredHereNote(msg, operand_ty);
13578 return sema.failWithOwnedErrorMsg(block, msg);
13579 }
13059 const first_imc_item_idx = in_mem_coercible.findFirstSet().?;
13060 const first_imc_field_idx = field_indices[first_imc_item_idx];
13061 const first_imc_field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[first_imc_field_idx]);
13062 const uncoerced = try coerce_block.addStructFieldVal(operand_val, first_imc_field_idx, first_imc_field_ty);
13063 const coerced = try coerce_block.addBitCast(capture_ty, uncoerced);
13064 _ = try coerce_block.addBr(capture_block_inst, coerced);
13065
13066 try cases_extra.appendSlice(@ptrCast(coerce_block.instructions.items));
13067 break :len coerce_block.instructions.items.len;
13068 };
1358013069
13581 if (has_else and seen_errors.count() == error_names.len) {
13582 // In order to enable common patterns for generic code allow simple else bodies
13583 // else => unreachable,
13584 // else => return,
13585 // else => |e| return e,
13586 // even if all the possible errors were already handled.
13587 const tags = sema.code.instructions.items(.tag);
13588 const datas = sema.code.instructions.items(.data);
13589 for (else_case.body) |else_inst| switch (tags[@intFromEnum(else_inst)]) {
13590 .dbg_stmt,
13591 .dbg_var_val,
13592 .ret_type,
13593 .as_node,
13594 .ret_node,
13595 .@"unreachable",
13596 .@"defer",
13597 .defer_err_code,
13598 .err_union_code,
13599 .ret_err_value_code,
13600 .save_err_ret_index,
13601 .restore_err_ret_index_unconditional,
13602 .restore_err_ret_index_fn_entry,
13603 .is_non_err,
13604 .ret_is_non_err,
13605 .condbr,
13606 => {},
13607 .extended => switch (datas[@intFromEnum(else_inst)].extended.opcode) {
13608 .restore_err_ret_index => {},
13609 else => break,
13070 try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.SwitchBr).@"struct".fields.len +
13071 cases_extra.items.len +
13072 @typeInfo(Air.Block).@"struct".fields.len +
13073 1);
13074
13075 const switch_br_inst: u32 = @intCast(sema.air_instructions.len);
13076 try sema.air_instructions.append(sema.gpa, .{
13077 .tag = .switch_br,
13078 .data = .{
13079 .pl_op = .{
13080 .operand = undefined, // set by switch below
13081 .payload = sema.addExtraAssumeCapacity(Air.SwitchBr{
13082 .cases_len = @intCast(prong_count),
13083 .else_body_len = @intCast(else_body_len),
13084 }),
1361013085 },
13611 else => break,
13612 } else break :else_validation;
13086 },
13087 });
13088 sema.air_extra.appendSliceAssumeCapacity(cases_extra.items);
13089
13090 // Set up block body
13091 switch (operand) {
13092 .simple => |s| {
13093 const air_datas = sema.air_instructions.items(.data);
13094 air_datas[switch_br_inst].pl_op.operand = s.cond;
13095 air_datas[@intFromEnum(capture_block_inst)].ty_pl.payload =
13096 sema.addExtraAssumeCapacity(Air.Block{ .body_len = 1 });
13097 sema.air_extra.appendAssumeCapacity(switch_br_inst);
13098 },
13099 .loop => {
13100 // The block must first extract the tag from the loaded union.
13101 const tag_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len);
13102 try sema.air_instructions.append(sema.gpa, .{
13103 .tag = .get_union_tag,
13104 .data = .{ .ty_op = .{
13105 .ty = .fromIntern(union_obj.enum_tag_ty),
13106 .operand = operand_val,
13107 } },
13108 });
13109 const air_datas = sema.air_instructions.items(.data);
13110 air_datas[switch_br_inst].pl_op.operand = tag_inst.toRef();
13111 air_datas[@intFromEnum(capture_block_inst)].ty_pl.payload =
13112 sema.addExtraAssumeCapacity(Air.Block{ .body_len = 2 });
13113 sema.air_extra.appendAssumeCapacity(@intFromEnum(tag_inst));
13114 sema.air_extra.appendAssumeCapacity(switch_br_inst);
13115 },
13116 }
1361313117
13118 return capture_block_inst.toRef();
13119 },
13120 .error_set => {
13121 if (capture_by_ref) {
1361413122 return sema.fail(
13615 block,
13616 else_case.src,
13617 "unreachable else prong; all cases already handled",
13123 case_block,
13124 capture_src,
13125 "error set cannot be captured by reference",
1361813126 .{},
1361913127 );
1362013128 }
1362113129
13622 var names: InferredErrorSet.NameMap = .{};
13623 try names.ensureUnusedCapacity(sema.arena, error_names.len);
13624 for (error_names.get(ip)) |error_name| {
13625 if (seen_errors.contains(error_name)) continue;
13130 if (case_vals.len == 1) {
13131 const item_val = sema.resolveConstDefinedValue(case_block, .unneeded, case_vals[0], undefined) catch unreachable;
13132 const item_ty = try pt.singleErrorSetType(item_val.getErrorName(zcu).unwrap().?);
13133 return sema.bitCast(case_block, item_ty, operand_val, operand_src, null);
13134 }
1362613135
13627 names.putAssumeCapacityNoClobber(error_name, {});
13136 var names: InferredErrorSet.NameMap = .{};
13137 try names.ensureUnusedCapacity(sema.arena, case_vals.len);
13138 for (case_vals) |err| {
13139 const err_val = sema.resolveConstDefinedValue(case_block, .unneeded, err, undefined) catch unreachable;
13140 names.putAssumeCapacityNoClobber(err_val.getErrorName(zcu).unwrap().?, {});
13141 }
13142 const error_ty = try pt.errorSetFromUnsortedNames(names.keys());
13143 return sema.bitCast(case_block, error_ty, operand_val, operand_src, null);
13144 },
13145 else => {
13146 // In this case the capture value is just the passed-through value
13147 // of the switch condition.
13148 if (capture_by_ref) {
13149 return operand_ptr;
13150 } else {
13151 return operand_val;
1362813152 }
13629 // No need to keep the hash map metadata correct; here we
13630 // extract the (sorted) keys only.
13631 return try pt.errorSetFromUnsortedNames(names.keys());
1363213153 },
1363313154 }
13634 return null;
1363513155}
1363613156
13637fn validateSwitchRange(
13638 sema: *Sema,
13639 block: *Block,
13640 range_set: *RangeSet,
13641 first_ref: Zir.Inst.Ref,
13642 last_ref: Zir.Inst.Ref,
13643 operand_ty: Type,
13644 item_src: LazySrcLoc,
13645) CompileError![2]Air.Inst.Ref {
13646 const first_src: LazySrcLoc = .{
13647 .base_node_inst = item_src.base_node_inst,
13648 .offset = .{ .switch_case_item_range_first = item_src.offset.switch_case_item },
13649 };
13650 const last_src: LazySrcLoc = .{
13651 .base_node_inst = item_src.base_node_inst,
13652 .offset = .{ .switch_case_item_range_last = item_src.offset.switch_case_item },
13653 };
13654 const first = try sema.resolveSwitchItemVal(block, first_ref, operand_ty, first_src);
13655 const last = try sema.resolveSwitchItemVal(block, last_ref, operand_ty, last_src);
13656 if (try Value.fromInterned(first.val).compareAll(.gt, Value.fromInterned(last.val), operand_ty, sema.pt)) {
13657 return sema.fail(block, item_src, "range start value is greater than the end value", .{});
13658 }
13659 const maybe_prev_src = try range_set.add(first.val, last.val, item_src);
13660 try sema.validateSwitchDupe(block, maybe_prev_src, item_src);
13661 return .{ first.ref, last.ref };
13662}
13157const ResolvedSwitchItem = struct {
13158 ref: Air.Inst.Ref,
13159 val: Value,
13160};
13161const ResolvedSwitchItemAndExtraIndex = struct { ResolvedSwitchItem, usize };
1366313162
13664fn validateSwitchItemInt(
13163fn resolveSwitchItem(
1366513164 sema: *Sema,
1366613165 block: *Block,
13667 range_set: *RangeSet,
13668 item_ref: Zir.Inst.Ref,
13669 operand_ty: Type,
1367013166 item_src: LazySrcLoc,
13671) CompileError!Air.Inst.Ref {
13672 const item = try sema.resolveSwitchItemVal(block, item_ref, operand_ty, item_src);
13673 const maybe_prev_src = try range_set.add(item.val, item.val, item_src);
13674 try sema.validateSwitchDupe(block, maybe_prev_src, item_src);
13675 return item.ref;
13676}
13167 item_ty: Type,
13168 item_info: Zir.Inst.SwitchBlock.ItemInfo,
13169 extra_index: usize,
13170 switch_inst: Zir.Inst.Index,
13171 prong_is_comptime_unreach: bool,
13172 prong_is_inline: bool,
13173) CompileError!ResolvedSwitchItemAndExtraIndex {
13174 const pt = sema.pt;
13175 const zcu = pt.zcu;
13176 const ip = &zcu.intern_pool;
13177 const gpa = sema.gpa;
1367713178
13678fn validateSwitchItemEnum(
13679 sema: *Sema,
13680 block: *Block,
13681 seen_fields: []?LazySrcLoc,
13682 range_set: *RangeSet,
13683 item_ref: Zir.Inst.Ref,
13684 operand_ty: Type,
13685 item_src: LazySrcLoc,
13686) CompileError!Air.Inst.Ref {
13687 const ip = &sema.pt.zcu.intern_pool;
13688 const item = try sema.resolveSwitchItemVal(block, item_ref, operand_ty, item_src);
13689 const int = ip.indexToKey(item.val).enum_tag.int;
13690 const field_index = ip.loadEnumType(ip.typeOf(item.val)).tagValueIndex(ip, int) orelse {
13691 const maybe_prev_src = try range_set.add(int, int, item_src);
13692 try sema.validateSwitchDupe(block, maybe_prev_src, item_src);
13693 return item.ref;
13694 };
13695 const maybe_prev_src = seen_fields[field_index];
13696 seen_fields[field_index] = item_src;
13697 try sema.validateSwitchDupe(block, maybe_prev_src, item_src);
13698 return item.ref;
13699}
13179 var end = extra_index;
13180 const uncoerced: Air.Inst.Ref, const uncoerced_ty: Type = uncoerced: switch (item_info.unwrap()) {
13181 .enum_literal => |str_index| {
13182 const zir_str = sema.code.nullTerminatedString(str_index);
13183 const name = try ip.getOrPutString(gpa, pt.tid, zir_str, .no_embedded_nulls);
13184 const uncoerced = try sema.analyzeDeclLiteral(block, item_src, name, item_ty, false);
13185 break :uncoerced .{ uncoerced, .enum_literal };
13186 },
13187 .error_value => |str_index| {
13188 const zir_str = sema.code.nullTerminatedString(str_index);
13189 const name = try ip.getOrPutString(gpa, pt.tid, zir_str, .no_embedded_nulls);
13190 // Make sure there's an error integer value associated with `name`.
13191 _ = try pt.getErrorValue(name);
13192 const err_set_ty = try pt.singleErrorSetType(name);
13193 const uncoerced = Air.internedToRef(try pt.intern(.{ .err = .{
13194 .ty = err_set_ty.toIntern(),
13195 .name = name,
13196 } }));
13197 break :uncoerced .{ uncoerced, err_set_ty };
13198 },
13199 .number_literal => |zir_ref| {
13200 const uncoerced = try sema.resolveInst(zir_ref);
13201 break :uncoerced .{ uncoerced, sema.typeOf(uncoerced) };
13202 },
13203 .body_len => |body_len| {
13204 const body = sema.code.bodySlice(extra_index, body_len);
13205 end += body.len;
1370013206
13701fn validateSwitchItemError(
13702 sema: *Sema,
13703 block: *Block,
13704 seen_errors: *SwitchErrorSet,
13705 item_ref: Zir.Inst.Ref,
13706 operand_ty: Type,
13707 item_src: LazySrcLoc,
13708) CompileError!Air.Inst.Ref {
13709 const item = try sema.resolveSwitchItemVal(block, item_ref, operand_ty, item_src);
13710 const error_name = sema.pt.zcu.intern_pool.indexToKey(item.val).err.name;
13711 const maybe_prev_src = if (try seen_errors.fetchPut(error_name, item_src)) |prev|
13712 prev.value
13713 else
13714 null;
13715 try sema.validateSwitchDupe(block, maybe_prev_src, item_src);
13716 return item.ref;
13717}
13207 const uncoerced = ref: {
13208 // The result location of item bodies is `.{ .coerce_ty = switch_inst }`.
13209 sema.inst_map.putAssumeCapacity(switch_inst, .fromType(item_ty));
13210 defer assert(sema.inst_map.remove(switch_inst));
13211 break :ref try sema.resolveInlineBody(block, body, switch_inst);
13212 };
13213 break :uncoerced .{ uncoerced, sema.typeOf(uncoerced) };
13214 },
13215 };
13216 const item_ref: Air.Inst.Ref = item_ref: {
13217 if (item_ty.zigTypeTag(zcu) == .error_set and
13218 uncoerced_ty.zigTypeTag(zcu) == .error_set)
13219 {
13220 // We allow prongs with errors which are not part of the error set
13221 // being switched on if their prong body is `=> comptime unreachable,`.
13222 switch (try sema.coerceInMemoryAllowedErrorSets(block, item_ty, uncoerced_ty, item_src, item_src)) {
13223 .ok => if (try sema.resolveValue(uncoerced)) |uncoerced_val| {
13224 break :item_ref try sema.coerceInMemory(uncoerced_val, item_ty);
13225 },
13226 .missing_error => if (prong_is_comptime_unreach and !prong_is_inline) {
13227 break :item_ref uncoerced;
13228 },
13229 .from_anyerror => {},
13230 else => unreachable,
13231 }
13232 }
13233 break :item_ref try sema.coerce(block, item_ty, uncoerced, item_src);
13234 };
13235 const maybe_lazy = try sema.resolveConstDefinedValue(block, item_src, item_ref, .{ .simple = .switch_item });
1371813236
13719fn validateSwitchDupe(
13720 sema: *Sema,
13721 block: *Block,
13722 maybe_prev_src: ?LazySrcLoc,
13723 item_src: LazySrcLoc,
13724) CompileError!void {
13725 const prev_item_src = maybe_prev_src orelse return;
13726 return sema.failWithOwnedErrorMsg(block, msg: {
13727 const msg = try sema.errMsg(
13728 item_src,
13729 "duplicate switch value",
13730 .{},
13731 );
13732 errdefer msg.destroy(sema.gpa);
13733 try sema.errNote(
13734 prev_item_src,
13735 msg,
13736 "previous value here",
13737 .{},
13738 );
13739 break :msg msg;
13740 });
13741}
13237 // We have to resolve lazy values here to avoid false negatives when detecting
13238 // duplicate items and comparing items to a comptime-known switch operand.
1374213239
13743fn validateSwitchItemBool(
13744 sema: *Sema,
13745 block: *Block,
13746 true_count: *u8,
13747 false_count: *u8,
13748 item_ref: Zir.Inst.Ref,
13749 item_src: LazySrcLoc,
13750) CompileError!Air.Inst.Ref {
13751 const item = try sema.resolveSwitchItemVal(block, item_ref, .bool, item_src);
13752 if (Value.fromInterned(item.val).toBool()) {
13753 true_count.* += 1;
13754 } else {
13755 false_count.* += 1;
13756 }
13757 if (true_count.* > 1 or false_count.* > 1) {
13758 return sema.fail(block, item_src, "duplicate switch value", .{});
13759 }
13760 return item.ref;
13240 const val = try sema.resolveLazyValue(maybe_lazy);
13241 const ref: Air.Inst.Ref = if (val.toIntern() == maybe_lazy.toIntern())
13242 item_ref
13243 else
13244 .fromValue(val);
13245 return .{ .{ .ref = ref, .val = val }, end };
1376113246}
1376213247
13763const ValueSrcMap = std.AutoHashMapUnmanaged(InternPool.Index, LazySrcLoc);
13764
13765fn validateSwitchItemSparse(
13248fn validateSwitchItemOrRange(
1376613249 sema: *Sema,
1376713250 block: *Block,
13768 seen_values: *ValueSrcMap,
13769 item_ref: Zir.Inst.Ref,
13770 operand_ty: Type,
1377113251 item_src: LazySrcLoc,
13772) CompileError!Air.Inst.Ref {
13773 const item = try sema.resolveSwitchItemVal(block, item_ref, operand_ty, item_src);
13774 const kv = try seen_values.fetchPut(sema.gpa, item.val, item_src) orelse return item.ref;
13775 try sema.validateSwitchDupe(block, kv.value, item_src);
13776 unreachable;
13777}
13778
13779fn validateSwitchNoRange(
13780 sema: *Sema,
13781 block: *Block,
13782 ranges_len: u32,
13783 operand_ty: Type,
13784 src_node_offset: std.zig.Ast.Node.Offset,
13252 /// If `opt_last_val` is not `null`, this refers to the first val of a range.
13253 item_val: Value,
13254 opt_last_val: ?Value,
13255 item_ty: Type,
13256 seen_enum_fields: []?LazySrcLoc,
13257 seen_errors: *std.AutoHashMapUnmanaged(InternPool.NullTerminatedString, LazySrcLoc),
13258 seen_sparse_values: *std.AutoHashMapUnmanaged(InternPool.Index, LazySrcLoc),
13259 range_set: *RangeSet,
13260 true_src: *?LazySrcLoc,
13261 false_src: *?LazySrcLoc,
13262 void_src: *?LazySrcLoc,
1378513263) CompileError!void {
13786 if (ranges_len == 0)
13787 return;
13788
13789 const operand_src = block.src(.{ .node_offset_switch_operand = src_node_offset });
13790 const range_src = block.src(.{ .node_offset_switch_range = src_node_offset });
13791
13792 const msg = msg: {
13793 const msg = try sema.errMsg(
13794 operand_src,
13795 "ranges not allowed when switching on type '{f}'",
13796 .{operand_ty.fmt(sema.pt)},
13797 );
13798 errdefer msg.destroy(sema.gpa);
13799 try sema.errNote(
13800 range_src,
13801 msg,
13802 "range here",
13803 .{},
13804 );
13805 break :msg msg;
13264 const pt = sema.pt;
13265 const zcu = pt.zcu;
13266 const ip = &zcu.intern_pool;
13267 const maybe_prev_src: ?LazySrcLoc = maybe_prev_src: switch (item_ty.zigTypeTag(zcu)) {
13268 .@"union" => unreachable,
13269 .@"enum" => {
13270 const int = ip.indexToKey(item_val.toIntern()).enum_tag.int;
13271 if (ip.loadEnumType(item_ty.toIntern()).tagValueIndex(ip, int)) |field_index| {
13272 const maybe_prev_src = seen_enum_fields[field_index];
13273 seen_enum_fields[field_index] = item_src;
13274 break :maybe_prev_src maybe_prev_src;
13275 } else {
13276 break :maybe_prev_src try range_set.add(sema.arena, .{
13277 .first = .fromInterned(int),
13278 .last = .fromInterned(int),
13279 .src = item_src,
13280 }, .fromInterned(ip.typeOf(int)), zcu);
13281 }
13282 },
13283 .error_set => {
13284 const error_name = ip.indexToKey(item_val.toIntern()).err.name;
13285 break :maybe_prev_src if (seen_errors.fetchPutAssumeCapacity(error_name, item_src)) |prev|
13286 prev.value
13287 else
13288 null;
13289 },
13290 .int, .comptime_int => {
13291 if (opt_last_val) |last_val| {
13292 const first_val = item_val;
13293 if (try first_val.compareAll(.gt, last_val, item_ty, pt)) {
13294 return sema.fail(block, item_src, "range start value is greater than the end value", .{});
13295 }
13296 break :maybe_prev_src range_set.addAssumeCapacity(.{
13297 .first = first_val,
13298 .last = last_val,
13299 .src = item_src,
13300 }, item_ty, zcu);
13301 } else {
13302 break :maybe_prev_src range_set.addAssumeCapacity(.{
13303 .first = item_val,
13304 .last = item_val,
13305 .src = item_src,
13306 }, item_ty, zcu);
13307 }
13308 },
13309 .enum_literal, .@"fn", .pointer, .type => {
13310 break :maybe_prev_src if (seen_sparse_values.fetchPutAssumeCapacity(item_val.toIntern(), item_src)) |prev|
13311 prev.value
13312 else
13313 null;
13314 },
13315 .bool => {
13316 if (item_val.toBool()) {
13317 if (true_src.*) |prev_src| break :maybe_prev_src prev_src;
13318 true_src.* = item_src;
13319 } else {
13320 if (false_src.*) |prev_src| break :maybe_prev_src prev_src;
13321 false_src.* = item_src;
13322 }
13323 break :maybe_prev_src null;
13324 },
13325 .void => {
13326 if (void_src.*) |prev_src| break :maybe_prev_src prev_src;
13327 void_src.* = item_src;
13328 break :maybe_prev_src null;
13329 },
13330 else => unreachable, // should have already checked for invalid types
1380613331 };
13807 return sema.failWithOwnedErrorMsg(block, msg);
13332 if (maybe_prev_src) |prev_src| {
13333 return sema.failWithOwnedErrorMsg(block, msg: {
13334 const msg = try sema.errMsg(
13335 item_src,
13336 "duplicate switch value",
13337 .{},
13338 );
13339 errdefer msg.destroy(sema.gpa);
13340 try sema.errNote(
13341 prev_src,
13342 msg,
13343 "previous value here",
13344 .{},
13345 );
13346 break :msg msg;
13347 });
13348 }
1380813349}
1380913350
1381013351fn maybeErrorUnwrap(
src/Zcu.zig+40-38
......@@ -878,7 +878,7 @@ pub const Namespace = struct {
878878 ns: Namespace,
879879 zcu: *Zcu,
880880 name: InternPool.NullTerminatedString,
881 writer: anytype,
881 writer: *Writer,
882882 ) @TypeOf(writer).Error!void {
883883 const sep: u8 = if (ns.parent.unwrap()) |parent| sep: {
884884 try zcu.namespacePtr(parent).renderFullyQualifiedDebugName(
......@@ -1125,7 +1125,7 @@ pub const File = struct {
11251125 return file.sub_file_path.len - ext.len;
11261126 }
11271127
1128 pub fn renderFullyQualifiedName(file: File, writer: anytype) !void {
1128 pub fn renderFullyQualifiedName(file: File, writer: *Writer) !void {
11291129 // Convert all the slashes into dots and truncate the extension.
11301130 const ext = std.fs.path.extension(file.sub_file_path);
11311131 const noext = file.sub_file_path[0 .. file.sub_file_path.len - ext.len];
......@@ -1135,7 +1135,7 @@ pub const File = struct {
11351135 };
11361136 }
11371137
1138 pub fn renderFullyQualifiedDebugName(file: File, writer: anytype) !void {
1138 pub fn renderFullyQualifiedDebugName(file: File, writer: *Writer) !void {
11391139 for (file.sub_file_path) |byte| switch (byte) {
11401140 '/', '\\' => try writer.writeByte('/'),
11411141 else => try writer.writeByte(byte),
......@@ -2177,33 +2177,33 @@ pub const SrcLoc = struct {
21772177 var multi_i: u32 = 0;
21782178 var scalar_i: u32 = 0;
21792179 var underscore_node: Ast.Node.OptionalIndex = .none;
2180 const case = case: for (case_nodes) |case_node| {
2180 const case: Ast.full.SwitchCase = case: for (case_nodes) |case_node| {
21812181 const case = tree.fullSwitchCase(case_node).?;
21822182 if (case.ast.values.len == 0) {
2183 if (want_case_idx == LazySrcLoc.Offset.SwitchCaseIndex.special_else) {
2183 if (want_case_idx == Zir.UnwrappedSwitchBlock.Case.Index.@"else") {
21842184 break :case case;
21852185 }
21862186 continue :case;
21872187 }
2188 if (underscore_node == .none) for (case.ast.values) |val_node| {
2189 if (tree.nodeTag(val_node) == .identifier and
2190 mem.eql(u8, tree.tokenSlice(tree.nodeMainToken(val_node)), "_"))
2191 {
2192 underscore_node = val_node.toOptional();
2193 if (want_case_idx == LazySrcLoc.Offset.SwitchCaseIndex.special_under) {
2194 break :case case;
2188 if (underscore_node == .none) {
2189 for (case.ast.values) |value| {
2190 if (tree.nodeTag(value) == .identifier and
2191 mem.eql(u8, tree.tokenSlice(tree.nodeMainToken(value)), "_"))
2192 {
2193 underscore_node = value.toOptional();
2194 if (want_case_idx.is_under) break :case case;
2195 if (case.ast.values.len == 1) continue :case;
21952196 }
2196 continue :case;
21972197 }
2198 };
2198 }
21992199
22002200 const is_multi = case.ast.values.len != 1 or
22012201 tree.nodeTag(case.ast.values[0]) == .switch_range;
22022202
22032203 switch (want_case_idx.kind) {
2204 .scalar => if (!is_multi and want_case_idx.index == scalar_i)
2204 .scalar => if (!is_multi and want_case_idx.value == scalar_i)
22052205 break :case case,
2206 .multi => if (is_multi and want_case_idx.index == multi_i)
2206 .multi => if (is_multi and want_case_idx.value == multi_i)
22072207 break :case case,
22082208 }
22092209
......@@ -2214,12 +2214,13 @@ pub const SrcLoc = struct {
22142214 }
22152215 } else unreachable;
22162216
2217 const want_item = switch (src_loc.lazy) {
2217 const want_item_idx = switch (src_loc.lazy) {
22182218 .switch_case_item,
22192219 .switch_case_item_range_first,
22202220 .switch_case_item_range_last,
22212221 => |x| item_idx: {
2222 assert(want_case_idx != LazySrcLoc.Offset.SwitchCaseIndex.special_else);
2222 assert(want_case_idx != Zir.UnwrappedSwitchBlock.Case.Index.@"else");
2223 assert(want_case_idx != Zir.UnwrappedSwitchBlock.Case.Index.bare_under);
22232224 break :item_idx x.item_idx;
22242225 },
22252226 .switch_capture, .switch_tag_capture => {
......@@ -2242,7 +2243,7 @@ pub const SrcLoc = struct {
22422243 else => unreachable,
22432244 };
22442245
2245 switch (want_item.kind) {
2246 switch (want_item_idx.kind) {
22462247 .single => {
22472248 var item_i: u32 = 0;
22482249 for (case.ast.values) |item_node| {
......@@ -2251,12 +2252,21 @@ pub const SrcLoc = struct {
22512252 {
22522253 continue;
22532254 }
2254 if (item_i != want_item.index) {
2255 if (item_i != want_item_idx.value) {
22552256 item_i += 1;
22562257 continue;
22572258 }
22582259 return tree.nodeToSpan(item_node);
2259 } else unreachable;
2260 } else {
2261 for (case.ast.values) |item_node| {
2262 const item_span = tree.nodeToSpan(item_node);
2263 std.debug.print("{s}\n", .{tree.source[item_span.start..item_span.end]});
2264 }
2265 std.debug.print("want_case_idx={any}\n", .{want_case_idx});
2266 std.debug.print("want_item_idx={any}\n", .{want_item_idx});
2267 unreachable;
2268 }
2269 // } else unreachable;
22602270 },
22612271 .range => {
22622272 var range_i: u32 = 0;
......@@ -2264,7 +2274,7 @@ pub const SrcLoc = struct {
22642274 if (tree.nodeTag(item_node) != .switch_range) {
22652275 continue;
22662276 }
2267 if (range_i != want_item.index) {
2277 if (range_i != want_item_idx.value) {
22682278 range_i += 1;
22692279 continue;
22702280 }
......@@ -2642,29 +2652,21 @@ pub const LazySrcLoc = struct {
26422652 /// The offset of the switch AST node.
26432653 switch_node_offset: Ast.Node.Offset,
26442654 /// The index of the case to point to within this switch.
2645 case_idx: SwitchCaseIndex,
2655 case_idx: Zir.UnwrappedSwitchBlock.Case.Index,
26462656 /// The index of the item to point to within this case.
2647 item_idx: SwitchItemIndex,
2657 item_idx: SwitchItem.Index,
2658
2659 pub const Index = packed struct(u32) {
2660 kind: enum(u1) { single, range },
2661 value: u31,
2662 };
26482663 };
26492664
26502665 pub const SwitchCapture = struct {
26512666 /// The offset of the switch AST node.
26522667 switch_node_offset: Ast.Node.Offset,
26532668 /// The index of the case whose capture to point to.
2654 case_idx: SwitchCaseIndex,
2655 };
2656
2657 pub const SwitchCaseIndex = packed struct(u32) {
2658 kind: enum(u1) { scalar, multi },
2659 index: u31,
2660
2661 pub const special_else: SwitchCaseIndex = @bitCast(@as(u32, std.math.maxInt(u32)));
2662 pub const special_under: SwitchCaseIndex = @bitCast(@as(u32, std.math.maxInt(u32) - 1));
2663 };
2664
2665 pub const SwitchItemIndex = packed struct(u32) {
2666 kind: enum(u1) { single, range },
2667 index: u31,
2669 case_idx: Zir.UnwrappedSwitchBlock.Case.Index,
26682670 };
26692671
26702672 pub const ArrayCat = struct {
test/behavior/switch.zig+156
......@@ -1120,3 +1120,159 @@ test "switch on non-exhaustive enum" {
11201120 try E.doTheTest(.a);
11211121 try comptime E.doTheTest(.a);
11221122}
1123
1124test "decl literals as switch cases" {
1125 const E = enum(u8) {
1126 bar = 3,
1127 _,
1128
1129 const foo: @This() = @enumFromInt(0xa);
1130
1131 fn doTheTest() !void {
1132 var e: @This() = .foo;
1133 _ = &e;
1134 const ok = switch (e) {
1135 .bar => false,
1136 .foo => true,
1137 else => false,
1138 };
1139 try expect(ok);
1140 }
1141 };
1142
1143 try E.doTheTest();
1144 try comptime E.doTheTest();
1145}
1146
1147// TODO audit after #15909 and/or #19855 are decided/implemented
1148test "switch with uninstantiable union fields" {
1149 const U = union(enum) {
1150 ok: void,
1151 a: noreturn,
1152 b: noreturn,
1153 c: error{},
1154
1155 fn doTheTest() !void {
1156 var u: @This() = .ok;
1157 _ = &u;
1158 try expect(switch (u) {
1159 .ok => true,
1160 .a => comptime unreachable,
1161 .b => comptime unreachable,
1162 .c => comptime unreachable,
1163 });
1164 try expect(switch (u) {
1165 .ok => true,
1166 .a, .b, .c => comptime unreachable,
1167 });
1168 try expect(switch (u) {
1169 .ok => true,
1170 else => comptime unreachable,
1171 });
1172 try expect(switch (u) {
1173 .a => comptime unreachable,
1174 .ok, .b, .c => true,
1175 });
1176 }
1177 };
1178
1179 try U.doTheTest();
1180 try comptime U.doTheTest();
1181}
1182
1183test "switch with tag capture" {
1184 const U = union(enum) {
1185 a,
1186 b: i32,
1187 c: u8,
1188 d: i32,
1189 e: noreturn,
1190
1191 fn doTheTest() !void {
1192 try doTheSwitch(.a);
1193 try doTheSwitch(.{ .b = 123 });
1194 try doTheSwitch(.{ .c = 0xFF });
1195 }
1196 fn doTheSwitch(u: @This()) !void {
1197 switch (u) {
1198 .a => |nothing, tag| {
1199 try expect(nothing == {});
1200 try expect(tag == .a);
1201 try expect(@intFromEnum(tag) == @intFromEnum(@This().a));
1202 },
1203 .b, .d => |_, tag| {
1204 try expect(tag == .b or tag == .d);
1205 },
1206 .e => |payload, tag| {
1207 _ = &payload;
1208 _ = &tag;
1209 comptime unreachable;
1210 },
1211 else => |un, tag| {
1212 try expect(tag == .c);
1213 try expect(un == .c);
1214 try expect(un.c == 0xFF);
1215 },
1216 }
1217 switch (u) {
1218 inline .a, .b, .c => |payload, tag| {
1219 if (@TypeOf(payload) == void) try expect(tag == .a);
1220 if (@TypeOf(payload) == i32) try expect(tag == .b);
1221 if (@TypeOf(payload) == u8) try expect(tag == .c);
1222 },
1223 inline else => |payload, tag| {
1224 if (@TypeOf(payload) == i32) try expect(tag == .d);
1225 try expect(tag != .e);
1226 },
1227 }
1228 }
1229 };
1230
1231 try U.doTheTest();
1232 try comptime U.doTheTest();
1233}
1234
1235test "switch with advanced prong items" {
1236 const S = struct {
1237 fn doTheTest() !void {
1238 try doTheSwitch(2000, 20);
1239 try doTheSwitch(2000, 10);
1240 try doTheSwitch(2000, 5);
1241
1242 try doTheOtherSwitch(@enumFromInt(123));
1243 try doTheOtherSwitch(@enumFromInt(456));
1244 }
1245 fn doTheSwitch(x: u32, comptime factor: u32) !void {
1246 const ok = switch (x) {
1247 num(factor) => true,
1248 typedNum(u32, factor) => true,
1249 blk: {
1250 var val = 400;
1251 val *= factor;
1252 break :blk val;
1253 } => true,
1254 else => false,
1255 };
1256 try expect(ok);
1257 }
1258 fn num(factor: u32) u32 {
1259 return 100 * factor;
1260 }
1261 fn typedNum(comptime T: type, factor: T) T {
1262 return 200 * factor;
1263 }
1264
1265 const E = enum(u32) { _ };
1266 fn doTheOtherSwitch(e: E) !void {
1267 const ok = switch (e) {
1268 @enumFromInt(123) => true,
1269 @enumFromInt(456) => true,
1270 else => false,
1271 };
1272 try expect(ok);
1273 }
1274 };
1275
1276 try S.doTheTest();
1277 try comptime S.doTheTest();
1278}
test/behavior/switch_loop.zig+164-2
......@@ -310,8 +310,8 @@ test "switch loop with single catch-all prong" {
310310 label: switch (E.a) {
311311 else => {
312312 x += 1;
313 if (x >= 5) continue :label .b;
314313 if (x == 10) break :label;
314 if (x >= 5) continue :label .b;
315315 continue :label .c;
316316 },
317317 }
......@@ -320,8 +320,8 @@ test "switch loop with single catch-all prong" {
320320 label: switch (E.a) {
321321 .a, .b, .c => {
322322 x += 1;
323 if (x >= 15) continue :label .b;
324323 if (x == 20) break :label;
324 if (x >= 15) continue :label .b;
325325 continue :label .c;
326326 },
327327 }
......@@ -346,3 +346,165 @@ test "switch loop with single catch-all prong" {
346346 try S.doTheTest();
347347 try comptime S.doTheTest();
348348}
349
350test "switch loop on type with opv" {
351 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
352
353 const S = struct {
354 const E = enum { opv };
355 const U = union(E) { opv: u0 };
356
357 fn doTheTest() !void {
358 var x: usize = 0;
359 label: switch (E.opv) {
360 .opv => {
361 x += 1;
362 if (x == 10) break :label;
363 if (x >= 5) continue :label .opv;
364 continue :label .opv;
365 },
366 }
367 try expect(x == 10);
368
369 label: switch (E.opv) {
370 else => {
371 x += 1;
372 if (x == 20) break :label;
373 if (x >= 15) continue :label .opv;
374 continue :label .opv;
375 },
376 }
377 try expect(x == 20);
378
379 label: switch (E.opv) {
380 .opv => if (false) continue :label true,
381 }
382
383 label: switch (U{ .opv = 0 }) {
384 .opv => |val| {
385 x += 1;
386 if (x == 30) break :label;
387 if (x >= 25) continue :label .{ .opv = val };
388 continue :label .{ .opv = 0 };
389 },
390 }
391 try expect(x == 30);
392 }
393 };
394 try S.doTheTest();
395 try comptime S.doTheTest();
396}
397
398test "switch loop with tag capture" {
399 const U = union(enum) {
400 a,
401 b: i32,
402 c: u8,
403 d: i32,
404 e: noreturn,
405
406 fn doTheTest() !void {
407 try doTheSwitch(.a);
408 try doTheSwitch(.{ .b = 123 });
409 try doTheSwitch(.{ .c = 0xFF });
410 }
411 fn doTheSwitch(u: @This()) !void {
412 const ok1 = label: switch (u) {
413 .a => |nothing, tag| {
414 try expect(nothing == {});
415 try expect(tag == .a);
416 try expect(@intFromEnum(tag) == @intFromEnum(@This().a));
417 continue :label .{ .d = 456 };
418 },
419 .b, .d => |_, tag| {
420 try expect(tag == .b or tag == .d);
421 continue :label .{ .c = 0x0F };
422 },
423 .e => |payload, tag| {
424 _ = &payload;
425 _ = &tag;
426 return error.AnalyzedNoreturnProng;
427 },
428 else => |un, tag| {
429 try expect(tag == .c);
430 try expect(un == .c);
431 if (un.c == 0xFF) continue :label .a;
432 if (un.c == 0x00) break :label false;
433 break :label true;
434 },
435 };
436 try expect(ok1);
437
438 const ok2 = label: switch (u) {
439 inline .a, .b, .c => |payload, tag| {
440 if (@TypeOf(payload) == void) {
441 try expect(tag == .a);
442 continue :label .{ .b = 456 };
443 }
444 if (@TypeOf(payload) == i32) {
445 try expect(tag == .b);
446 continue :label .{ .d = payload };
447 }
448 if (@TypeOf(payload) == u8) {
449 try expect(tag == .c);
450 continue :label .{ .d = payload };
451 }
452 },
453 inline else => |payload, tag| {
454 if (@TypeOf(payload) == i32) try expect(tag == .d);
455 try expect(tag != .e);
456 if (payload == 0) break :label false;
457 break :label true;
458 },
459 };
460 try expect(ok2);
461 }
462 };
463
464 try U.doTheTest();
465 try comptime U.doTheTest();
466}
467
468test "switch loop for error handling" {
469 const Error = error{ MyError, MyOtherError };
470 const S = struct {
471 fn doTheTest() !void {
472 try doThePayloadSwitch(123);
473 try doTheErrSwitch(error.MyError);
474 try doTheErrSwitch(error.MyOtherError);
475 }
476 fn doThePayloadSwitch(eu: Error!u32) !void {
477 const x = eu catch |err| label: switch (err) {
478 error.MyError => continue :label error.MyOtherError,
479 error.MyOtherError => break :label 0,
480 };
481 try expect(x == 123);
482
483 const y = if (eu) |payload| label: {
484 break :label payload * 2;
485 } else |err| label: switch (err) {
486 error.MyError => continue :label error.MyOtherError,
487 error.MyOtherError => break :label 0,
488 };
489 try expect(y == 246);
490 }
491 fn doTheErrSwitch(eu: Error!u32) !void {
492 const x = eu catch |err| label: switch (err) {
493 error.MyError => continue :label error.MyOtherError,
494 error.MyOtherError => break :label 123,
495 };
496 try expect(x == 123);
497
498 const y = if (eu) |payload| label: {
499 break :label payload * 2;
500 } else |err| label: switch (err) {
501 error.MyError => continue :label error.MyOtherError,
502 error.MyOtherError => break :label 123,
503 };
504 try expect(y == 123);
505 }
506 };
507
508 try S.doTheTest();
509 try comptime S.doTheTest();
510}
test/behavior/switch_on_captured_error.zig+198-15
......@@ -17,7 +17,7 @@ test "switch on error union catch capture" {
1717 try testElse();
1818 try testCapture();
1919 try testInline();
20 try testEmptyErrSet();
20 try testUnreachableElseProng();
2121 try testAddressOf();
2222 }
2323
......@@ -240,22 +240,90 @@ test "switch on error union catch capture" {
240240 {
241241 var a: error{}!u64 = 0;
242242 _ = &a;
243 const b: u64 = a catch |err| switch (err) {
244 else => |e| return e,
243 const b = a catch |err| switch (err) {
244 undefined => @compileError("unreachable"),
245245 };
246246 try expectEqual(@as(u64, 0), b);
247247 }
248 }
249
250 fn testUnreachableElseProng() !void {
248251 {
249252 var a: error{}!u64 = 0;
250253 _ = &a;
251 const b: u64 = a catch |err| switch (err) {
252 error.UnknownError => return error.Fail,
254 const b = a catch |err| switch (err) {
255 else => unreachable,
256 };
257 try expectEqual(@as(u64, 0), b);
258 }
259 {
260 var a: error{}!u64 = 0;
261 _ = &a;
262 const b = a catch |err| switch (err) {
263 else => return,
264 };
265 try expectEqual(@as(u64, 0), b);
266 }
267 {
268 var a: error{}!u64 = 0;
269 _ = &a;
270 const b = a catch |err| switch (err) {
271 else => |e| return e,
272 };
273 try expectEqual(@as(u64, 0), b);
274 }
275 {
276 var a: error{MyError}!u64 = error.MyError;
277 _ = &a;
278 const b = a catch |err| switch (err) {
279 error.MyError => 0,
280 else => unreachable,
281 };
282 try expectEqual(@as(u64, 0), b);
283 }
284 {
285 var a: error{MyError}!u64 = error.MyError;
286 _ = &a;
287 const b = a catch |err| switch (err) {
288 error.MyError => 0,
289 else => return,
290 };
291 try expectEqual(@as(u64, 0), b);
292 }
293 {
294 var a: error{MyError}!u64 = error.MyError;
295 _ = &a;
296 const b = a catch |err| switch (err) {
297 error.MyError => 0,
253298 else => |e| return e,
254299 };
255300 try expectEqual(@as(u64, 0), b);
256301 }
257302 }
258303
304 fn testErrNotInSet() !void {
305 {
306 var a: error{MyError}!u64 = 0;
307 _ = &a;
308 const b = a catch |err| switch (err) {
309 error.MyError => 1,
310 error.MyOtherError => comptime unreachable,
311 error.YetAnotherError, error.ThereIsAnother => comptime unreachable,
312 };
313 try expectEqual(@as(u64, 0), b);
314 }
315 {
316 var a: error{MyError}!u64 = error.MyError;
317 _ = &a;
318 const b = a catch |err| switch (err) {
319 error.MyError => 0,
320 error.MyOtherError => comptime unreachable,
321 error.YetAnotherError, error.ThereIsAnother => comptime unreachable,
322 };
323 try expectEqual(@as(u64, 0), b);
324 }
325 }
326
259327 fn testAddressOf() !void {
260328 {
261329 const a: anyerror!usize = 0;
......@@ -316,8 +384,8 @@ test "switch on error union if else capture" {
316384 try testCapturePtr();
317385 try testInline();
318386 try testInlinePtr();
319 try testEmptyErrSet();
320 try testEmptyErrSetPtr();
387 try testUnreachableElseProng();
388 try testUnreachableElseProngPtr();
321389 try testAddressOf();
322390 }
323391
......@@ -755,40 +823,155 @@ test "switch on error union if else capture" {
755823 {
756824 var a: error{}!u64 = 0;
757825 _ = &a;
758 const b: u64 = if (a) |x| x else |err| switch (err) {
759 else => |e| return e,
826 const b = if (a) |x| x else |err| switch (err) {
827 undefined => @compileError("unreachable"),
760828 };
761829 try expectEqual(@as(u64, 0), b);
762830 }
831 }
832
833 fn testEmptyErrSetPtr() !void {
763834 {
764835 var a: error{}!u64 = 0;
765836 _ = &a;
766 const b: u64 = if (a) |x| x else |err| switch (err) {
837 const b = if (a) |*x| x.* else |err| switch (err) {
838 error.undefined => @compileError("unreachable"),
839 };
840 try expectEqual(@as(u64, 0), b);
841 }
842 }
843
844 fn testUnreachableElseProng() !void {
845 {
846 var a: error{}!u64 = 0;
847 _ = &a;
848 const b = if (a) |x| x else |err| switch (err) {
849 else => unreachable,
850 };
851 try expectEqual(@as(u64, 0), b);
852 }
853 {
854 var a: error{}!u64 = 0;
855 _ = &a;
856 const b = if (a) |x| x else |err| switch (err) {
857 error.UnknownError => return error.Fail,
858 else => return,
859 };
860 try expectEqual(@as(u64, 0), b);
861 }
862 {
863 var a: error{}!u64 = 0;
864 _ = &a;
865 const b = if (a) |x| x else |err| switch (err) {
767866 error.UnknownError => return error.Fail,
768867 else => |e| return e,
769868 };
770869 try expectEqual(@as(u64, 0), b);
771870 }
871 {
872 var a: error{MyError}!u64 = error.MyError;
873 _ = &a;
874 const b = if (a) |x| x else |err| switch (err) {
875 error.MyError => 0,
876 else => unreachable,
877 };
878 try expectEqual(@as(u64, 0), b);
879 }
880 {
881 var a: error{MyError}!u64 = error.MyError;
882 _ = &a;
883 const b = if (a) |x| x else |err| switch (err) {
884 error.MyError => 0,
885 else => return,
886 };
887 try expectEqual(@as(u64, 0), b);
888 }
889 {
890 var a: error{MyError}!u64 = error.MyError;
891 _ = &a;
892 const b = if (a) |x| x else |err| switch (err) {
893 error.MyError => 0,
894 else => |e| return e,
895 };
896 try expectEqual(@as(u64, 0), b);
897 }
772898 }
773899
774 fn testEmptyErrSetPtr() !void {
900 fn testUnreachableElseProngPtr() !void {
775901 {
776902 var a: error{}!u64 = 0;
777903 _ = &a;
778 const b: u64 = if (a) |*x| x.* else |err| switch (err) {
779 else => |e| return e,
904 const b = if (a) |*x| x.* else |err| switch (err) {
905 else => unreachable,
780906 };
781907 try expectEqual(@as(u64, 0), b);
782908 }
783909 {
784910 var a: error{}!u64 = 0;
785911 _ = &a;
786 const b: u64 = if (a) |*x| x.* else |err| switch (err) {
787 error.UnknownError => return error.Fail,
912 const b = if (a) |*x| x.* else |err| switch (err) {
913 else => return,
914 };
915 try expectEqual(@as(u64, 0), b);
916 }
917 {
918 var a: error{}!u64 = 0;
919 _ = &a;
920 const b = if (a) |*x| x.* else |err| switch (err) {
788921 else => |e| return e,
789922 };
790923 try expectEqual(@as(u64, 0), b);
791924 }
925 {
926 var a: error{MyError}!u64 = error.MyError;
927 _ = &a;
928 const b = if (a) |*x| x.* else |err| switch (err) {
929 error.MyError => 0,
930 else => unreachable,
931 };
932 try expectEqual(@as(u64, 0), b);
933 }
934 {
935 var a: error{MyError}!u64 = error.MyError;
936 _ = &a;
937 const b = if (a) |*x| x.* else |err| switch (err) {
938 error.MyError => 0,
939 else => return,
940 };
941 try expectEqual(@as(u64, 0), b);
942 }
943 {
944 var a: error{MyError}!u64 = error.MyError;
945 _ = &a;
946 const b = if (a) |*x| x.* else |err| switch (err) {
947 error.MyError => 0,
948 else => |e| return e,
949 };
950 try expectEqual(@as(u64, 0), b);
951 }
952 }
953
954 fn testErrNotInSet() !void {
955 {
956 var a: error{MyError}!u64 = 0;
957 _ = &a;
958 const b = if (a) |x| x else |err| switch (err) {
959 error.MyError => 1,
960 error.MyOtherError => comptime unreachable,
961 error.YetAnotherError, error.ThereIsAnother => comptime unreachable,
962 };
963 try expectEqual(@as(u64, 0), b);
964 }
965 {
966 var a: error{MyError}!u64 = error.MyError;
967 _ = &a;
968 const b = if (a) |x| x else |err| switch (err) {
969 error.MyError => 0,
970 error.MyOtherError => comptime unreachable,
971 error.YetAnotherError, error.ThereIsAnother => comptime unreachable,
972 };
973 try expectEqual(@as(u64, 0), b);
974 }
792975 }
793976
794977 fn testAddressOf() !void {
test/cases/compile_errors/duplicate_boolean_switch_value.zig+2
......@@ -18,4 +18,6 @@ comptime {
1818// error
1919//
2020// :5:9: error: duplicate switch value
21// :3:9: note: previous value here
2122// :13:9: error: duplicate switch value
23// :11:9: note: previous value here
test/cases/compile_errors/invalid_switch_item.zig+4-4
......@@ -36,11 +36,11 @@ export fn f3() void {
3636
3737// error
3838//
39// :8:10: error: no field named 'x' in enum 'tmp.E'
39// :8:10: error: enum 'tmp.E' has no member named 'x'
4040// :1:11: note: enum declared here
41// :16:10: error: no field named 'x' in enum 'tmp.E'
41// :16:10: error: enum 'tmp.E' has no member named 'x'
4242// :1:11: note: enum declared here
43// :24:10: error: no field named 'x' in enum 'tmp.E'
43// :24:10: error: enum 'tmp.E' has no member named 'x'
4444// :1:11: note: enum declared here
45// :32:10: error: no field named 'x' in enum 'tmp.E'
45// :32:10: error: enum 'tmp.E' has no member named 'x'
4646// :1:11: note: enum declared here
test/cases/compile_errors/switch_on_error_with_1_field_with_no_prongs.zig+23-7
......@@ -1,18 +1,34 @@
11const Error = error{M};
22
3export fn entry() void {
4 const f: Error!void = void{};
3export fn entry1() void {
4 var f: Error!void = {};
5 _ = &f;
56 if (f) {} else |e| switch (e) {}
67}
78
89export fn entry2() void {
9 const f: Error!void = void{};
10 var f: Error!void = {};
11 _ = &f;
12 f catch |e| switch (e) {};
13}
14
15export fn entry3() void {
16 const f: Error!void = error.M;
17 if (f) {} else |e| switch (e) {}
18}
19
20export fn entry4() void {
21 const f: Error!void = error.M;
1022 f catch |e| switch (e) {};
1123}
1224
1325// error
1426//
15// :5:24: error: switch must handle all possibilities
16// :5:24: note: unhandled error value: 'error.M'
17// :10:17: error: switch must handle all possibilities
18// :10:17: note: unhandled error value: 'error.M'
27// :6:24: error: switch must handle all possibilities
28// :6:24: note: unhandled error value: 'error.M'
29// :12:17: error: switch must handle all possibilities
30// :12:17: note: unhandled error value: 'error.M'
31// :17:24: error: switch must handle all possibilities
32// :17:24: note: unhandled error value: 'error.M'
33// :22:17: error: switch must handle all possibilities
34// :22:17: note: unhandled error value: 'error.M'
test/cases/compile_errors/tag_capture_on_non_inline_prong.zig deleted-12
......@@ -1,12 +0,0 @@
1const E = enum { a, b, c, d };
2pub export fn entry() void {
3 var x: E = .a;
4 switch (x) {
5 .a, .b => |aorb, d| @compileLog(aorb, d),
6 inline .c, .d => |*cord| @compileLog(cord),
7 }
8}
9
10// error
11//
12// :5:26: error: tag capture on non-inline prong