| ... | ... | @@ -509,7 +509,7 @@ pub const Block = struct { |
| 509 | 509 | .parent = parent, |
| 510 | 510 | .sema = parent.sema, |
| 511 | 511 | .namespace = parent.namespace, |
| 512 | | .instructions = .{}, |
| 512 | .instructions = .empty, |
| 513 | 513 | .label = null, |
| 514 | 514 | .inlining = parent.inlining, |
| 515 | 515 | .comptime_reason = parent.comptime_reason, |
| ... | ... | @@ -6496,26 +6496,23 @@ fn zirSwitchContinue(sema: *Sema, start_block: *Block, inst: Zir.Inst.Index) Com |
| 6496 | 6496 | |
| 6497 | 6497 | switch (sema.code.instructions.items(.tag)[@intFromEnum(switch_inst)]) { |
| 6498 | 6498 | .switch_block, .switch_block_ref => {}, |
| 6499 | .switch_block_err_union => unreachable, // wrong code path! |
| 6499 | 6500 | else => unreachable, // assertion failure |
| 6500 | 6501 | } |
| 6501 | 6502 | |
| 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); |
| 6508 | 6505 | try sema.validateRuntimeValue(start_block, operand_src, operand); |
| 6509 | 6506 | |
| 6510 | 6507 | // We want to generate a `switch_dispatch` instruction with the switch condition, |
| 6511 | 6508 | // possibly preceded by a store to the stack alloc containing the raw operand. |
| 6512 | 6509 | // 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. |
| 6516 | 6513 | |
| 6517 | 6514 | var block = start_block; |
| 6518 | | while (true) { |
| 6515 | while (true) : (block = block.parent.?) { |
| 6519 | 6516 | if (block.label) |label| { |
| 6520 | 6517 | if (label.zir_block == switch_inst) { |
| 6521 | 6518 | 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 |
| 6529 | 6526 | return; |
| 6530 | 6527 | } |
| 6531 | 6528 | } |
| 6532 | | block = block.parent.?; |
| 6533 | 6529 | } |
| 6534 | 6530 | } |
| 6535 | 6531 | |
| ... | ... | @@ -8483,8 +8479,20 @@ fn zirDeclLiteral(sema: *Sema, block: *Block, inst: Zir.Inst.Index, do_coerce: b |
| 8483 | 8479 | sema.code.nullTerminatedString(extra.field_name_start), |
| 8484 | 8480 | .no_embedded_nulls, |
| 8485 | 8481 | ); |
| 8486 | | |
| 8487 | 8482 | 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 | |
| 8486 | fn 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; |
| 8488 | 8496 | |
| 8489 | 8497 | const uncoerced_result = res: { |
| 8490 | 8498 | if (orig_ty.toIntern() == .generic_poison_type) { |
| ... | ... | @@ -10518,1284 +10526,1572 @@ fn zirSliceSentinelTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE |
| 10518 | 10526 | return Air.internedToRef(sentinel_ty.toIntern()); |
| 10519 | 10527 | } |
| 10520 | 10528 | |
| 10521 | | /// Holds common data used when analyzing or resolving switch prong bodies, |
| 10522 | | /// including setting up captures. |
| 10523 | | const 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 | | ); |
| 10529 | fn zirSwitchBlockErrUnion(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 10530 | const tracy = trace(@src()); |
| 10531 | defer tracy.end(); |
| 10590 | 10532 | |
| 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; |
| 10595 | 10536 | |
| 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 }); |
| 10601 | 10541 | |
| 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! |
| 10606 | 10543 | |
| 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); |
| 10616 | 10563 | |
| 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 | } |
| 10621 | 10597 | |
| 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); |
| 10624 | 10602 | |
| 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; |
| 10629 | 10605 | |
| 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 | }; |
| 10652 | 10610 | |
| 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 | } |
| 10658 | 10616 | |
| 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)); |
| 10663 | 10624 | |
| 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 | }; |
| 10673 | 10632 | |
| 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); |
| 10678 | 10638 | |
| 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 | }; |
| 10681 | 10641 | |
| 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); |
| 10686 | 10643 | |
| 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); |
| 10704 | 10656 | }; |
| 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; |
| 10716 | 10657 | } |
| 10717 | 10658 | |
| 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 | } |
| 10731 | 10669 | |
| 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)); |
| 10734 | 10685 | |
| 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 | } } }); |
| 10736 | 10690 | |
| 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 | } |
| 10749 | 10693 | |
| 10750 | | const operand_ty = sema.typeOf(operand_val); |
| 10751 | | const operand_ptr_ty = if (capture_byref) sema.typeOf(operand_ptr) else undefined; |
| 10694 | fn 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); |
| 10752 | 10703 | |
| 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); |
| 10785 | 10723 | |
| 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 | } |
| 10790 | 10732 | |
| 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`. |
| 10736 | fn 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; |
| 10801 | 10750 | |
| 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 }); |
| 10806 | 10754 | |
| 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; |
| 10809 | 10757 | |
| 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; |
| 10815 | 10760 | |
| 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 }; |
| 10822 | 10766 | |
| 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 | }; |
| 10830 | 10780 | |
| 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 | }; |
| 10842 | 10805 | |
| 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); |
| 10852 | 10807 | |
| 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; |
| 10885 | 10810 | |
| 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 | } |
| 10896 | 10817 | |
| 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 | }; |
| 10902 | 10822 | |
| 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; |
| 10906 | 10827 | |
| 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; |
| 10914 | 10831 | |
| 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; |
| 10916 | 10836 | |
| 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 }; |
| 10920 | 10869 | |
| 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; |
| 10935 | 10874 | |
| 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 | } |
| 10938 | 10886 | |
| 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 | } |
| 10942 | 10891 | |
| 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; |
| 10951 | 10925 | } |
| 10952 | 10926 | } |
| 10927 | continue; |
| 10953 | 10928 | } |
| 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 | }; |
| 10954 | 10941 | |
| 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; |
| 10977 | 10944 | |
| 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)); |
| 10993 | 11002 | |
| 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)); |
| 11016 | 11009 | |
| 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)); |
| 11023 | 11012 | |
| 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 | } |
| 11027 | 11015 | |
| 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)); |
| 11077 | 11022 | |
| 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 | } |
| 11089 | 11047 | |
| 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! |
| 11095 | 11049 | |
| 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 | } |
| 11117 | 11063 | |
| 11118 | | fn switchCond( |
| 11064 | fn finishSwitchBr( |
| 11119 | 11065 | sema: *Sema, |
| 11120 | 11066 | 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 { |
| 11124 | 11076 | const pt = sema.pt; |
| 11125 | 11077 | 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; |
| 11149 | 11080 | |
| 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 }); |
| 11165 | 11084 | |
| 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; |
| 11180 | 11087 | |
| 11181 | | const SwitchErrorSet = std.AutoHashMap(InternPool.NullTerminatedString, LazySrcLoc); |
| 11088 | const else_case = validated_switch.else_case; |
| 11089 | const under_case = validated_switch.under_case; |
| 11182 | 11090 | |
| 11183 | | fn 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(); |
| 11186 | 11093 | |
| 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; |
| 11198 | 11098 | |
| 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 | }; |
| 11200 | 11103 | |
| 11201 | 11104 | // 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); |
| 11223 | 11107 | |
| 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; |
| 11226 | 11111 | |
| 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, |
| 11231 | 11115 | }; |
| 11116 | const union_originally = operand_ty.zigTypeTag(zcu) == .@"union"; |
| 11117 | const err_set = operand_ty.zigTypeTag(zcu) == .error_set; |
| 11232 | 11118 | |
| 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); |
| 11242 | 11121 | |
| 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); |
| 11267 | 11127 | |
| 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); |
| 11270 | 11135 | |
| 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; |
| 11276 | 11140 | |
| 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 | } |
| 11282 | 11154 | |
| 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; |
| 11284 | 11160 | |
| 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; |
| 11299 | 11164 | |
| 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 | } |
| 11321 | 11171 | |
| 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; |
| 11326 | 11176 | |
| 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; |
| 11339 | 11179 | |
| 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; |
| 11354 | 11184 | |
| 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; |
| 11360 | 11194 | |
| 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); |
| 11374 | 11218 | |
| 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)); |
| 11377 | 11229 | } |
| 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 | | ); |
| 11403 | 11230 | } |
| 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; |
| 11405 | 11234 | |
| 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 |
| 11411 | 11236 | |
| 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; |
| 11416 | 11240 | |
| 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 | } |
| 11425 | 11248 | |
| 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; |
| 11432 | 11275 | |
| 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); |
| 11436 | 11277 | |
| 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; |
| 11481 | 11287 | |
| 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); |
| 11484 | 11307 | |
| 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 | } |
| 11506 | 11321 | |
| 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; |
| 11509 | 11326 | |
| 11510 | | fn 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 | } |
| 11513 | 11365 | |
| 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; |
| 11525 | 11406 | |
| 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); |
| 11532 | 11408 | |
| 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); |
| 11534 | 11410 | |
| 11535 | | const operand_ty = sema.typeOf(val); |
| 11411 | if (emit_bb) try sema.emitBackwardBranch(block, else_prong_src); |
| 11412 | emit_bb = true; |
| 11536 | 11413 | |
| 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)); |
| 11546 | 11448 | } |
| 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 | | }; |
| 11564 | 11449 | } |
| 11565 | 11450 | |
| 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; |
| 11589 | 11453 | |
| 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 | } |
| 11591 | 11465 | |
| 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. |
| 11598 | 11472 | |
| 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)); |
| 11608 | 11474 | |
| 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; |
| 11669 | 11476 | |
| 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); |
| 11676 | 11500 | |
| 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)); |
| 11682 | 11522 | |
| 11683 | | var empty_enum = false; |
| 11523 | // We fall through to the regular catch-all prong generation. |
| 11684 | 11524 | |
| 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 | } |
| 11686 | 11528 | |
| 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; |
| 11709 | 11552 | }; |
| 11710 | | return sema.failWithOwnedErrorMsg(block, msg); |
| 11711 | | } |
| 11712 | 11553 | |
| 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 = .{ |
| 11730 | 11568 | .switch_node_offset = src_node_offset, |
| 11731 | | .case_idx = .special_under, |
| 11732 | | .item_idx = .{ .kind = .single, .index = @intCast(item_i) }, |
| 11569 | .case_idx = index, |
| 11733 | 11570 | } }), |
| 11571 | capture, |
| 11572 | has_tag_capture, |
| 11573 | .none, |
| 11574 | .special, |
| 11575 | validated_switch.else_err_ty, |
| 11576 | switch_inst, |
| 11577 | zir_switch, |
| 11734 | 11578 | ); |
| 11735 | 11579 | } |
| 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); |
| 11760 | 11587 | } |
| 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 | }; |
| 11788 | 11593 | |
| 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`. |
| 11634 | fn 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 | |
| 11734 | const 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`. |
| 11853 | fn 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 | }); |
| 11791 | 11943 | } |
| 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" => { |
| 11792 | 12088 | const all_tags_handled = for (seen_enum_fields) |seen_src| { |
| 11793 | 12089 | if (seen_src == null) break false; |
| 11794 | 12090 | } else true; |
| 11795 | 12091 | |
| 11796 | 12092 | if (has_else) { |
| 11797 | 12093 | if (all_tags_handled) { |
| 11798 | | if (cond_ty.isNonexhaustiveEnum(zcu)) { |
| 12094 | if (item_ty.isNonexhaustiveEnum(zcu)) { |
| 11799 | 12095 | if (has_under) return sema.fail( |
| 11800 | 12096 | block, |
| 11801 | 12097 | else_prong_src, |
| ... | ... | @@ -11820,9 +12116,9 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r |
| 11820 | 12116 | for (seen_enum_fields, 0..) |seen_src, i| { |
| 11821 | 12117 | if (seen_src != null) continue; |
| 11822 | 12118 | |
| 11823 | | const field_name = cond_ty.enumFieldName(i, zcu); |
| 12119 | const field_name = item_ty.enumFieldName(i, zcu); |
| 11824 | 12120 | try sema.addFieldErrNote( |
| 11825 | | cond_ty, |
| 12121 | item_ty, |
| 11826 | 12122 | i, |
| 11827 | 12123 | msg, |
| 11828 | 12124 | "unhandled enumeration value: '{f}'", |
| ... | ... | @@ -11830,15 +12126,17 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r |
| 11830 | 12126 | ); |
| 11831 | 12127 | } |
| 11832 | 12128 | try sema.errNote( |
| 11833 | | cond_ty.srcLoc(zcu), |
| 12129 | item_ty.srcLoc(zcu), |
| 11834 | 12130 | msg, |
| 11835 | 12131 | "enum '{f}' declared here", |
| 11836 | | .{cond_ty.fmt(pt)}, |
| 12132 | .{item_ty.fmt(pt)}, |
| 11837 | 12133 | ); |
| 11838 | 12134 | break :msg msg; |
| 11839 | 12135 | }; |
| 11840 | 12136 | 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 | { |
| 11842 | 12140 | return sema.fail( |
| 11843 | 12141 | block, |
| 11844 | 12142 | src, |
| ... | ... | @@ -11847,101 +12145,83 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r |
| 11847 | 12145 | ); |
| 11848 | 12146 | } |
| 11849 | 12147 | }, |
| 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( |
| 11900 | 12153 | 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 | ); |
| 11910 | 12158 | } |
| 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( |
| 11921 | 12200 | 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 | .{}, |
| 11931 | 12204 | ); |
| 11932 | | case_vals.appendAssumeCapacity(vals[0]); |
| 11933 | | case_vals.appendAssumeCapacity(vals[1]); |
| 11934 | 12205 | } |
| 11935 | 12206 | |
| 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| { |
| 11940 | 12220 | 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)) { |
| 11945 | 12225 | if (has_else) { |
| 11946 | 12226 | return sema.fail( |
| 11947 | 12227 | block, |
| ... | ... | @@ -11953,7 +12233,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r |
| 11953 | 12233 | break :check_range; |
| 11954 | 12234 | } |
| 11955 | 12235 | } |
| 11956 | | if (special_prongs == .none) { |
| 12236 | if (!has_else) { |
| 11957 | 12237 | return sema.fail( |
| 11958 | 12238 | block, |
| 11959 | 12239 | src, |
| ... | ... | @@ -11963,61 +12243,24 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r |
| 11963 | 12243 | } |
| 11964 | 12244 | } |
| 11965 | 12245 | }, |
| 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 | ); |
| 12018 | 12254 | } |
| 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 | }; |
| 12019 | 12262 | if (has_else) { |
| 12020 | | if (true_count + false_count == 2) { |
| 12263 | if (all_values_handled) { |
| 12021 | 12264 | return sema.fail( |
| 12022 | 12265 | block, |
| 12023 | 12266 | else_prong_src, |
| ... | ... | @@ -12026,7 +12269,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r |
| 12026 | 12269 | ); |
| 12027 | 12270 | } |
| 12028 | 12271 | } else { |
| 12029 | | if (true_count + false_count < 2) { |
| 12272 | if (!all_values_handled) { |
| 12030 | 12273 | return sema.fail( |
| 12031 | 12274 | block, |
| 12032 | 12275 | src, |
| ... | ... | @@ -12036,1775 +12279,1073 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r |
| 12036 | 12279 | } |
| 12037 | 12280 | } |
| 12038 | 12281 | }, |
| 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, |
| 12123 | 12283 | } |
| 12124 | 12284 | |
| 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, |
| 12134 | 12293 | |
| 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, |
| 12142 | 12298 | }; |
| 12299 | } |
| 12143 | 12300 | |
| 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 | | }; |
| 12301 | fn 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; |
| 12158 | 12317 | |
| 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, |
| 12176 | 12324 | }; |
| 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; |
| 12180 | 12327 | |
| 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; |
| 12190 | 12348 | } |
| 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; |
| 12201 | 12355 | } |
| 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; |
| 12204 | 12359 | } |
| 12205 | | } |
| 12206 | 12360 | |
| 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, |
| 12231 | 12391 | ); |
| 12232 | 12392 | } |
| 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))) |
| 12238 | 12399 | { |
| 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, |
| 12244 | 12406 | 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, |
| 12247 | 12409 | } }), |
| 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, |
| 12251 | 12415 | merges, |
| 12416 | switch_inst, |
| 12417 | zir_switch, |
| 12252 | 12418 | ); |
| 12253 | 12419 | } |
| 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 }); |
| 12340 | 12420 | } |
| 12341 | 12421 | } |
| 12342 | 12422 | |
| 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; |
| 12386 | 12425 | |
| 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 |
| 12409 | 12427 | |
| 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, |
| 12424 | 12448 | ); |
| 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)); |
| 12435 | 12449 | } |
| 12436 | 12450 | |
| 12437 | | return sema.resolveAnalyzedBlock(block, src, &child_block, merges, false); |
| 12438 | | } |
| 12439 | | |
| 12440 | | const 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 | | |
| 12448 | | fn 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 |
| 12513 | 12452 | |
| 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; |
| 12582 | 12463 | } 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; |
| 12790 | 12466 | } |
| 12791 | | |
| 12792 | | cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items)); |
| 12793 | 12467 | } |
| 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 | } |
| 12794 | 12488 | |
| 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; |
| 12489 | const 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 | }; |
| 12953 | 12516 | |
| 12954 | | if (emit_bb) try sema.emitBackwardBranch(block, else_prong_src); |
| 12955 | | emit_bb = true; |
| 12517 | const CatchAllSwitchCase = enum { none, @"else", under }; |
| 12956 | 12518 | |
| 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); |
| 12519 | const SwitchProngKind = union(enum) { |
| 12520 | item_refs: []const Air.Inst.Ref, |
| 12521 | has_ranges, |
| 12522 | special, |
| 12523 | }; |
| 12971 | 12524 | |
| 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`. |
| 12527 | fn 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 }); |
| 12985 | 12548 | |
| 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; |
| 12988 | 12553 | |
| 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)); |
| 12991 | 12578 | |
| 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)); |
| 13006 | 12592 | |
| 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)); |
| 13023 | 12595 | |
| 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 | } |
| 13026 | 12598 | |
| 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 | | } |
| 12599 | fn 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 | } |
| 13036 | 12623 | |
| 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`. |
| 12626 | fn 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; |
| 13041 | 12645 | |
| 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, |
| 13081 | 12652 | }; |
| 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 | } |
| 13082 | 12658 | |
| 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 | } |
| 13088 | 12684 | }; |
| 13089 | 12685 | |
| 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)); |
| 13100 | 12710 | |
| 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)); |
| 13118 | 12724 | |
| 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)); |
| 13120 | 12727 | |
| 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); |
| 13128 | 12729 | } |
| 13129 | 12730 | |
| 13130 | | fn resolveSwitchComptimeLoop( |
| 12731 | fn analyzeSwitchTagCapture( |
| 13131 | 12732 | 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, |
| 13147 | 12739 | ) 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; |
| 13187 | 12742 | |
| 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 | }; |
| 13189 | 12747 | |
| 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! |
| 13199 | 12755 | } |
| 12756 | const tag_ty = operand_ty.unionTagType(zcu).?; |
| 12757 | return sema.unionToTag(case_block, tag_ty, operand_val, tag_capture_src); |
| 13200 | 12758 | } |
| 13201 | 12759 | |
| 13202 | | fn resolveSwitchComptime( |
| 12760 | fn analyzeSwitchPayloadCapture( |
| 13203 | 12761 | 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, |
| 13208 | 12768 | 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, |
| 13218 | 12778 | ) 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; |
| 13222 | 12782 | |
| 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); |
| 13251 | 12808 | } |
| 12809 | } else if (capture_by_ref) { |
| 12810 | return sema.uavRef(item_val.toIntern()); |
| 12811 | } else { |
| 12812 | return inline_case_capture; |
| 13252 | 12813 | } |
| 13253 | 12814 | } |
| 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 | | } |
| 13290 | 12815 | |
| 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; |
| 13296 | 12817 | |
| 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; |
| 13318 | 12827 | } |
| 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 | }; |
| 13346 | 12831 | } |
| 13347 | 12832 | |
| 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; |
| 13366 | 12837 | |
| 13367 | | const 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]); |
| 13374 | 12840 | |
| 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 | } |
| 13376 | 12846 | |
| 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; |
| 13393 | 12853 | |
| 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 | } |
| 13397 | 12862 | |
| 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 | }; |
| 13407 | 12889 | |
| 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 | } |
| 13410 | 12922 | |
| 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 | }; |
| 13416 | 12938 | |
| 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 | } |
| 13420 | 12944 | |
| 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 | } |
| 13434 | 12948 | |
| 13435 | | const ResolvedSwitchItem = struct { |
| 13436 | | ref: Air.Inst.Ref, |
| 13437 | | val: InternPool.Index, |
| 13438 | | }; |
| 13439 | | fn 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 | } |
| 13448 | 12956 | |
| 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); |
| 13452 | 12958 | |
| 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 | } |
| 13454 | 12962 | |
| 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 | }; |
| 13456 | 12977 | |
| 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. |
| 13461 | 12980 | |
| 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. |
| 13464 | 12984 | |
| 13465 | | fn 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 | } |
| 13481 | 12996 | |
| 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 | }); |
| 13484 | 13006 | |
| 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(); |
| 13493 | 13008 | |
| 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(); |
| 13518 | 13012 | |
| 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); |
| 13532 | 13018 | } |
| 13533 | 13019 | |
| 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); |
| 13554 | 13026 | |
| 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 | } }, |
| 13564 | 13034 | }; |
| 13565 | 13035 | |
| 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 |
| 13572 | 13052 | } |
| 13573 | 13053 | } |
| 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); |
| 13574 | 13058 | |
| 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 | }; |
| 13580 | 13069 | |
| 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 | }), |
| 13610 | 13085 | }, |
| 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 | } |
| 13613 | 13117 | |
| 13118 | return capture_block_inst.toRef(); |
| 13119 | }, |
| 13120 | .error_set => { |
| 13121 | if (capture_by_ref) { |
| 13614 | 13122 | 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", |
| 13618 | 13126 | .{}, |
| 13619 | 13127 | ); |
| 13620 | 13128 | } |
| 13621 | 13129 | |
| 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 | } |
| 13626 | 13135 | |
| 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; |
| 13628 | 13152 | } |
| 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()); |
| 13632 | 13153 | }, |
| 13633 | 13154 | } |
| 13634 | | return null; |
| 13635 | 13155 | } |
| 13636 | 13156 | |
| 13637 | | fn 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 | | } |
| 13157 | const ResolvedSwitchItem = struct { |
| 13158 | ref: Air.Inst.Ref, |
| 13159 | val: Value, |
| 13160 | }; |
| 13161 | const ResolvedSwitchItemAndExtraIndex = struct { ResolvedSwitchItem, usize }; |
| 13663 | 13162 | |
| 13664 | | fn validateSwitchItemInt( |
| 13163 | fn resolveSwitchItem( |
| 13665 | 13164 | sema: *Sema, |
| 13666 | 13165 | block: *Block, |
| 13667 | | range_set: *RangeSet, |
| 13668 | | item_ref: Zir.Inst.Ref, |
| 13669 | | operand_ty: Type, |
| 13670 | 13166 | 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; |
| 13677 | 13178 | |
| 13678 | | fn 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; |
| 13700 | 13206 | |
| 13701 | | fn 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 }); |
| 13718 | 13236 | |
| 13719 | | fn 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. |
| 13742 | 13239 | |
| 13743 | | fn 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 }; |
| 13761 | 13246 | } |
| 13762 | 13247 | |
| 13763 | | const ValueSrcMap = std.AutoHashMapUnmanaged(InternPool.Index, LazySrcLoc); |
| 13764 | | |
| 13765 | | fn validateSwitchItemSparse( |
| 13248 | fn validateSwitchItemOrRange( |
| 13766 | 13249 | sema: *Sema, |
| 13767 | 13250 | block: *Block, |
| 13768 | | seen_values: *ValueSrcMap, |
| 13769 | | item_ref: Zir.Inst.Ref, |
| 13770 | | operand_ty: Type, |
| 13771 | 13251 | 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 | | |
| 13779 | | fn 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, |
| 13785 | 13263 | ) 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 |
| 13806 | 13331 | }; |
| 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 | } |
| 13808 | 13349 | } |
| 13809 | 13350 | |
| 13810 | 13351 | fn maybeErrorUnwrap( |