| ... | @@ -509,7 +509,7 @@ pub const Block = struct { | ... | @@ -509,7 +509,7 @@ pub const Block = struct { |
| 509 | .parent = parent, | 509 | .parent = parent, |
| 510 | .sema = parent.sema, | 510 | .sema = parent.sema, |
| 511 | .namespace = parent.namespace, | 511 | .namespace = parent.namespace, |
| 512 | .instructions = .{}, | 512 | .instructions = .empty, |
| 513 | .label = null, | 513 | .label = null, |
| 514 | .inlining = parent.inlining, | 514 | .inlining = parent.inlining, |
| 515 | .comptime_reason = parent.comptime_reason, | 515 | .comptime_reason = parent.comptime_reason, |
| ... | @@ -6496,26 +6496,23 @@ fn zirSwitchContinue(sema: *Sema, start_block: *Block, inst: Zir.Inst.Index) Com | ... | @@ -6496,26 +6496,23 @@ fn zirSwitchContinue(sema: *Sema, start_block: *Block, inst: Zir.Inst.Index) Com |
| 6496 | | 6496 | |
| 6497 | switch (sema.code.instructions.items(.tag)[@intFromEnum(switch_inst)]) { | 6497 | switch (sema.code.instructions.items(.tag)[@intFromEnum(switch_inst)]) { |
| 6498 | .switch_block, .switch_block_ref => {}, | 6498 | .switch_block, .switch_block_ref => {}, |
| | 6499 | .switch_block_err_union => unreachable, // wrong code path! |
| 6499 | else => unreachable, // assertion failure | 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 operand_ty = (try sema.resolveInst(switch_inst.toRef())).toType(); |
| 6503 | const switch_operand_ref = sema.code.extraData(Zir.Inst.SwitchBlock, switch_payload_index).data.operand; | 6504 | const operand = try sema.coerce(start_block, operand_ty, uncoerced_operand, operand_src); |
| 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 | | | |
| 6508 | try sema.validateRuntimeValue(start_block, operand_src, operand); | 6505 | try sema.validateRuntimeValue(start_block, operand_src, operand); |
| 6509 | | 6506 | |
| 6510 | // We want to generate a `switch_dispatch` instruction with the switch condition, | 6507 | // We want to generate a `switch_dispatch` instruction with the switch condition, |
| 6511 | // possibly preceded by a store to the stack alloc containing the raw operand. | 6508 | // possibly preceded by a store to the stack alloc containing the raw operand. |
| 6512 | // However, to avoid too much special-case state in Sema, this is handled by the | 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 | 6510 | // `switch` lowering logic. As such, we will find the `Block` corresponding to |
| 6514 | // parent `switch_block[_ref]` instruction, create a dummy `br`, and add a merge | 6511 | // the parent `switch_block[_ref]` instruction, create a dummy `br`, and add a |
| 6515 | // to signal to the switch logic to rewrite this into an appropriate dispatch. | 6512 | // merge to signal to the switch logic to rewrite this into an appropriate dispatch. |
| 6516 | | 6513 | |
| 6517 | var block = start_block; | 6514 | var block = start_block; |
| 6518 | while (true) { | 6515 | while (true) : (block = block.parent.?) { |
| 6519 | if (block.label) |label| { | 6516 | if (block.label) |label| { |
| 6520 | if (label.zir_block == switch_inst) { | 6517 | if (label.zir_block == switch_inst) { |
| 6521 | const br_ref = try start_block.addBr(label.merges.block_inst, operand); | 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,7 +6526,6 @@ fn zirSwitchContinue(sema: *Sema, start_block: *Block, inst: Zir.Inst.Index) Com |
| 6529 | return; | 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,8 +8479,20 @@ fn zirDeclLiteral(sema: *Sema, block: *Block, inst: Zir.Inst.Index, do_coerce: b |
| 8483 | sema.code.nullTerminatedString(extra.field_name_start), | 8479 | sema.code.nullTerminatedString(extra.field_name_start), |
| 8484 | .no_embedded_nulls, | 8480 | .no_embedded_nulls, |
| 8485 | ); | 8481 | ); |
| 8486 | | | |
| 8487 | const orig_ty: Type = try sema.resolveTypeOrPoison(block, src, extra.lhs) orelse .generic_poison; | 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 | const uncoerced_result = res: { | 8497 | const uncoerced_result = res: { |
| 8490 | if (orig_ty.toIntern() == .generic_poison_type) { | 8498 | if (orig_ty.toIntern() == .generic_poison_type) { |
| ... | @@ -10518,1284 +10526,1572 @@ fn zirSliceSentinelTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE | ... | @@ -10518,1284 +10526,1572 @@ fn zirSliceSentinelTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE |
| 10518 | return Air.internedToRef(sentinel_ty.toIntern()); | 10526 | return Air.internedToRef(sentinel_ty.toIntern()); |
| 10519 | } | 10527 | } |
| 10520 | | 10528 | |
| 10521 | /// Holds common data used when analyzing or resolving switch prong bodies, | 10529 | fn zirSwitchBlockErrUnion(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 10522 | /// including setting up captures. | 10530 | const tracy = trace(@src()); |
| 10523 | const SwitchProngAnalysis = struct { | 10531 | defer tracy.end(); |
| 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 | ); | | |
| 10590 | | 10532 | |
| 10591 | // We can propagate `.cold` hints from this branch since it's comptime-known | 10533 | const pt = sema.pt; |
| 10592 | // to be taken from the parent branch. | 10534 | const zcu = pt.zcu; |
| 10593 | const parent_hint = sema.branch_hint; | 10535 | const gpa = sema.gpa; |
| 10594 | defer sema.branch_hint = parent_hint orelse if (sema.branch_hint == .cold) .cold else null; | | |
| 10595 | | 10536 | |
| 10596 | if (has_tag_capture) { | 10537 | const zir_switch = sema.code.getSwitchBlock(inst); |
| 10597 | const tag_ref = try spa.analyzeTagCapture(child_block, capture_src, inline_case_capture); | 10538 | const src_node_offset = zir_switch.catch_or_if_src_node_offset.unwrap().?; |
| 10598 | sema.inst_map.putAssumeCapacity(spa.tag_capture_inst, tag_ref); | 10539 | const src = block.src(.{ .node_offset_main_token = src_node_offset }); |
| 10599 | } | 10540 | const operand_src = block.src(.{ .node_offset_if_cond = src_node_offset }); |
| 10600 | defer if (has_tag_capture) assert(sema.inst_map.remove(spa.tag_capture_inst)); | | |
| 10601 | | 10541 | |
| 10602 | switch (capture) { | 10542 | assert(!zir_switch.has_continue); // wrong codepath! |
| 10603 | .none => { | | |
| 10604 | return sema.resolveBlockBody(spa.parent_block, src, child_block, prong_body, spa.switch_block_inst, merges); | | |
| 10605 | }, | | |
| 10606 | | 10543 | |
| 10607 | .by_val, .by_ref => { | 10544 | const block_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len); |
| 10608 | const capture_ref = try spa.analyzeCapture( | 10545 | try sema.air_instructions.append(gpa, .{ |
| 10609 | child_block, | 10546 | .tag = .block, |
| 10610 | capture == .by_ref, | 10547 | .data = undefined, |
| 10611 | prong_type == .special, | 10548 | }); |
| 10612 | capture_src, | 10549 | var label: Block.Label = .{ |
| 10613 | case_vals, | 10550 | .zir_block = inst, |
| 10614 | inline_case_capture, | 10551 | .merges = .{ |
| 10615 | ); | 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)) { | 10564 | const non_err_case = zir_switch.non_err_case.?; |
| 10618 | // This prong should be unreachable! | 10565 | |
| 10619 | return .unreachable_value; | 10566 | var non_err_block: Block = child_block.makeSubBlock(); |
| 10620 | } | 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); | 10598 | const non_err_cond = if (non_err_case.operand_is_ref) |
| 10623 | defer assert(sema.inst_map.remove(spa.switch_block_inst)); | 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); | 10603 | const is_non_err = try sema.resolveDefinedValue(block, operand_src, non_err_cond); |
| 10626 | }, | 10604 | const non_err_reachable = if (is_non_err) |val| val.toBool() else true; |
| 10627 | } | | |
| 10628 | } | | |
| 10629 | | 10605 | |
| 10630 | /// Analyze a switch prong which may have peers at runtime. | 10606 | const err_set_empty = err_set_empty: { |
| 10631 | /// Uses `analyzeBodyRuntimeBreak`. Sets up captures as needed. | 10607 | const err_set_ty = err_union_ty.errorUnionSet(zcu); |
| 10632 | /// Returns the `BranchHint` for the prong. | 10608 | break :err_set_empty err_set_ty.errorSetIsEmpty(zcu); |
| 10633 | fn analyzeProngRuntime( | 10609 | }; |
| 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; | | |
| 10652 | | 10610 | |
| 10653 | if (has_tag_capture) { | 10611 | const non_err_hint: std.builtin.BranchHint = hint: { |
| 10654 | const tag_ref = try spa.analyzeTagCapture(case_block, capture_src, inline_case_capture); | 10612 | // don't analyze the non-error body if it's unreachable |
| 10655 | sema.inst_map.putAssumeCapacity(spa.tag_capture_inst, tag_ref); | 10613 | if (!non_err_reachable) { |
| 10656 | } | 10614 | break :hint undefined; |
| 10657 | defer if (has_tag_capture) assert(sema.inst_map.remove(spa.tag_capture_inst)); | 10615 | } |
| 10658 | | 10616 | |
| 10659 | switch (capture) { | 10617 | const eu_payload: Air.Inst.Ref = switch (non_err_case.capture) { |
| 10660 | .none => { | 10618 | .by_val => try sema.analyzeErrUnionPayload(&non_err_block, src, err_union_ty, eu_maybe_ptr, operand_src, false), |
| 10661 | return sema.analyzeBodyRuntimeBreak(case_block, prong_body); | 10619 | .by_ref => try sema.analyzeErrUnionPayloadPtr(&non_err_block, src, eu_maybe_ptr, false, false), |
| 10662 | }, | 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 => { | 10625 | const always_non_err = if (is_non_err) |val| val.toBool() else err_set_empty; |
| 10665 | const capture_ref = try spa.analyzeCapture( | 10626 | if (always_non_err) { |
| 10666 | case_block, | 10627 | // Early return; we don't analyze the switch as it's unreachable. |
| 10667 | capture == .by_ref, | 10628 | return sema.resolveBlockBody(block, src, &non_err_block, non_err_case.body, inst, merges); |
| 10668 | prong_type == .special, | 10629 | } |
| 10669 | capture_src, | 10630 | break :hint try sema.analyzeBodyRuntimeBreak(&non_err_block, non_err_case.body); |
| 10670 | case_vals, | 10631 | }; |
| 10671 | inline_case_capture, | | |
| 10672 | ); | | |
| 10673 | | 10632 | |
| 10674 | if (sema.typeOf(capture_ref).isNoReturn(sema.pt.zcu)) { | 10633 | // Emit this into the switch block as it's our error case! |
| 10675 | // No need to analyze any further, the prong is unreachable | 10634 | const eu_code = if (non_err_case.operand_is_ref) |
| 10676 | return .none; | 10635 | try sema.analyzeErrUnionCodePtr(&switch_block, operand_src, eu_maybe_ptr) |
| 10677 | } | 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); | 10639 | break :non_err .{ eu_code, non_err_reachable, non_err_cond, non_err_hint, err_set_empty }; |
| 10680 | defer assert(sema.inst_map.remove(spa.switch_block_inst)); | 10640 | }; |
| 10681 | | 10641 | |
| 10682 | return sema.analyzeBodyRuntimeBreak(case_block, prong_body); | 10642 | const validated_switch = try sema.validateSwitchBlock(block, raw_switch_operand, false, inst, &zir_switch); |
| 10683 | }, | | |
| 10684 | } | | |
| 10685 | } | | |
| 10686 | | 10643 | |
| 10687 | fn analyzeTagCapture( | 10644 | const maybe_switch_ref: ?Air.Inst.Ref = ref: { |
| 10688 | spa: SwitchProngAnalysis, | 10645 | if (err_set_empty) break :ref .unreachable_value; |
| 10689 | block: *Block, | 10646 | // make err capture (i.e. switch operand) available to switch prong bodies |
| 10690 | capture_src: LazySrcLoc, | 10647 | sema.inst_map.putAssumeCapacityNoClobber(inst, raw_switch_operand); |
| 10691 | inline_case_capture: Air.Inst.Ref, | 10648 | defer assert(sema.inst_map.remove(inst)); |
| 10692 | ) CompileError!Air.Inst.Ref { | 10649 | break :ref try sema.analyzeSwitchBlock(block, &switch_block, raw_switch_operand, false, merges, inst, &zir_switch, &validated_switch); |
| 10693 | const sema = spa.sema; | 10650 | }; |
| 10694 | const pt = sema.pt; | 10651 | |
| 10695 | const zcu = pt.zcu; | 10652 | if (!non_err_reachable) { |
| 10696 | const operand_ty = switch (spa.operand) { | 10653 | return maybe_switch_ref orelse { |
| 10697 | .simple => |s| sema.typeOf(s.by_val), | 10654 | const switch_src = block.nodeOffset(zir_switch.switch_src_node_offset); |
| 10698 | .loop => |l| ty: { | 10655 | return sema.resolveAnalyzedBlock(block, switch_src, &switch_block, merges, false); |
| 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 | }, | | |
| 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( | 10659 | if (maybe_switch_ref) |switch_ref| { |
| 10719 | spa: SwitchProngAnalysis, | 10660 | if (sema.typeOf(switch_ref).isNoReturn(zcu)) { |
| 10720 | block: *Block, | 10661 | _ = try switch_block.addNoOp(.unreach); |
| 10721 | capture_byref: bool, | 10662 | } else { |
| 10722 | is_special_prong: bool, | 10663 | const br_ref = try switch_block.addBr(merges.block_inst, switch_ref); |
| 10723 | capture_src: LazySrcLoc, | 10664 | try merges.results.append(gpa, switch_ref); |
| 10724 | case_vals: []const Air.Inst.Ref, | 10665 | try merges.br_list.append(gpa, br_ref.toIndex().?); |
| 10725 | inline_case_capture: Air.Inst.Ref, | 10666 | try merges.src_locs.append(gpa, null); |
| 10726 | ) CompileError!Air.Inst.Ref { | 10667 | } |
| 10727 | const sema = spa.sema; | 10668 | } |
| 10728 | const pt = sema.pt; | | |
| 10729 | const zcu = pt.zcu; | | |
| 10730 | const ip = &zcu.intern_pool; | | |
| 10731 | | 10669 | |
| 10732 | const zir_datas = sema.code.instructions.items(.data); | 10670 | try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).@"struct".fields.len + |
| 10733 | const switch_node_offset = zir_datas[@intFromEnum(spa.switch_block_inst)].pl_node.src_node; | 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) { | 10691 | return sema.resolveAnalyzedBlock(block, src, &child_block, merges, false); |
| 10738 | .simple => |s| .{ s.by_val, s.by_ref }, | 10692 | } |
| 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 | }; | | |
| 10749 | | 10693 | |
| 10750 | const operand_ty = sema.typeOf(operand_val); | 10694 | fn zirSwitchBlock( |
| 10751 | const operand_ptr_ty = if (capture_byref) sema.typeOf(operand_ptr) else undefined; | 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) { | 10704 | const block_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len); |
| 10754 | const item_val = sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, inline_case_capture, undefined) catch unreachable; | 10705 | try sema.air_instructions.append(sema.gpa, .{ |
| 10755 | if (operand_ty.zigTypeTag(zcu) == .@"union") { | 10706 | .tag = .block, |
| 10756 | const field_index: u32 = @intCast(operand_ty.unionTagFieldIndex(item_val, zcu).?); | 10707 | .data = undefined, |
| 10757 | const union_obj = zcu.typeToUnion(operand_ty).?; | 10708 | }); |
| 10758 | const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]); | 10709 | var label: Block.Label = .{ |
| 10759 | if (capture_byref) { | 10710 | .zir_block = inst, |
| 10760 | const ptr_field_ty = try pt.ptrTypeSema(.{ | 10711 | .merges = .{ |
| 10761 | .child = field_ty.toIntern(), | 10712 | .src_locs = .{}, |
| 10762 | .flags = .{ | 10713 | .results = .{}, |
| 10763 | .is_const = !operand_ptr_ty.ptrIsMutable(zcu), | 10714 | .br_list = .{}, |
| 10764 | .is_volatile = operand_ptr_ty.isVolatilePtr(zcu), | 10715 | .block_inst = block_inst, |
| 10765 | .address_space = operand_ptr_ty.ptrAddressSpace(zcu), | 10716 | }, |
| 10766 | }, | 10717 | }; |
| 10767 | }); | 10718 | var child_block = block.makeSubBlock(); |
| 10768 | if (try sema.resolveDefinedValue(block, operand_src, operand_ptr)) |union_ptr| { | 10719 | child_block.label = &label; |
| 10769 | return Air.internedToRef((try union_ptr.ptrField(field_index, pt)).toIntern()); | 10720 | const merges = &child_block.label.?.merges; |
| 10770 | } | 10721 | defer child_block.instructions.deinit(sema.gpa); |
| 10771 | return block.addStructFieldPtr(operand_ptr, field_index, ptr_field_ty); | 10722 | defer merges.deinit(sema.gpa); |
| 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 | } | | |
| 10785 | | 10723 | |
| 10786 | if (is_special_prong) { | 10724 | const raw_operand = try sema.resolveInst(zir_switch.main_operand); |
| 10787 | if (capture_byref) { | 10725 | const validated_switch = try sema.validateSwitchBlock(block, raw_operand, operand_is_ref, inst, &zir_switch); |
| 10788 | return operand_ptr; | 10726 | const maybe_ref = try sema.analyzeSwitchBlock(block, &child_block, raw_operand, operand_is_ref, merges, inst, &zir_switch, &validated_switch); |
| 10789 | } | 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)) { | 10733 | /// If the switch can be resolved to a value at comptime, this will return a `Ref` |
| 10792 | .error_set => if (spa.else_error_ty) |ty| { | 10734 | /// that's never `.none`. |
| 10793 | return sema.bitCast(block, ty, operand_val, operand_src, null); | 10735 | /// If not, this will return `null` and emit its instructions into `child_block`. |
| 10794 | } else { | 10736 | fn analyzeSwitchBlock( |
| 10795 | try sema.analyzeUnreachable(block, operand_src, false); | 10737 | sema: *Sema, |
| 10796 | return .unreachable_value; | 10738 | block: *Block, |
| 10797 | }, | 10739 | child_block: *Block, |
| 10798 | else => return operand_val, | 10740 | raw_operand: Air.Inst.Ref, |
| 10799 | } | 10741 | operand_is_ref: bool, |
| 10800 | } | 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)) { | 10751 | const src_node_offset = zir_switch.switch_src_node_offset; |
| 10803 | .@"union" => { | 10752 | const src = block.nodeOffset(src_node_offset); |
| 10804 | const union_obj = zcu.typeToUnion(operand_ty).?; | 10753 | const operand_src = block.src(.{ .node_offset_switch_operand = src_node_offset }); |
| 10805 | const first_item_val = sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, case_vals[0], undefined) catch unreachable; | | |
| 10806 | | 10754 | |
| 10807 | const first_field_index: u32 = zcu.unionTagFieldIndex(union_obj, first_item_val).?; | 10755 | const has_else = zir_switch.else_case != null; |
| 10808 | const first_field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[first_field_index]); | 10756 | const has_under = zir_switch.under_case != .none; |
| 10809 | | 10757 | |
| 10810 | const field_indices = try sema.arena.alloc(u32, case_vals.len); | 10758 | const else_case = validated_switch.else_case; |
| 10811 | for (case_vals, field_indices) |item, *field_idx| { | 10759 | const under_case = validated_switch.under_case; |
| 10812 | const item_val = sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, item, undefined) catch unreachable; | | |
| 10813 | field_idx.* = zcu.unionTagFieldIndex(union_obj, item_val).?; | | |
| 10814 | } | | |
| 10815 | | 10760 | |
| 10816 | // Fast path: if all the operands are the same type already, we don't need to hit | 10761 | const operand: SwitchOperand, const operand_ty: Type, const maybe_operand_opv: ?Value, const item_ty: Type = operand: { |
| 10817 | // PTR! This will also allow us to emit simpler code. | 10762 | const val, const ref = if (operand_is_ref) |
| 10818 | const same_types = for (field_indices[1..]) |field_idx| { | 10763 | .{ try sema.analyzeLoad(block, src, raw_operand, operand_src), raw_operand } |
| 10819 | const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]); | 10764 | else |
| 10820 | if (!field_ty.eql(first_field_ty, zcu)) break false; | 10765 | .{ raw_operand, undefined }; |
| 10821 | } else true; | | |
| 10822 | | 10766 | |
| 10823 | const capture_ty = if (same_types) first_field_ty else capture_ty: { | 10767 | const operand_ty = sema.typeOf(val); |
| 10824 | // We need values to run PTR on, so make a bunch of undef constants. | 10768 | const maybe_operand_opv = try sema.typeHasOnePossibleValue(operand_ty); |
| 10825 | const dummy_captures = try sema.arena.alloc(Air.Inst.Ref, case_vals.len); | 10769 | const init_cond: Air.Inst.Ref, const item_ty: Type = switch (operand_ty.zigTypeTag(zcu)) { |
| 10826 | for (dummy_captures, field_indices) |*dummy, field_idx| { | 10770 | .@"union" => tag: { |
| 10827 | const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]); | 10771 | const tag_ty = operand_ty.unionTagType(zcu).?; |
| 10828 | dummy.* = try pt.undefRef(field_ty); | 10772 | const tag_val = try sema.unionToTag(block, tag_ty, val, operand_src); |
| 10829 | } | 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); | 10781 | if (zir_switch.has_continue and !block.isComptime()) { |
| 10832 | for (case_srcs, 0..) |*case_src, i| { | 10782 | const operand_alloc: Air.Inst.Ref = if (zir_switch.any_maybe_runtime_capture and |
| 10833 | case_src.* = .{ | 10783 | maybe_operand_opv == null) |
| 10834 | .base_node_inst = capture_src.base_node_inst, | 10784 | alloc: { |
| 10835 | .offset = .{ .switch_case_item = .{ | 10785 | const operand_ptr_ty = try pt.singleMutPtrType(sema.typeOf(raw_operand)); |
| 10836 | .switch_node_offset = switch_node_offset, | 10786 | const operand_alloc = try block.addTy(.alloc, operand_ptr_ty); |
| 10837 | .case_idx = capture_src.offset.switch_capture.case_idx, | 10787 | _ = try block.addBinOp(.store, operand_alloc, raw_operand); |
| 10838 | .item_idx = .{ .kind = .single, .index = @intCast(i) }, | 10788 | break :alloc operand_alloc; |
| 10839 | } }, | 10789 | } else undefined; |
| 10840 | }; | 10790 | break :operand .{ .{ .loop = .{ |
| 10841 | } | 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) { | 10806 | const raw_operand_ty = sema.typeOf(raw_operand); |
| 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 | }; | | |
| 10852 | | 10807 | |
| 10853 | // By-reference captures have some further restrictions which make them easier to emit | 10808 | const union_originally = operand_ty.zigTypeTag(zcu) == .@"union"; |
| 10854 | if (capture_byref) { | 10809 | const err_set = operand_ty.zigTypeTag(zcu) == .error_set; |
| 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 | } | | |
| 10885 | | 10810 | |
| 10886 | break :resolve sema.resolvePeerTypes(block, capture_src, dummy_captures, .{ .override = case_srcs }) catch |err| switch (err) { | 10811 | if (item_ty.zigTypeTag(zcu) == .@"enum" and |
| 10887 | error.AnalysisFail => { | 10812 | validated_switch.seen_enum_fields.len == 0 and |
| 10888 | const msg = sema.err orelse return error.AnalysisFail; | 10813 | !operand_ty.isNonexhaustiveEnum(zcu)) |
| 10889 | try sema.errNote(capture_src, msg, "this coercion is only possible when capturing by value", .{}); | 10814 | { |
| 10890 | try sema.reparentOwnedErrorMsg(capture_src, msg, "capture group with incompatible types", .{}); | 10815 | return .void_value; // switch on empty enum/union |
| 10891 | return error.AnalysisFail; | 10816 | } |
| 10892 | }, | | |
| 10893 | else => |e| return e, | | |
| 10894 | }; | | |
| 10895 | }; | | |
| 10896 | | 10817 | |
| 10897 | if (try sema.resolveDefinedValue(block, operand_src, operand_ptr)) |op_ptr_val| { | 10818 | const cond_ref = switch (operand) { |
| 10898 | if (op_ptr_val.isUndef(zcu)) return pt.undefRef(capture_ptr_ty); | 10819 | .simple => |s| s.cond, |
| 10899 | const field_ptr_val = try op_ptr_val.ptrField(first_field_index, pt); | 10820 | .loop => |l| l.init_cond, |
| 10900 | return Air.internedToRef((try pt.getCoerced(field_ptr_val, capture_ptr_ty)).toIntern()); | 10821 | }; |
| 10901 | } | | |
| 10902 | | 10822 | |
| 10903 | try sema.requireRuntimeBlock(block, operand_src, null); | 10823 | // We treat `else` and `_` the same, except if both are present. |
| 10904 | return block.addStructFieldPtr(operand_ptr, first_field_index, capture_ptr_ty); | 10824 | const else_is_named_only = has_else and has_under; |
| 10905 | } | 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| { | 10828 | resolve_at_comptime: { |
| 10908 | if (operand_val_val.isUndef(zcu)) return pt.undefRef(capture_ty); | 10829 | // always runtime; evaluation in comptime scope uses `simple` |
| 10909 | const union_val = ip.indexToKey(operand_val_val.toIntern()).un; | 10830 | if (operand == .loop) break :resolve_at_comptime; |
| 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 | } | | |
| 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) { | 10837 | while (true) { |
| 10918 | return block.addStructFieldVal(operand_val, first_field_index, capture_ty); | 10838 | if (sema.resolveSwitchBlock( |
| 10919 | } | 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. | 10870 | const new_cond_ref = if (union_originally) |
| 10922 | // If we can, try to avoid that using in-memory coercions. | 10871 | try sema.unionToTag(child_block, item_ty, new_val, src) |
| 10923 | const first_non_imc = in_mem: { | 10872 | else |
| 10924 | for (field_indices, 0..) |field_idx, i| { | 10873 | new_val; |
| 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 | }; | | |
| 10935 | | 10874 | |
| 10936 | // By-val capture with heterogeneous types which are not all in-memory coercible to | 10875 | cur_cond_val = try sema.resolveConstDefinedValue(child_block, src, new_cond_ref, null); |
| 10937 | // the resolved capture type. We finally have to fall back to the ugly method. | 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 | 10887 | if (child_block.isComptime()) { |
| 10940 | // be several, and we can squash all of these cases into the same switch prong using | 10888 | _ = try sema.resolveConstDefinedValue(child_block, operand_src, operand.simple.cond, null); |
| 10941 | // a simple bitcast. We'll make this the 'else' prong. | 10889 | unreachable; |
| | 10890 | } |
| 10942 | | 10891 | |
| 10943 | var in_mem_coercible = try std.DynamicBitSet.initFull(sema.arena, field_indices.len); | 10892 | if (try sema.typeHasOnePossibleValue(item_ty)) |item_opv| { |
| 10944 | in_mem_coercible.unset(first_non_imc); | 10893 | // We simplify conditions with OPV to either a `loop` or a `block` since |
| 10945 | { | 10894 | // we cannot switch on a value which doesn't exist at runtime. |
| 10946 | const next = first_non_imc + 1; | 10895 | assert(operand == .loop); // `simple` should have already been comptime-resolved above! |
| 10947 | for (field_indices[next..], next..) |field_idx, i| { | 10896 | |
| 10948 | const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]); | 10897 | var case_block = child_block.makeSubBlock(); |
| 10949 | if (.ok != try sema.coerceInMemoryAllowed(block, capture_ty, field_ty, false, zcu.getTarget(), LazySrcLoc.unneeded, LazySrcLoc.unneeded, null)) { | 10898 | case_block.runtime_loop = null; |
| 10950 | in_mem_coercible.unset(i); | 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(.{ | 10942 | const analyze_body = sema.wantSwitchProngBodyAnalysis(block, .fromValue(item_opv), operand_ty, union_originally, err_set, false); |
| 10956 | .tag = .block, | 10943 | if (!analyze_body) return .unreachable_value; |
| 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 | } | | |
| 10977 | | 10944 | |
| 10978 | { | 10945 | if (!(err_set and |
| 10979 | // Non-bitcast cases | 10946 | try sema.maybeErrorUnwrap(&case_block, body, cond_ref, operand_src, true))) |
| 10980 | var it = in_mem_coercible.iterator(.{ .kind = .unset }); | 10947 | { |
| 10981 | while (it.next()) |idx| { | 10948 | // Set up captures manually to avoid special cases in the main logic. |
| 10982 | var coerce_block = block.makeSubBlock(); | 10949 | const payload_inst: Zir.Inst.Index = if (capture != .none) inst: { |
| 10983 | defer coerce_block.instructions.deinit(sema.gpa); | 10950 | const payload_inst = zir_switch.payload_capture_placeholder.unwrap() orelse switch_inst; |
| 10984 | | 10951 | const payload_ref: Air.Inst.Ref = payload_ref: { |
| 10985 | const case_src: LazySrcLoc = .{ | 10952 | const item_val: InternPool.Index = switch (operand_ty.zigTypeTag(zcu)) { |
| 10986 | .base_node_inst = capture_src.base_node_inst, | 10953 | .@"union" => item_val: { |
| 10987 | .offset = .{ .switch_case_item = .{ | 10954 | if (maybe_operand_opv) |operand_opv| { |
| 10988 | .switch_node_offset = switch_node_offset, | 10955 | break :item_val zcu.intern_pool.indexToKey(operand_opv.toIntern()).un.val; |
| 10989 | .case_idx = capture_src.offset.switch_capture.case_idx, | 10956 | } |
| 10990 | .item_idx = .{ .kind = .single, .index = @intCast(idx) }, | 10957 | assert(union_originally); // operand type must be union, otherwise it would be an OPV type here |
| 10991 | } }, | 10958 | assert(zir_switch.any_maybe_runtime_capture); // there's a payload capture |
| 10992 | }; | 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]; | 11003 | const tag_inst: Zir.Inst.Index = if (has_tag_capture) inst: { |
| 10995 | const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]); | 11004 | const tag_inst = zir_switch.tag_capture_placeholder.unwrap() orelse switch_inst; |
| 10996 | const uncoerced = try coerce_block.addStructFieldVal(operand_val, field_idx, field_ty); | 11005 | sema.inst_map.putAssumeCapacity(tag_inst, .fromValue(item_opv)); |
| 10997 | const coerced = try sema.coerce(&coerce_block, capture_ty, uncoerced, case_src); | 11006 | break :inst tag_inst; |
| 10998 | _ = try coerce_block.addBr(capture_block_inst, coerced); | 11007 | } else undefined; |
| 10999 | | 11008 | defer if (has_tag_capture) assert(sema.inst_map.remove(tag_inst)); |
| 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); | | |
| 11016 | | 11009 | |
| 11017 | const first_imc_item_idx = in_mem_coercible.findFirstSet().?; | 11010 | if (zir_switch.has_continue) sema.inst_map.putAssumeCapacity(switch_inst, .fromType(raw_operand_ty)); |
| 11018 | const first_imc_field_idx = field_indices[first_imc_item_idx]; | 11011 | defer if (zir_switch.has_continue) assert(sema.inst_map.remove(switch_inst)); |
| 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); | | |
| 11023 | | 11012 | |
| 11024 | try cases_extra.appendSlice(@ptrCast(coerce_block.instructions.items)); | 11013 | _ = try sema.analyzeBodyRuntimeBreak(&case_block, body); |
| 11025 | break :len coerce_block.instructions.items.len; | 11014 | } |
| 11026 | }; | | |
| 11027 | | 11015 | |
| 11028 | try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.SwitchBr).@"struct".fields.len + | 11016 | try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).@"struct".fields.len + |
| 11029 | cases_extra.items.len + | 11017 | case_block.instructions.items.len); |
| 11030 | @typeInfo(Air.Block).@"struct".fields.len + | 11018 | const payload_index = sema.addExtraAssumeCapacity(Air.Block{ |
| 11031 | 1); | 11019 | .body_len = @intCast(case_block.instructions.items.len), |
| 11032 | | 11020 | }); |
| 11033 | const switch_br_inst: u32 = @intCast(sema.air_instructions.len); | 11021 | sema.air_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items)); |
| 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 | } | | |
| 11077 | | 11022 | |
| 11078 | return capture_block_inst.toRef(); | 11023 | const air_tag: Air.Inst.Tag = if (merges.extra_insts.items.len > 0) |
| 11079 | }, | 11024 | .loop |
| 11080 | .error_set => { | 11025 | else |
| 11081 | if (capture_byref) { | 11026 | .block; |
| 11082 | return sema.fail( | 11027 | const air_loop_ref = try child_block.addInst(.{ |
| 11083 | block, | 11028 | .tag = air_tag, |
| 11084 | capture_src, | 11029 | .data = .{ .ty_pl = .{ |
| 11085 | "error set cannot be captured by reference", | 11030 | .ty = .noreturn_type, |
| 11086 | .{}, | 11031 | .payload = payload_index, |
| 11087 | ); | 11032 | } }, |
| 11088 | } | 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) { | 11048 | assert(maybe_operand_opv == null); // `operand_ty` can only be an OPV type if `item_ty` is one too! |
| 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 | } | | |
| 11095 | | 11049 | |
| 11096 | var names: InferredErrorSet.NameMap = .{}; | 11050 | try sema.finishSwitchBr( |
| 11097 | try names.ensureUnusedCapacity(sema.arena, case_vals.len); | 11051 | block, |
| 11098 | for (case_vals) |err| { | 11052 | child_block, |
| 11099 | const err_val = sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, err, undefined) catch unreachable; | 11053 | operand, |
| 11100 | names.putAssumeCapacityNoClobber(err_val.getErrorName(zcu).unwrap().?, {}); | 11054 | raw_operand_ty, |
| 11101 | } | 11055 | operand_is_ref, |
| 11102 | const error_ty = try pt.errorSetFromUnsortedNames(names.keys()); | 11056 | merges, |
| 11103 | return sema.bitCast(block, error_ty, operand_val, operand_src, null); | 11057 | switch_inst, |
| 11104 | }, | 11058 | zir_switch, |
| 11105 | else => { | 11059 | validated_switch, |
| 11106 | // In this case the capture value is just the passed-through value | 11060 | ); |
| 11107 | // of the switch condition. | 11061 | return null; |
| 11108 | if (capture_byref) { | 11062 | } |
| 11109 | return operand_ptr; | | |
| 11110 | } else { | | |
| 11111 | return operand_val; | | |
| 11112 | } | | |
| 11113 | }, | | |
| 11114 | } | | |
| 11115 | } | | |
| 11116 | }; | | |
| 11117 | | 11063 | |
| 11118 | fn switchCond( | 11064 | fn finishSwitchBr( |
| 11119 | sema: *Sema, | 11065 | sema: *Sema, |
| 11120 | block: *Block, | 11066 | block: *Block, |
| 11121 | src: LazySrcLoc, | 11067 | child_block: *Block, |
| 11122 | operand: Air.Inst.Ref, | 11068 | operand: SwitchOperand, |
| 11123 | ) CompileError!Air.Inst.Ref { | 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 | const pt = sema.pt; | 11076 | const pt = sema.pt; |
| 11125 | const zcu = pt.zcu; | 11077 | const zcu = pt.zcu; |
| 11126 | const operand_ty = sema.typeOf(operand); | 11078 | const ip = &zcu.intern_pool; |
| 11127 | switch (operand_ty.zigTypeTag(zcu)) { | 11079 | const gpa = sema.gpa; |
| 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 | }, | | |
| 11149 | | 11080 | |
| 11150 | .@"union" => { | 11081 | const src_node_offset = zir_switch.switch_src_node_offset; |
| 11151 | try operand_ty.resolveFields(pt); | 11082 | const src = block.nodeOffset(src_node_offset); |
| 11152 | const enum_ty = operand_ty.unionTagType(zcu) orelse { | 11083 | const operand_src = block.src(.{ .node_offset_switch_operand = src_node_offset }); |
| 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 | }, | | |
| 11165 | | 11084 | |
| 11166 | .error_union, | 11085 | const has_else = zir_switch.else_case != null; |
| 11167 | .noreturn, | 11086 | const has_under = zir_switch.under_case != .none; |
| 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 | } | | |
| 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 { | 11091 | const scalar_cases_len = zir_switch.scalarCasesLen(); |
| 11184 | const tracy = trace(@src()); | 11092 | const multi_cases_len = zir_switch.multiCasesLen(); |
| 11185 | defer tracy.end(); | | |
| 11186 | | 11093 | |
| 11187 | const pt = sema.pt; | 11094 | const operand_ty = if (operand_is_ref) |
| 11188 | const zcu = pt.zcu; | 11095 | raw_operand_ty.childType(zcu) |
| 11189 | const gpa = sema.gpa; | 11096 | else |
| 11190 | const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; | 11097 | raw_operand_ty; |
| 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 }); | | |
| 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 | // AstGen guarantees that the instruction immediately preceding | 11104 | // AstGen guarantees that the instruction immediately preceding |
| 11202 | // switch_block_err_union is a dbg_stmt | 11105 | // switch_block[_ref]/switch_block_err_union is a dbg_stmt. |
| 11203 | const cond_dbg_node_index: Zir.Inst.Index = @enumFromInt(@intFromEnum(inst) - 1); | 11106 | const cond_dbg_node_index: Zir.Inst.Index = @enumFromInt(@intFromEnum(switch_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; | | |
| 11223 | | 11107 | |
| 11224 | var case_vals = try std.ArrayList(Air.Inst.Ref).initCapacity(gpa, scalar_cases_len + 2 * multi_cases_len); | 11108 | const else_is_named_only = has_else and has_under; |
| 11225 | defer case_vals.deinit(gpa); | 11109 | const catch_all_case: CatchAllSwitchCase = |
| | 11110 | if (has_under) .under else if (has_else) .@"else" else .none; |
| 11226 | | 11111 | |
| 11227 | const NonError = struct { | 11112 | const item_ty = switch (operand_ty.zigTypeTag(zcu)) { |
| 11228 | body: []const Zir.Inst.Index, | 11113 | .@"union" => operand_ty.unionTagType(zcu).?, |
| 11229 | end: usize, | 11114 | else => operand_ty, |
| 11230 | capture: Zir.Inst.SwitchBlock.ProngInfo.Capture, | | |
| 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: { | 11119 | const estimated_cases_len: u32 = scalar_cases_len + multi_cases_len + |
| 11234 | const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[header_extra_index]); | 11120 | @intFromBool(has_else or has_under); |
| 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 | }; | | |
| 11242 | | 11121 | |
| 11243 | const Else = struct { | 11122 | var cases_extra: std.ArrayList(u32) = try .initCapacity(gpa, estimated_cases_len * |
| 11244 | body: []const Zir.Inst.Index, | 11123 | @typeInfo(Air.SwitchBr.Case).@"struct".fields.len); |
| 11245 | end: usize, | 11124 | defer cases_extra.deinit(gpa); |
| 11246 | is_inline: bool, | 11125 | var branch_hints: Air.SwitchBr.BranchHints = try .initCapacity(gpa, estimated_cases_len); |
| 11247 | has_capture: bool, | 11126 | defer branch_hints.deinit(gpa); |
| 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 | }; | | |
| 11267 | | 11127 | |
| 11268 | var seen_errors = SwitchErrorSet.init(gpa); | 11128 | // We will reuse this block for each case. |
| 11269 | defer seen_errors.deinit(); | 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); | 11136 | const case_vals = validated_switch.case_vals; |
| 11272 | const operand_err_set = if (extra.data.bits.payload_is_ref) | 11137 | var case_val_idx: usize = 0; |
| 11273 | operand_ty.childType(zcu) | 11138 | var case_it = zir_switch.iterateCases(); |
| 11274 | else | 11139 | var extra_index = zir_switch.end; |
| 11275 | operand_ty; | | |
| 11276 | | 11140 | |
| 11277 | if (operand_err_set.zigTypeTag(zcu) != .error_union) { | 11141 | var cases_len: u32 = 0; |
| 11278 | return sema.fail(block, switch_src, "expected error union type, found '{f}'", .{ | 11142 | while (case_it.next()) |case| { |
| 11279 | operand_ty.fmt(pt), | 11143 | if (case.isUnder()) { // we'll deal with this later |
| 11280 | }); | 11144 | extra_index += case.prong_info.body_len; |
| 11281 | } | 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); | 11161 | const prong_info = case.prong_info; |
| 11286 | try sema.air_instructions.append(gpa, .{ | 11162 | const prong_body = sema.code.bodySlice(extra_index, prong_info.body_len); |
| 11287 | .tag = .block, | 11163 | extra_index += prong_body.len; |
| 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 | }; | | |
| 11299 | | 11164 | |
| 11300 | var child_block: Block = .{ | 11165 | // Enough capacity for inlining regular items, we can't really predict |
| 11301 | .parent = block, | 11166 | // how many range items we will end up with (at least not in a safe and |
| 11302 | .sema = sema, | 11167 | // cheap manner) so we allocate on demand for those. |
| 11303 | .namespace = block.namespace, | 11168 | if (prong_info.is_inline) { |
| 11304 | .instructions = .{}, | 11169 | try branch_hints.ensureUnusedCapacity(gpa, @intCast(case.item_infos.len)); |
| 11305 | .label = &label, | 11170 | } |
| 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); | | |
| 11321 | | 11171 | |
| 11322 | const resolved_err_set = try sema.resolveInferredErrorSetTy(block, main_src, operand_err_set_ty.toIntern()); | 11172 | var emit_bb = false; |
| 11323 | if (Type.fromInterned(resolved_err_set).errorSetIsEmpty(zcu)) { | 11173 | var any_analyze_body = false; |
| 11324 | return sema.resolveBlockBody(block, main_operand_src, &child_block, non_error_case.body, inst, merges); | 11174 | for (case.item_infos, item_refs, 0..) |item_info, item_ref, item_i| { |
| 11325 | } | 11175 | if (item_info.bodyLen()) |body_len| extra_index += body_len; |
| 11326 | | 11176 | |
| 11327 | const else_error_ty: ?Type = try validateErrSetSwitch( | 11177 | const analyze_body = sema.wantSwitchProngBodyAnalysis(block, item_ref, operand_ty, union_originally, err_set, prong_info.is_comptime_unreach); |
| 11328 | sema, | 11178 | if (analyze_body) any_analyze_body = true; |
| 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 | ); | | |
| 11339 | | 11179 | |
| 11340 | var spa: SwitchProngAnalysis = .{ | 11180 | if (prong_info.is_inline) { |
| 11341 | .sema = sema, | 11181 | cases_len += 1; |
| 11342 | .parent_block = block, | 11182 | case_block.instructions.clearRetainingCapacity(); |
| 11343 | .operand = .{ | 11183 | case_block.error_return_trace_index = child_block.error_return_trace_index; |
| 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 | }; | | |
| 11354 | | 11184 | |
| 11355 | if (try sema.resolveDefinedValue(&child_block, main_src, raw_operand_val)) |ov| { | 11185 | if (emit_bb) { |
| 11356 | const operand_val = if (extra.data.bits.payload_is_ref) | 11186 | const bb_src = block.src(.{ .switch_case_item = .{ |
| 11357 | (try sema.pointerDeref(&child_block, main_src, ov, operand_ty)).? | 11187 | .switch_node_offset = src_node_offset, |
| 11358 | else | 11188 | .case_idx = case.index, |
| 11359 | ov; | 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)) { | 11195 | const prong_hint: std.builtin.BranchHint = hint: { |
| 11362 | return sema.resolveBlockBody(block, main_operand_src, &child_block, non_error_case.body, inst, merges); | 11196 | if (analyze_body) break :hint try sema.analyzeSwitchProng( |
| 11363 | } else { | 11197 | &case_block, |
| 11364 | const err_val = Value.fromInterned(try pt.intern(.{ | 11198 | operand, |
| 11365 | .err = .{ | 11199 | operand_ty, |
| 11366 | .ty = operand_err_set_ty.toIntern(), | 11200 | raw_operand_ty, |
| 11367 | .name = operand_val.getErrorName(zcu).unwrap().?, | 11201 | prong_body, |
| 11368 | }, | 11202 | block.src(.{ .switch_capture = .{ |
| 11369 | })); | 11203 | .switch_node_offset = src_node_offset, |
| 11370 | spa.operand.simple.by_val = if (extra.data.bits.payload_is_ref) | 11204 | .case_idx = case.index, |
| 11371 | try sema.analyzeErrUnionCodePtr(block, switch_operand_src, raw_operand_val) | 11205 | } }), |
| 11372 | else | 11206 | prong_info.capture, |
| 11373 | try sema.analyzeErrUnionCode(block, switch_operand_src, raw_operand_val); | 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) { | 11219 | try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len + |
| 11376 | sema.inst_map.putAssumeCapacity(err_capture_inst, spa.operand.simple.by_val); | 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) { | 11235 | any_analyze_body = true; // always an integer range, always needs analysis |
| 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 | } | | |
| 11411 | | 11236 | |
| 11412 | if (child_block.isComptime()) { | 11237 | if (prong_info.is_inline) { |
| 11413 | _ = try sema.resolveConstDefinedValue(&child_block, main_operand_src, raw_operand_val, null); | 11238 | var item = sema.resolveConstDefinedValue(block, .unneeded, range_ref[0], undefined) catch unreachable; |
| 11414 | unreachable; | 11239 | const item_last = sema.resolveConstDefinedValue(block, .unneeded, range_ref[1], undefined) catch unreachable; |
| 11415 | } | | |
| 11416 | | 11240 | |
| 11417 | const cond = if (extra.data.bits.payload_is_ref) blk: { | 11241 | if (try item.getUnsignedIntSema(pt)) |first_int| { |
| 11418 | try sema.checkErrorType(block, main_src, sema.typeOf(raw_operand_val).elemType2(zcu)); | 11242 | if (try item_last.getUnsignedIntSema(pt)) |last_int| { |
| 11419 | const loaded = try sema.analyzeLoad(block, main_src, raw_operand_val, main_src); | 11243 | if (std.math.cast(u32, last_int - first_int)) |range_len| { |
| 11420 | break :blk try sema.analyzeIsNonErr(block, main_src, loaded); | 11244 | try branch_hints.ensureUnusedCapacity(gpa, range_len); |
| 11421 | } else blk: { | 11245 | } |
| 11422 | try sema.checkErrorType(block, main_src, sema.typeOf(raw_operand_val)); | 11246 | } |
| 11423 | break :blk try sema.analyzeIsNonErr(block, main_src, raw_operand_val); | 11247 | } |
| 11424 | }; | | |
| 11425 | | 11248 | |
| 11426 | var sub_block = child_block.makeSubBlock(); | 11249 | var prev_result_overflowed = false; |
| 11427 | sub_block.runtime_loop = null; | 11250 | while (item.compareScalar(.lte, item_last, operand_ty, zcu)) : ({ |
| 11428 | sub_block.runtime_cond = main_operand_src; | 11251 | // Previous validation has resolved any possible lazy values. |
| 11429 | sub_block.runtime_index.increment(); | 11252 | const int_val: Value, const int_ty: Type = switch (operand_ty.zigTypeTag(zcu)) { |
| 11430 | sub_block.need_debug_scope = null; // this body is emitted regardless | 11253 | .int => .{ item, operand_ty }, |
| 11431 | defer sub_block.instructions.deinit(gpa); | 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); | 11276 | const item_ref: Air.Inst.Ref = .fromValue(item); |
| 11434 | const true_instructions = try sub_block.instructions.toOwnedSlice(gpa); | | |
| 11435 | defer gpa.free(true_instructions); | | |
| 11436 | | 11277 | |
| 11437 | spa.operand.simple.by_val = if (extra.data.bits.payload_is_ref) | 11278 | if (emit_bb) { |
| 11438 | try sema.analyzeErrUnionCodePtr(&sub_block, switch_operand_src, raw_operand_val) | 11279 | const bb_src = block.src(.{ .switch_case_item = .{ |
| 11439 | else | 11280 | .switch_node_offset = src_node_offset, |
| 11440 | try sema.analyzeErrUnionCode(&sub_block, switch_operand_src, raw_operand_val); | 11281 | .case_idx = case.index, |
| 11441 | | 11282 | .item_idx = .{ .kind = .range, .value = @intCast(range_i) }, |
| 11442 | if (extra.data.bits.any_uses_err_capture) { | 11283 | } }); |
| 11443 | sema.inst_map.putAssumeCapacity(err_capture_inst, spa.operand.simple.by_val); | 11284 | try sema.emitBackwardBranch(block, bb_src); |
| 11444 | } | 11285 | } |
| 11445 | defer if (extra.data.bits.any_uses_err_capture) assert(sema.inst_map.remove(err_capture_inst)); | 11286 | emit_bb = true; |
| 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 | ); | | |
| 11481 | | 11287 | |
| 11482 | try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).@"struct".fields.len + | 11288 | const prong_hint = try sema.analyzeSwitchProng( |
| 11483 | true_instructions.len + sub_block.instructions.items.len); | 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(.{ | 11308 | try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len + |
| 11486 | .tag = .cond_br, | 11309 | 1 + // `item`, no ranges |
| 11487 | .data = .{ | 11310 | case_block.instructions.items.len); |
| 11488 | .pl_op = .{ | 11311 | cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{ |
| 11489 | .operand = cond, | 11312 | .items_len = 1, |
| 11490 | .payload = sema.addExtraAssumeCapacity(Air.CondBr{ | 11313 | .ranges_len = 0, |
| 11491 | .then_body_len = @intCast(true_instructions.len), | 11314 | .body_len = @intCast(case_block.instructions.items.len), |
| 11492 | .else_body_len = @intCast(sub_block.instructions.items.len), | 11315 | })); |
| 11493 | .branch_hints = .{ | 11316 | cases_extra.appendAssumeCapacity(@intFromEnum(item_ref)); |
| 11494 | .true = non_error_hint, | 11317 | cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items)); |
| 11495 | .false = .none, | 11318 | } |
| 11496 | // Code coverage is desired for error handling. | 11319 | } |
| 11497 | .then_cov = .poi, | 11320 | } |
| 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)); | | |
| 11506 | | 11321 | |
| 11507 | return sema.resolveAnalyzedBlock(block, main_src, &child_block, merges, false); | 11322 | if (!prong_info.is_inline) { |
| 11508 | } | 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 { | 11327 | const prong_hint: std.builtin.BranchHint = hint: { |
| 11511 | const tracy = trace(@src()); | 11328 | if (any_analyze_body) break :hint try sema.analyzeSwitchProng( |
| 11512 | defer tracy.end(); | 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; | 11366 | const catch_all_extra: []const u32 = catch_all_extra: { |
| 11515 | const zcu = pt.zcu; | 11367 | if (catch_all_case == .none and !case_block.wantSafety()) { |
| 11516 | const ip = &zcu.intern_pool; | 11368 | try branch_hints.append(gpa, .none); |
| 11517 | const gpa = sema.gpa; | 11369 | break :catch_all_extra &.{}; |
| 11518 | const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; | 11370 | } |
| 11519 | const src = block.nodeOffset(inst_data.src_node); | 11371 | var emit_bb = false; |
| 11520 | const src_node_offset = inst_data.src_node; | 11372 | if (has_else and else_case.is_inline) { |
| 11521 | const operand_src = block.src(.{ .node_offset_switch_operand = src_node_offset }); | 11373 | const else_prong_src = block.src(.{ .node_offset_switch_else_prong = src_node_offset }); |
| 11522 | const else_prong_src = block.src(.{ .node_offset_switch_else_prong = src_node_offset }); | 11374 | var error_names: InternPool.NullTerminatedString.Slice = undefined; |
| 11523 | const under_prong_src = block.src(.{ .node_offset_switch_under_prong = src_node_offset }); | 11375 | var min_int: Value = undefined; |
| 11524 | const extra = sema.code.extraData(Zir.Inst.SwitchBlock, inst_data.payload_index); | 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: { | 11407 | const item_ref: Air.Inst.Ref = .fromValue(item_val); |
| 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 }; | | |
| 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()) { | 11414 | const prong_hint: std.builtin.BranchHint = hint: { |
| 11538 | // Even if the operand is comptime-known, this `switch` is runtime. | 11415 | if (analyze_body) break :hint try sema.analyzeSwitchProng( |
| 11539 | if (try operand_ty.comptimeOnlySema(pt)) { | 11416 | &case_block, |
| 11540 | return sema.failWithOwnedErrorMsg(block, msg: { | 11417 | operand, |
| 11541 | const msg = try sema.errMsg(operand_src, "operand of switch loop has comptime-only type '{f}'", .{operand_ty.fmt(pt)}); | 11418 | operand_ty, |
| 11542 | errdefer msg.destroy(gpa); | 11419 | raw_operand_ty, |
| 11543 | try sema.errNote(operand_src, msg, "switch loops are evaluated at runtime outside of comptime scopes", .{}); | 11420 | else_case.body, |
| 11544 | break :msg msg; | 11421 | block.src(.{ .switch_capture = .{ |
| 11545 | }); | 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 | 11451 | case_block.instructions.clearRetainingCapacity(); |
| 11567 | // is concerned, it really is dispatching a single prong. `resolveSwitchComptime` will | 11452 | case_block.error_return_trace_index = child_block.error_return_trace_index; |
| 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); | | |
| 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; | 11466 | if (else_is_named_only and !else_case.is_inline) { |
| 11593 | const multi_cases_len = if (extra.data.bits.has_multi_cases) blk: { | 11467 | // If we have both an `else` and an `_` prong, all named values go |
| 11594 | const multi_cases_len = sema.code.extra[header_extra_index]; | 11468 | // into the `else` prong and all unnamed ones go into the `_` prong. |
| 11595 | header_extra_index += 1; | 11469 | // We will manually enumerate all named values which haven't been |
| 11596 | break :blk multi_cases_len; | 11470 | // encountered yet and create an extra prong for them, which will |
| 11597 | } else 0; | 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: { | 11473 | assert(operand_ty.isNonexhaustiveEnum(zcu)); |
| 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; | | |
| 11608 | | 11474 | |
| 11609 | var case_vals = try std.ArrayList(Air.Inst.Ref).initCapacity(gpa, scalar_cases_len + 2 * multi_cases_len); | 11475 | cases_len += 1; |
| 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; | | |
| 11669 | | 11476 | |
| 11670 | // Duplicate checking variables later also used for `inline else`. | 11477 | const prong_hint: std.builtin.BranchHint = hint: { |
| 11671 | var seen_enum_fields: []?LazySrcLoc = &.{}; | 11478 | if (!else_case.is_inline) break :hint try sema.analyzeSwitchProng( |
| 11672 | var seen_errors = SwitchErrorSet.init(gpa); | 11479 | &case_block, |
| 11673 | var range_set = RangeSet.init(gpa, zcu); | 11480 | operand, |
| 11674 | var true_count: u8 = 0; | 11481 | operand_ty, |
| 11675 | var false_count: u8 = 0; | 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 { | 11501 | try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len + |
| 11678 | range_set.deinit(); | 11502 | (validated_switch.seen_enum_fields.len - zir_switch.totalItemsLen()) + |
| 11679 | gpa.free(seen_enum_fields); | 11503 | case_block.instructions.items.len); |
| 11680 | seen_errors.deinit(); | 11504 | const extra_case = cases_extra.addManyAsArrayAssumeCapacity( |
| 11681 | } | 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. | 11529 | const analyze_catch_all_body = analyze_body: { |
| 11688 | if (has_under and !raw_operand_ty.isNonexhaustiveEnum(zcu)) { | 11530 | switch (catch_all_case) { |
| 11689 | const msg = msg: { | 11531 | .none => break :analyze_body false, // we still may want a safety check! |
| 11690 | const msg = try sema.errMsg( | 11532 | .under => break :analyze_body true, // can't be a union anyway |
| 11691 | src, | 11533 | .@"else" => if (else_case.is_inline) break :analyze_body false, |
| 11692 | "'_' prong only allowed when switching on non-exhaustive enums", | 11534 | } |
| 11693 | .{}, | 11535 | if (union_originally) { |
| 11694 | ); | 11536 | const union_obj = zcu.typeToUnion(operand_ty).?; |
| 11695 | errdefer msg.destroy(gpa); | 11537 | for (validated_switch.seen_enum_fields, 0..) |seen_field, field_i| { |
| 11696 | try sema.errNote( | 11538 | if (seen_field != null) continue; |
| 11697 | under_prong_src, | 11539 | const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_i]); |
| 11698 | msg, | 11540 | if (!field_ty.isNoReturn(zcu)) break :analyze_body true; |
| 11699 | "'_' prong here", | 11541 | } |
| 11700 | .{}, | 11542 | break :analyze_body false; |
| 11701 | ); | 11543 | } |
| 11702 | try sema.errNote( | 11544 | if (err_set) { |
| 11703 | src, | 11545 | const else_err_ty = validated_switch.else_err_ty orelse { |
| 11704 | msg, | 11546 | assert(else_case.is_simple_noreturn); |
| 11705 | "consider using 'else'", | 11547 | break :analyze_body false; |
| 11706 | .{}, | 11548 | }; |
| 11707 | ); | 11549 | if (else_err_ty.errorSetIsEmpty(zcu)) break :analyze_body false; |
| 11708 | break :msg msg; | 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. | 11554 | const catch_all_hint = hint: { |
| 11714 | switch (cond_ty.zigTypeTag(zcu)) { | 11555 | if (analyze_catch_all_body) { |
| 11715 | .@"union" => unreachable, // handled in `switchCond` | 11556 | const index, const body, const capture, const has_tag_capture = switch (catch_all_case) { |
| 11716 | .@"enum" => { | 11557 | .@"else" => .{ else_case.index, else_case.body, else_case.capture, else_case.has_tag_capture }, |
| 11717 | seen_enum_fields = try gpa.alloc(?LazySrcLoc, cond_ty.enumFieldCount(zcu)); | 11558 | .under => .{ under_case.index, under_case.body, under_case.capture, under_case.has_tag_capture }, |
| 11718 | empty_enum = seen_enum_fields.len == 0 and !cond_ty.isNonexhaustiveEnum(zcu); | 11559 | .none => unreachable, |
| 11719 | @memset(seen_enum_fields, null); | 11560 | }; |
| 11720 | // `range_set` is used for non-exhaustive enum values that do not correspond to any tags. | 11561 | break :hint try sema.analyzeSwitchProng( |
| 11721 | | 11562 | &case_block, |
| 11722 | for (absorbed_items, 0..) |item_ref, item_i| { | 11563 | operand, |
| 11723 | _ = try sema.validateSwitchItemEnum( | 11564 | operand_ty, |
| 11724 | block, | 11565 | raw_operand_ty, |
| 11725 | seen_enum_fields, | 11566 | body, |
| 11726 | &range_set, | 11567 | block.src(.{ .switch_capture = .{ |
| 11727 | item_ref, | | |
| 11728 | cond_ty, | | |
| 11729 | block.src(.{ .switch_case_item = .{ | | |
| 11730 | .switch_node_offset = src_node_offset, | 11568 | .switch_node_offset = src_node_offset, |
| 11731 | .case_idx = .special_under, | 11569 | .case_idx = index, |
| 11732 | .item_idx = .{ .kind = .single, .index = @intCast(item_i) }, | | |
| 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); | 11580 | // We still need a terminator in this block, but we have proven |
| 11737 | | 11581 | // that it is unreachable. |
| 11738 | var extra_index: usize = special_end; | 11582 | if (case_block.wantSafety()) { |
| 11739 | { | 11583 | try sema.zirDbgStmt(&case_block, cond_dbg_node_index); |
| 11740 | var scalar_i: u32 = 0; | 11584 | try sema.safetyPanic(&case_block, src, .corrupt_switch); |
| 11741 | while (scalar_i < scalar_cases_len) : (scalar_i += 1) { | 11585 | } else { |
| 11742 | const item_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]); | 11586 | _ = try case_block.addNoOp(.unreach); |
| 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 | } | | |
| 11760 | } | 11587 | } |
| 11761 | { | 11588 | break :hint .cold; // Safety check / unreachable branches are cold. |
| 11762 | var multi_i: u32 = 0; | 11589 | }; |
| 11763 | while (multi_i < multi_cases_len) : (multi_i += 1) { | 11590 | try branch_hints.append(gpa, catch_all_hint); |
| 11764 | const items_len = sema.code.extra[extra_index]; | 11591 | break :catch_all_extra @ptrCast(case_block.instructions.items); |
| 11765 | extra_index += 1; | 11592 | }; |
| 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 | } | | |
| 11788 | | 11593 | |
| 11789 | try sema.validateSwitchNoRange(block, ranges_len, cond_ty, src_node_offset); | 11594 | assert(branch_hints.count == cases_len + 1); // +1 for catch-all hint |
| 11790 | } | 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 | const all_tags_handled = for (seen_enum_fields) |seen_src| { | 12088 | const all_tags_handled = for (seen_enum_fields) |seen_src| { |
| 11793 | if (seen_src == null) break false; | 12089 | if (seen_src == null) break false; |
| 11794 | } else true; | 12090 | } else true; |
| 11795 | | 12091 | |
| 11796 | if (has_else) { | 12092 | if (has_else) { |
| 11797 | if (all_tags_handled) { | 12093 | if (all_tags_handled) { |
| 11798 | if (cond_ty.isNonexhaustiveEnum(zcu)) { | 12094 | if (item_ty.isNonexhaustiveEnum(zcu)) { |
| 11799 | if (has_under) return sema.fail( | 12095 | if (has_under) return sema.fail( |
| 11800 | block, | 12096 | block, |
| 11801 | else_prong_src, | 12097 | else_prong_src, |
| ... | @@ -11820,9 +12116,9 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r | ... | @@ -11820,9 +12116,9 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r |
| 11820 | for (seen_enum_fields, 0..) |seen_src, i| { | 12116 | for (seen_enum_fields, 0..) |seen_src, i| { |
| 11821 | if (seen_src != null) continue; | 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 | try sema.addFieldErrNote( | 12120 | try sema.addFieldErrNote( |
| 11825 | cond_ty, | 12121 | item_ty, |
| 11826 | i, | 12122 | i, |
| 11827 | msg, | 12123 | msg, |
| 11828 | "unhandled enumeration value: '{f}'", | 12124 | "unhandled enumeration value: '{f}'", |
| ... | @@ -11830,15 +12126,17 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r | ... | @@ -11830,15 +12126,17 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r |
| 11830 | ); | 12126 | ); |
| 11831 | } | 12127 | } |
| 11832 | try sema.errNote( | 12128 | try sema.errNote( |
| 11833 | cond_ty.srcLoc(zcu), | 12129 | item_ty.srcLoc(zcu), |
| 11834 | msg, | 12130 | msg, |
| 11835 | "enum '{f}' declared here", | 12131 | "enum '{f}' declared here", |
| 11836 | .{cond_ty.fmt(pt)}, | 12132 | .{item_ty.fmt(pt)}, |
| 11837 | ); | 12133 | ); |
| 11838 | break :msg msg; | 12134 | break :msg msg; |
| 11839 | }; | 12135 | }; |
| 11840 | return sema.failWithOwnedErrorMsg(block, msg); | 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 | return sema.fail( | 12140 | return sema.fail( |
| 11843 | block, | 12141 | block, |
| 11844 | src, | 12142 | src, |
| ... | @@ -11847,101 +12145,83 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r | ... | @@ -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( | 12148 | .error_set => { |
| 11851 | sema, | 12149 | else_err_ty = ty: switch (try sema.resolveInferredErrorSetTy(block, src, item_ty.toIntern())) { |
| 11852 | block, | 12150 | .anyerror_type => { |
| 11853 | &seen_errors, | 12151 | if (!has_else) { |
| 11854 | &case_vals, | 12152 | return sema.fail( |
| 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( | | |
| 11900 | block, | 12153 | block, |
| 11901 | &range_set, | 12154 | src, |
| 11902 | item_ref, | 12155 | "else prong required when switching on type 'anyerror'", |
| 11903 | cond_ty, | 12156 | .{}, |
| 11904 | block.src(.{ .switch_case_item = .{ | 12157 | ); |
| 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 | )); | | |
| 11910 | } | 12158 | } |
| 11911 | | 12159 | break :ty .anyerror; |
| 11912 | try case_vals.ensureUnusedCapacity(gpa, 2 * ranges_len); | 12160 | }, |
| 11913 | var range_i: u32 = 0; | 12161 | else => |err_set_ty_index| { |
| 11914 | while (range_i < ranges_len) : (range_i += 1) { | 12162 | const error_names = ip.indexToKey(err_set_ty_index).error_set_type.names; |
| 11915 | const item_first: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]); | 12163 | var maybe_msg: ?*Zcu.ErrorMsg = null; |
| 11916 | extra_index += 1; | 12164 | errdefer if (maybe_msg) |msg| msg.destroy(sema.gpa); |
| 11917 | const item_last: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]); | 12165 | |
| 11918 | extra_index += 1; | 12166 | var seen_errors_from_set: u32 = 0; |
| 11919 | | 12167 | for (error_names.get(ip)) |error_name| { |
| 11920 | const vals = try sema.validateSwitchRange( | 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 | block, | 12200 | block, |
| 11922 | &range_set, | 12201 | else_prong_src, |
| 11923 | item_first, | 12202 | "unreachable else prong; all cases already handled", |
| 11924 | item_last, | 12203 | .{}, |
| 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 | } }), | | |
| 11931 | ); | 12204 | ); |
| 11932 | case_vals.appendAssumeCapacity(vals[0]); | | |
| 11933 | case_vals.appendAssumeCapacity(vals[1]); | | |
| 11934 | } | 12205 | } |
| 11935 | | 12206 | |
| 11936 | extra_index += info.body_len; | 12207 | var names: InferredErrorSet.NameMap = .{}; |
| 11937 | } | 12208 | try names.ensureUnusedCapacity(sema.arena, error_names.len); |
| 11938 | } | 12209 | for (error_names.get(ip)) |error_name| { |
| 11939 | | 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 | check_range: { | 12220 | check_range: { |
| 11941 | if (cond_ty.zigTypeTag(zcu) == .int) { | 12221 | if (type_tag == .int) { |
| 11942 | const min_int = try cond_ty.minInt(pt, cond_ty); | 12222 | const min_int = try item_ty.minInt(pt, item_ty); |
| 11943 | const max_int = try cond_ty.maxInt(pt, cond_ty); | 12223 | const max_int = try item_ty.maxInt(pt, item_ty); |
| 11944 | if (try range_set.spans(min_int.toIntern(), max_int.toIntern())) { | 12224 | if (try range_set.spans(arena, min_int, max_int, item_ty, zcu)) { |
| 11945 | if (has_else) { | 12225 | if (has_else) { |
| 11946 | return sema.fail( | 12226 | return sema.fail( |
| 11947 | block, | 12227 | block, |
| ... | @@ -11953,7 +12233,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r | ... | @@ -11953,7 +12233,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r |
| 11953 | break :check_range; | 12233 | break :check_range; |
| 11954 | } | 12234 | } |
| 11955 | } | 12235 | } |
| 11956 | if (special_prongs == .none) { | 12236 | if (!has_else) { |
| 11957 | return sema.fail( | 12237 | return sema.fail( |
| 11958 | block, | 12238 | block, |
| 11959 | src, | 12239 | src, |
| ... | @@ -11963,61 +12243,24 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r | ... | @@ -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 => { | 12246 | .enum_literal, .@"fn", .pointer, .type => { |
| 11967 | var extra_index: usize = special_end; | 12247 | if (!has_else) { |
| 11968 | { | 12248 | return sema.fail( |
| 11969 | var scalar_i: u32 = 0; | 12249 | block, |
| 11970 | while (scalar_i < scalar_cases_len) : (scalar_i += 1) { | 12250 | src, |
| 11971 | const item_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]); | 12251 | "else prong required when switching on type '{f}'", |
| 11972 | extra_index += 1; | 12252 | .{item_ty.fmt(pt)}, |
| 11973 | const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]); | 12253 | ); |
| 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 | } | | |
| 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 | if (has_else) { | 12262 | if (has_else) { |
| 12020 | if (true_count + false_count == 2) { | 12263 | if (all_values_handled) { |
| 12021 | return sema.fail( | 12264 | return sema.fail( |
| 12022 | block, | 12265 | block, |
| 12023 | else_prong_src, | 12266 | else_prong_src, |
| ... | @@ -12026,7 +12269,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r | ... | @@ -12026,7 +12269,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r |
| 12026 | ); | 12269 | ); |
| 12027 | } | 12270 | } |
| 12028 | } else { | 12271 | } else { |
| 12029 | if (true_count + false_count < 2) { | 12272 | if (!all_values_handled) { |
| 12030 | return sema.fail( | 12273 | return sema.fail( |
| 12031 | block, | 12274 | block, |
| 12032 | src, | 12275 | src, |
| ... | @@ -12036,1775 +12279,1073 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r | ... | @@ -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 => { | 12282 | else => unreachable, |
| 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 | }), | | |
| 12123 | } | 12283 | } |
| 12124 | | 12284 | |
| 12125 | var special_members_only: ?SpecialProng = null; | 12285 | return .{ |
| 12126 | var special_members_only_src: LazySrcLoc = undefined; | 12286 | .seen_enum_fields = seen_enum_fields, |
| 12127 | const special_generic, const special_generic_src = if (has_under) b: { | 12287 | .seen_errors = seen_errors, |
| 12128 | if (has_else) { | 12288 | .seen_sparse_values = seen_sparse_values, |
| 12129 | special_members_only = special_else; | 12289 | .seen_ranges = range_set.ranges.items, |
| 12130 | special_members_only_src = else_prong_src; | 12290 | .true_src = true_src, |
| 12131 | } | 12291 | .false_src = false_src, |
| 12132 | break :b .{ special_under, under_prong_src }; | 12292 | .void_src = void_src, |
| 12133 | } else .{ special_else, else_prong_src }; | | |
| 12134 | | 12293 | |
| 12135 | const spa: SwitchProngAnalysis = .{ | 12294 | .case_vals = case_vals.items, |
| 12136 | .sema = sema, | 12295 | .else_case = else_case, |
| 12137 | .parent_block = block, | 12296 | .under_case = under_case, |
| 12138 | .operand = operand, | 12297 | .else_err_ty = else_err_ty, |
| 12139 | .else_error_ty = else_error_ty, | | |
| 12140 | .switch_block_inst = inst, | | |
| 12141 | .tag_capture_inst = tag_capture_inst, | | |
| 12142 | }; | 12298 | }; |
| | 12299 | } |
| 12143 | | 12300 | |
| 12144 | const block_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len); | 12301 | fn resolveSwitchBlock( |
| 12145 | try sema.air_instructions.append(gpa, .{ | 12302 | sema: *Sema, |
| 12146 | .tag = .block, | 12303 | block: *Block, |
| 12147 | .data = undefined, | 12304 | child_block: *Block, |
| 12148 | }); | 12305 | operand: SwitchOperand, |
| 12149 | var label: Block.Label = .{ | 12306 | raw_operand_ty: Type, |
| 12150 | .zir_block = inst, | 12307 | maybe_lazy_cond_val: Value, |
| 12151 | .merges = .{ | 12308 | catch_all_case: CatchAllSwitchCase, |
| 12152 | .src_locs = .{}, | 12309 | else_is_named_only: bool, |
| 12153 | .results = .{}, | 12310 | merges: *Block.Merges, |
| 12154 | .br_list = .{}, | 12311 | switch_inst: Zir.Inst.Index, |
| 12155 | .block_inst = block_inst, | 12312 | zir_switch: *const Zir.UnwrappedSwitchBlock, |
| 12156 | }, | 12313 | validated_switch: *const ValidatedSwitchBlock, |
| 12157 | }; | 12314 | ) CompileError!Air.Inst.Ref { |
| | 12315 | const pt = sema.pt; |
| | 12316 | const zcu = pt.zcu; |
| 12158 | | 12317 | |
| 12159 | var child_block: Block = .{ | 12318 | const switch_node_offset = zir_switch.switch_src_node_offset; |
| 12160 | .parent = block, | 12319 | |
| 12161 | .sema = sema, | 12320 | const operand_ty = sema.typeOf(operand.simple.by_val); |
| 12162 | .namespace = block.namespace, | 12321 | const item_ty = switch (operand_ty.zigTypeTag(zcu)) { |
| 12163 | .instructions = .{}, | 12322 | .@"union" => operand_ty.unionTagType(zcu).?, |
| 12164 | .label = &label, | 12323 | else => operand_ty, |
| 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, | | |
| 12176 | }; | 12324 | }; |
| 12177 | const merges = &child_block.label.?.merges; | 12325 | const union_originally = operand_ty.zigTypeTag(zcu) == .@"union"; |
| 12178 | defer child_block.instructions.deinit(gpa); | 12326 | const err_set = item_ty.zigTypeTag(zcu) == .error_set; |
| 12179 | defer merges.deinit(gpa); | | |
| 12180 | | 12327 | |
| 12181 | if (scalar_cases_len + multi_cases_len == 0 and | 12328 | const cond_ref = operand.simple.cond; |
| 12182 | special_members_only == null and | 12329 | // We have to resolve lazy values to ensure that comparisons with switch |
| 12183 | !special_generic.is_inline) | 12330 | // prong items don't produce false negatives. |
| 12184 | { | 12331 | const cond_val = try sema.resolveLazyValue(maybe_lazy_cond_val); |
| 12185 | if (empty_enum) { | 12332 | |
| 12186 | return .void_value; | 12333 | const case_vals = validated_switch.case_vals; |
| 12187 | } | 12334 | var case_val_idx: usize = 0; |
| 12188 | if (special_prongs == .none) { | 12335 | var extra_index = zir_switch.end; |
| 12189 | return sema.fail(block, src, "switch must handle all possibilities", .{}); | 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) { | 12349 | |
| 12192 | .simple => |s| s.cond, | 12350 | const prong_info = case.prong_info; |
| 12193 | .loop => |l| l.init_cond, | 12351 | const prong_body = sema.code.bodySlice(extra_index, prong_info.body_len); |
| 12194 | }; | 12352 | extra_index += prong_body.len; |
| 12195 | if (zcu.backendSupportsFeature(.is_named_enum_value) and block.wantSafety() and | 12353 | for (case.item_infos) |item_info| { |
| 12196 | raw_operand_ty.zigTypeTag(zcu) == .@"enum" and !raw_operand_ty.isNonexhaustiveEnum(zcu)) | 12354 | if (item_info.bodyLen()) |body_len| extra_index += body_len; |
| 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); | | |
| 12201 | } | 12355 | } |
| 12202 | if (err_set and try sema.maybeErrorUnwrap(block, special_generic.body, init_cond, operand_src, false)) { | 12356 | for (case.range_infos) |range_info| { |
| 12203 | return .unreachable_value; | 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) { | 12361 | const item_refs = case_vals[case_val_idx..][0..case.item_infos.len]; |
| 12208 | .loop => {}, // always runtime; evaluation in comptime scope uses `simple` | 12362 | case_val_idx += item_refs.len; |
| 12209 | .simple => |s| { | 12363 | const range_refs: []const [2]Air.Inst.Ref = @ptrCast(case_vals[case_val_idx..][0 .. 2 * case.range_infos.len]); |
| 12210 | if (try sema.resolveDefinedValue(&child_block, src, s.cond)) |cond_val| { | 12364 | case_val_idx += 2 * range_refs.len; |
| 12211 | return resolveSwitchComptimeLoop( | 12365 | for (item_refs) |item_ref| { |
| 12212 | sema, | 12366 | const item_val = sema.resolveConstDefinedValue(child_block, .unneeded, item_ref, undefined) catch unreachable; |
| 12213 | spa, | 12367 | if (cond_val.eql(item_val, item_ty, zcu)) { |
| 12214 | &child_block, | 12368 | if (err_set) try sema.maybeErrorUnwrapComptime(child_block, prong_body, cond_ref); |
| 12215 | if (operand_is_ref) | 12369 | if (union_originally and operand_ty.unionFieldType(item_val, zcu).?.isNoReturn(zcu)) { |
| 12216 | sema.typeOf(s.by_ref) | 12370 | // This prong should be unreachable! |
| 12217 | else | 12371 | return .unreachable_value; |
| 12218 | raw_operand_ty, | 12372 | } |
| 12219 | cond_ty, | 12373 | return sema.resolveSwitchProng( |
| 12220 | cond_val, | 12374 | block, |
| 12221 | src_node_offset, | 12375 | child_block, |
| 12222 | special_members_only, | 12376 | operand, |
| 12223 | special_generic, | 12377 | raw_operand_ty, |
| 12224 | has_under, | 12378 | prong_body, |
| 12225 | case_vals, | 12379 | block.src(.{ .switch_capture = .{ |
| 12226 | scalar_cases_len, | 12380 | .switch_node_offset = switch_node_offset, |
| 12227 | multi_cases_len, | 12381 | .case_idx = case.index, |
| 12228 | err_set, | 12382 | } }), |
| 12229 | empty_enum, | 12383 | prong_info.capture, |
| 12230 | operand_is_ref, | 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 | | 12393 | } |
| 12234 | if (scalar_cases_len + multi_cases_len == 0 and | 12394 | for (range_refs) |range_ref| { |
| 12235 | special_members_only == null and | 12395 | const first_val = sema.resolveConstDefinedValue(child_block, .unneeded, range_ref[0], undefined) catch unreachable; |
| 12236 | !special_generic.is_inline and | 12396 | const last_val = sema.resolveConstDefinedValue(child_block, .unneeded, range_ref[1], undefined) catch unreachable; |
| 12237 | !extra.data.bits.has_continue) | 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( | 12400 | return sema.resolveSwitchProng( |
| 12240 | &child_block, | 12401 | block, |
| 12241 | .special, | 12402 | child_block, |
| 12242 | special_generic.body, | 12403 | operand, |
| 12243 | special_generic.capture, | 12404 | raw_operand_ty, |
| | 12405 | prong_body, |
| 12244 | block.src(.{ .switch_capture = .{ | 12406 | block.src(.{ .switch_capture = .{ |
| 12245 | .switch_node_offset = src_node_offset, | 12407 | .switch_node_offset = switch_node_offset, |
| 12246 | .case_idx = if (has_under) .special_under else .special_else, | 12408 | .case_idx = case.index, |
| 12247 | } }), | 12409 | } }), |
| 12248 | undefined, // case_vals may be undefined for special prongs | 12410 | prong_info.capture, |
| 12249 | .none, | 12411 | prong_info.has_tag_capture, |
| 12250 | false, | 12412 | if (prong_info.is_inline) cond_ref else .none, |
| | 12413 | .has_ranges, |
| | 12414 | validated_switch.else_err_ty, |
| 12251 | merges, | 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( | 12423 | const else_case = validated_switch.else_case; |
| 12344 | spa, | 12424 | const under_case = validated_switch.under_case; |
| 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 | } | | |
| 12386 | | 12425 | |
| 12387 | const new_operand_val = if (operand_is_ref) | 12426 | // named-only prong |
| 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 | }); | | |
| 12409 | | 12427 | |
| 12410 | if (replacement_block.instructions.items.len == 1) { | 12428 | if (else_is_named_only and item_ty.enumTagFieldIndex(cond_val, zcu) != null) { |
| 12411 | // Optimization: we don't need a block! | 12429 | assert(item_ty.isNonexhaustiveEnum(zcu)); |
| 12412 | sema.air_instructions.set( | 12430 | return sema.resolveSwitchProng( |
| 12413 | @intFromEnum(placeholder_inst), | 12431 | block, |
| 12414 | sema.air_instructions.get(@intFromEnum(replacement_block.instructions.items[0])), | 12432 | child_block, |
| 12415 | ); | 12433 | operand, |
| 12416 | continue; | 12434 | raw_operand_ty, |
| 12417 | } | 12435 | else_case.body, |
| 12418 | | 12436 | block.src(.{ .switch_capture = .{ |
| 12419 | // Replace placeholder with a block. | 12437 | .switch_node_offset = switch_node_offset, |
| 12420 | // No `br` is needed as the block is a switch dispatch so necessarily `noreturn`. | 12438 | .case_idx = else_case.index, |
| 12421 | try sema.air_extra.ensureUnusedCapacity( | 12439 | } }), |
| 12422 | gpa, | 12440 | else_case.capture, |
| 12423 | @typeInfo(Air.Block).@"struct".fields.len + replacement_block.instructions.items.len, | 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); | 12451 | // catch-all prong |
| 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; | | |
| 12513 | | 12452 | |
| 12514 | const item = case_vals.items[scalar_i]; | 12453 | const index, const body, const capture, const has_tag_capture, const is_inline = switch (catch_all_case) { |
| 12515 | // `item` is already guaranteed to be constant known. | 12454 | .@"else" => .{ else_case.index, else_case.body, else_case.capture, else_case.has_tag_capture, else_case.is_inline }, |
| 12516 | | 12455 | .under => .{ under_case.index, under_case.body, under_case.capture, under_case.has_tag_capture, false }, |
| 12517 | const analyze_body = if (union_originally) blk: { | 12456 | .none => unreachable, |
| 12518 | const unresolved_item_val = sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, item, undefined) catch unreachable; | 12457 | }; |
| 12519 | const item_val = sema.resolveLazyValue(unresolved_item_val) catch unreachable; | 12458 | if (err_set) try sema.maybeErrorUnwrapComptime(child_block, body, cond_ref); |
| 12520 | const field_ty = maybe_union_ty.unionFieldType(item_val, zcu).?; | 12459 | if (union_originally) { |
| 12521 | break :blk field_ty.zigTypeTag(zcu) != .noreturn; | 12460 | for (validated_switch.seen_enum_fields, 0..) |maybe_seen, field_i| { |
| 12522 | } else true; | 12461 | if (maybe_seen != null) continue; |
| 12523 | | 12462 | if (!operand_ty.unionFieldTypeByIndex(field_i, zcu).isNoReturn(zcu)) break; |
| 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 | }; | | |
| 12582 | } else { | 12463 | } else { |
| 12583 | @branchHint(.likely); | 12464 | // This prong should be unreachable! |
| 12584 | const items_len = sema.code.extra[extra_index]; | 12465 | return .unreachable_value; |
| 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 | }); | | |
| 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: { | 12489 | const SwitchOperand = union(enum) { |
| 12796 | var emit_bb = false; | 12490 | /// This switch will be dispatched only once, with the given operand. |
| 12797 | // If this is true we must have a 'true' else prong and not an underscore because | 12491 | simple: struct { |
| 12798 | // underscore prongs can never be inlined. We've already checked for this. | 12492 | /// The raw switch operand value. Always defined. |
| 12799 | if (else_prong.is_inline) switch (operand_ty.zigTypeTag(zcu)) { | 12493 | by_val: Air.Inst.Ref, |
| 12800 | .@"enum" => { | 12494 | /// The switch operand *pointer*. Defined only if there is a prong |
| 12801 | if (operand_ty.isNonexhaustiveEnum(zcu) and !union_originally) { | 12495 | /// with a by-ref capture. |
| 12802 | return sema.fail(block, else_prong_src, "cannot enumerate values of type '{f}' for 'inline else'", .{ | 12496 | by_ref: Air.Inst.Ref, |
| 12803 | operand_ty.fmt(pt), | 12497 | /// The switch condition value. For unions, `operand` is the union |
| 12804 | }); | 12498 | /// and `cond` is its enum tag value. |
| 12805 | } | 12499 | cond: Air.Inst.Ref, |
| 12806 | for (seen_enum_fields, 0..) |f, i| { | 12500 | }, |
| 12807 | if (f != null) continue; | 12501 | /// This switch may be dispatched multiple times with `continue` syntax. |
| 12808 | cases_len += 1; | 12502 | /// As such, the operand is stored in an alloc if needed. |
| 12809 | | 12503 | loop: struct { |
| 12810 | const item_val = try pt.enumValueFieldIndex(operand_ty, @intCast(i)); | 12504 | /// The `alloc` containing the `switch` operand for the active dispatch. |
| 12811 | const item_ref = Air.internedToRef(item_val.toIntern()); | 12505 | /// Each prong must load from this `alloc` to get captures. |
| 12812 | | 12506 | /// If there are no captures, this may be undefined. |
| 12813 | case_block.instructions.shrinkRetainingCapacity(0); | 12507 | operand_alloc: Air.Inst.Ref, |
| 12814 | case_block.error_return_trace_index = child_block.error_return_trace_index; | 12508 | /// Whether `operand_alloc` contains a by-val operand or a by-ref |
| 12815 | | 12509 | /// operand. |
| 12816 | const analyze_body = if (union_originally) blk: { | 12510 | operand_is_ref: bool, |
| 12817 | const field_ty = maybe_union_ty.unionFieldType(item_val, zcu).?; | 12511 | /// The switch condition value for the *initial* dispatch. For |
| 12818 | break :blk field_ty.zigTypeTag(zcu) != .noreturn; | 12512 | /// unions, this is the enum tag value. |
| 12819 | } else true; | 12513 | init_cond: Air.Inst.Ref, |
| 12820 | | 12514 | }, |
| 12821 | if (emit_bb) try sema.emitBackwardBranch(block, else_prong_src); | 12515 | }; |
| 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; | | |
| 12953 | | 12516 | |
| 12954 | if (emit_bb) try sema.emitBackwardBranch(block, else_prong_src); | 12517 | const CatchAllSwitchCase = enum { none, @"else", under }; |
| 12955 | emit_bb = true; | | |
| 12956 | | 12518 | |
| 12957 | const prong_hint = try spa.analyzeProngRuntime( | 12519 | const SwitchProngKind = union(enum) { |
| 12958 | &case_block, | 12520 | item_refs: []const Air.Inst.Ref, |
| 12959 | .special, | 12521 | has_ranges, |
| 12960 | else_prong.body, | 12522 | special, |
| 12961 | else_prong.capture, | 12523 | }; |
| 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); | | |
| 12971 | | 12524 | |
| 12972 | try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len + | 12525 | /// Resolve a switch prong which is determined at comptime to have no peers. |
| 12973 | 1 + // `item`, no ranges | 12526 | /// Sets up captures as needed. Uses `analyzeBodyRuntimeBreak`. |
| 12974 | case_block.instructions.items.len); | 12527 | fn resolveSwitchProng( |
| 12975 | cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{ | 12528 | sema: *Sema, |
| 12976 | .items_len = 1, | 12529 | block: *Block, |
| 12977 | .ranges_len = 0, | 12530 | child_block: *Block, |
| 12978 | .body_len = @intCast(case_block.instructions.items.len), | 12531 | operand: SwitchOperand, |
| 12979 | })); | 12532 | raw_operand_ty: Type, |
| 12980 | cases_extra.appendAssumeCapacity(@intFromEnum(Air.Inst.Ref.bool_true)); | 12533 | prong_body: []const Zir.Inst.Index, |
| 12981 | cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items)); | 12534 | /// Must use the `switch_capture` field in `offset`. |
| 12982 | } | 12535 | capture_src: LazySrcLoc, |
| 12983 | if (false_count == 0) { | 12536 | capture: Zir.Inst.SwitchBlock.ProngInfo.Capture, |
| 12984 | cases_len += 1; | 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); | 12549 | // We can propagate `.cold` hints from this branch since it's comptime-known |
| 12987 | case_block.error_return_trace_index = child_block.error_return_trace_index; | 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); | 12554 | const payload_inst: Zir.Inst.Index = if (capture != .none) inst: { |
| 12990 | emit_bb = true; | 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( | 12579 | const tag_inst: Zir.Inst.Index = if (has_tag_capture) inst: { |
| 12993 | &case_block, | 12580 | const tag_inst = zir_switch.tag_capture_placeholder.unwrap() orelse switch_inst; |
| 12994 | .special, | 12581 | const tag_ref = try sema.analyzeSwitchTagCapture( |
| 12995 | else_prong.body, | 12582 | child_block, |
| 12996 | else_prong.capture, | 12583 | operand.simple.by_val, |
| 12997 | child_block.src(.{ .switch_capture = .{ | 12584 | sema.typeOf(operand.simple.by_val), |
| 12998 | .switch_node_offset = switch_node_offset, | 12585 | capture_src, |
| 12999 | .case_idx = .special_else, | 12586 | inline_case_capture, |
| 13000 | } }), | 12587 | ); |
| 13001 | &.{.bool_false}, | 12588 | sema.inst_map.putAssumeCapacity(tag_inst, tag_ref); |
| 13002 | .bool_false, | 12589 | break :inst tag_inst; |
| 13003 | else_prong.has_tag_capture, | 12590 | } else undefined; |
| 13004 | ); | 12591 | defer if (has_tag_capture) assert(sema.inst_map.remove(tag_inst)); |
| 13005 | try branch_hints.append(gpa, prong_hint); | | |
| 13006 | | 12592 | |
| 13007 | try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len + | 12593 | if (zir_switch.has_continue) sema.inst_map.putAssumeCapacity(switch_inst, .fromType(raw_operand_ty)); |
| 13008 | 1 + // `item`, no ranges | 12594 | defer if (zir_switch.has_continue) assert(sema.inst_map.remove(switch_inst)); |
| 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 | }; | | |
| 13023 | | 12595 | |
| 13024 | case_block.instructions.shrinkRetainingCapacity(0); | 12596 | return sema.resolveBlockBody(block, src, child_block, prong_body, switch_inst, merges); |
| 13025 | case_block.error_return_trace_index = child_block.error_return_trace_index; | 12597 | } |
| 13026 | | 12598 | |
| 13027 | if (zcu.backendSupportsFeature(.is_named_enum_value) and | 12599 | fn wantSwitchProngBodyAnalysis( |
| 13028 | else_prong.body.len != 0 and block.wantSafety() and | 12600 | sema: *Sema, |
| 13029 | operand_ty.zigTypeTag(zcu) == .@"enum" and | 12601 | block: *Block, |
| 13030 | (!operand_ty.isNonexhaustiveEnum(zcu) or union_originally)) | 12602 | item_ref: Air.Inst.Ref, |
| 13031 | { | 12603 | operand_ty: Type, |
| 13032 | try sema.zirDbgStmt(&case_block, cond_dbg_node_index); | 12604 | union_originally: bool, |
| 13033 | const ok = try case_block.addUnOp(.is_named_enum_value, operand); | 12605 | err_set: bool, |
| 13034 | try sema.addSafetyCheck(&case_block, src, ok, .corrupt_switch); | 12606 | prong_is_comptime_unreach: bool, |
| 13035 | } | 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) | 12624 | /// Assumes that `operand_ty` has more than one possible value. |
| 13038 | .special_under | 12625 | /// Sets up captures as needed. Uses `analyzeBodyRuntimeBreak`. |
| 13039 | else | 12626 | fn analyzeSwitchProng( |
| 13040 | .special_else; | 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) | 12646 | const operand_src = case_block.src(.{ .node_offset_switch_operand = zir_switch.switch_src_node_offset }); |
| 13043 | for (seen_enum_fields, 0..) |seen_field, index| { | 12647 | |
| 13044 | if (seen_field != null) continue; | 12648 | if (operand_ty.zigTypeTag(zcu) == .error_set) { |
| 13045 | const union_obj = zcu.typeToUnion(maybe_union_ty).?; | 12649 | const cond_ref = switch (operand) { |
| 13046 | const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[index]); | 12650 | .simple => |s| s.cond, |
| 13047 | if (field_ty.zigTypeTag(zcu) != .noreturn) break true; | 12651 | .loop => |l| l.init_cond, |
| 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; | | |
| 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); | 12659 | const operand_val, const operand_ptr = load_operand: { |
| 13084 | break :else_body case_block.instructions.items; | 12660 | if (capture == .none and !has_tag_capture) { |
| 13085 | } else else_body: { | 12661 | // No need to load the operand for this prong! |
| 13086 | try branch_hints.append(gpa, .none); | 12662 | break :load_operand .{ undefined, undefined }; |
| 13087 | break :else_body &.{}; | 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); | 12686 | const payload_inst: Zir.Inst.Index = if (capture != .none) inst: { |
| 13091 | | 12687 | const payload_inst = zir_switch.payload_capture_placeholder.unwrap() orelse switch_inst; |
| 13092 | try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr).@"struct".fields.len + | 12688 | const payload_ref = try sema.analyzeSwitchPayloadCapture( |
| 13093 | cases_extra.items.len + else_body.len + | 12689 | case_block, |
| 13094 | (std.math.divCeil(usize, branch_hints.items.len, 10) catch unreachable)); // branch hints | 12690 | operand, |
| 13095 | | 12691 | operand_val, |
| 13096 | const payload_index = sema.addExtraAssumeCapacity(Air.SwitchBr{ | 12692 | operand_ptr, |
| 13097 | .cases_len = @intCast(cases_len), | 12693 | operand_ty, |
| 13098 | .else_body_len = @intCast(else_body.len), | 12694 | operand_src, |
| 13099 | }); | 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 | { | 12711 | const tag_inst: Zir.Inst.Index = if (has_tag_capture) inst: { |
| 13102 | // Add branch hints. | 12712 | const tag_inst = zir_switch.tag_capture_placeholder.unwrap() orelse switch_inst; |
| 13103 | var cur_bag: u32 = 0; | 12713 | const tag_ref = try sema.analyzeSwitchTagCapture( |
| 13104 | for (branch_hints.items, 0..) |hint, idx| { | 12714 | case_block, |
| 13105 | const idx_in_bag = idx % 10; | 12715 | operand_val, |
| 13106 | cur_bag |= @as(u32, @intFromEnum(hint)) << @intCast(idx_in_bag * 3); | 12716 | operand_ty, |
| 13107 | if (idx_in_bag == 9) { | 12717 | capture_src, |
| 13108 | sema.air_extra.appendAssumeCapacity(cur_bag); | 12718 | inline_case_capture, |
| 13109 | cur_bag = 0; | 12719 | ); |
| 13110 | } | 12720 | sema.inst_map.putAssumeCapacity(tag_inst, tag_ref); |
| 13111 | } | 12721 | break :inst tag_inst; |
| 13112 | if (branch_hints.items.len % 10 != 0) { | 12722 | } else undefined; |
| 13113 | sema.air_extra.appendAssumeCapacity(cur_bag); | 12723 | defer if (has_tag_capture) assert(sema.inst_map.remove(tag_inst)); |
| 13114 | } | | |
| 13115 | } | | |
| 13116 | sema.air_extra.appendSliceAssumeCapacity(@ptrCast(cases_extra.items)); | | |
| 13117 | sema.air_extra.appendSliceAssumeCapacity(@ptrCast(else_body)); | | |
| 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(.{ | 12728 | return sema.analyzeBodyRuntimeBreak(case_block, prong_body); |
| 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 | }); | | |
| 13128 | } | 12729 | } |
| 13129 | | 12730 | |
| 13130 | fn resolveSwitchComptimeLoop( | 12731 | fn analyzeSwitchTagCapture( |
| 13131 | sema: *Sema, | 12732 | sema: *Sema, |
| 13132 | init_spa: SwitchProngAnalysis, | 12733 | case_block: *Block, |
| 13133 | child_block: *Block, | 12734 | /// May be `undefined` if `inline_case_capture` is not `.none`. |
| 13134 | maybe_ptr_operand_ty: Type, | 12735 | operand_val: Air.Inst.Ref, |
| 13135 | cond_ty: Type, | 12736 | operand_ty: Type, |
| 13136 | init_cond_val: Value, | 12737 | capture_src: LazySrcLoc, |
| 13137 | switch_node_offset: std.zig.Ast.Node.Offset, | 12738 | inline_case_capture: Air.Inst.Ref, |
| 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, | | |
| 13147 | ) CompileError!Air.Inst.Ref { | 12739 | ) CompileError!Air.Inst.Ref { |
| 13148 | var spa = init_spa; | 12740 | const pt = sema.pt; |
| 13149 | var cond_val = init_cond_val; | 12741 | const zcu = pt.zcu; |
| 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 }; | | |
| 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); | 12748 | if (operand_ty.zigTypeTag(zcu) != .@"union") { |
| 13191 | spa.operand = .{ .simple = .{ | 12749 | return sema.fail(case_block, tag_capture_src, "cannot capture tag of non-union type '{f}'", .{ |
| 13192 | .by_val = val, | 12750 | operand_ty.fmt(pt), |
| 13193 | .by_ref = ref, | 12751 | }); |
| 13194 | .cond = cond_ref, | 12752 | } |
| 13195 | } }; | 12753 | if (inline_case_capture != .none) { |
| 13196 | }, | 12754 | return inline_case_capture; // this already is the tag, it's what we're switching on! |
| 13197 | else => |e| return e, | | |
| 13198 | } | | |
| 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 | sema: *Sema, | 12761 | sema: *Sema, |
| 13204 | spa: SwitchProngAnalysis, | 12762 | case_block: *Block, |
| 13205 | child_block: *Block, | 12763 | operand: SwitchOperand, |
| 13206 | cond_operand: Air.Inst.Ref, | 12764 | /// May be `undefined` if this is an inline capture and operand is not a union. |
| 13207 | operand_val: Value, | 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 | operand_ty: Type, | 12768 | operand_ty: Type, |
| 13209 | switch_node_offset: std.zig.Ast.Node.Offset, | 12769 | operand_src: LazySrcLoc, |
| 13210 | special_members_only: ?SpecialProng, | 12770 | capture_src: LazySrcLoc, |
| 13211 | special_generic: SpecialProng, | 12771 | capture_by_ref: bool, |
| 13212 | special_generic_is_under: bool, | 12772 | is_special_prong: bool, |
| 13213 | case_vals: std.ArrayList(Air.Inst.Ref), | 12773 | /// May be `undefined` if `is_special_prong` is `true`. |
| 13214 | scalar_cases_len: u32, | 12774 | case_vals: []const Air.Inst.Ref, |
| 13215 | multi_cases_len: u32, | 12775 | /// If this is not `.none`, this is an inline capture. |
| 13216 | err_set: bool, | 12776 | inline_case_capture: Air.Inst.Ref, |
| 13217 | empty_enum: bool, | 12777 | else_err_ty: ?Type, |
| 13218 | ) CompileError!Air.Inst.Ref { | 12778 | ) CompileError!Air.Inst.Ref { |
| 13219 | const zcu = sema.pt.zcu; | 12779 | const pt = sema.pt; |
| 13220 | const merges = &child_block.label.?.merges; | 12780 | const zcu = pt.zcu; |
| 13221 | const resolved_operand_val = try sema.resolveLazyValue(operand_val); | 12781 | const ip = &zcu.intern_pool; |
| 13222 | | 12782 | |
| 13223 | var extra_index: usize = special_generic.end; | 12783 | const switch_node_offset = operand_src.offset.node_offset_switch_operand; |
| 13224 | { | 12784 | |
| 13225 | var scalar_i: usize = 0; | 12785 | if (inline_case_capture != .none) { |
| 13226 | while (scalar_i < scalar_cases_len) : (scalar_i += 1) { | 12786 | const item_val = sema.resolveConstDefinedValue(case_block, .unneeded, inline_case_capture, undefined) catch unreachable; |
| 13227 | extra_index += 1; | 12787 | if (operand_ty.zigTypeTag(zcu) == .@"union") { |
| 13228 | const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]); | 12788 | const field_index: u32 = @intCast(operand_ty.unionTagFieldIndex(item_val, zcu).?); |
| 13229 | extra_index += 1; | 12789 | const union_obj = zcu.typeToUnion(operand_ty).?; |
| 13230 | const body = sema.code.bodySlice(extra_index, info.body_len); | 12790 | const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]); |
| 13231 | extra_index += info.body_len; | 12791 | if (capture_by_ref) { |
| 13232 | | 12792 | const operand_ptr_info = sema.typeOf(operand_ptr).ptrInfo(zcu); |
| 13233 | const item = case_vals.items[scalar_i]; | 12793 | const ptr_field_ty = try pt.ptrTypeSema(.{ |
| 13234 | const item_val = sema.resolveConstDefinedValue(child_block, LazySrcLoc.unneeded, item, undefined) catch unreachable; | 12794 | .child = field_ty.toIntern(), |
| 13235 | if (operand_val.eql(item_val, operand_ty, sema.pt.zcu)) { | 12795 | .flags = .{ |
| 13236 | if (err_set) try sema.maybeErrorUnwrapComptime(child_block, body, cond_operand); | 12796 | .is_const = operand_ptr_info.flags.is_const, |
| 13237 | return spa.resolveProngComptime( | 12797 | .is_volatile = operand_ptr_info.flags.is_volatile, |
| 13238 | child_block, | 12798 | .address_space = operand_ptr_info.flags.address_space, |
| 13239 | .normal, | 12799 | }, |
| 13240 | body, | 12800 | }); |
| 13241 | info.capture, | 12801 | return case_block.addStructFieldPtr(operand_ptr, field_index, ptr_field_ty); |
| 13242 | child_block.src(.{ .switch_capture = .{ | 12802 | } else { |
| 13243 | .switch_node_offset = switch_node_offset, | 12803 | if (try sema.resolveDefinedValue(case_block, operand_src, operand_val)) |union_val| { |
| 13244 | .case_idx = .{ .kind = .scalar, .index = @intCast(scalar_i) }, | 12804 | const tag_and_val = ip.indexToKey(union_val.toIntern()).un; |
| 13245 | } }), | 12805 | return .fromIntern(tag_and_val.val); |
| 13246 | &.{item}, | 12806 | } |
| 13247 | if (info.is_inline) cond_operand else .none, | 12807 | return case_block.addStructFieldVal(operand_val, field_index, field_ty); |
| 13248 | info.has_tag_capture, | | |
| 13249 | merges, | | |
| 13250 | ); | | |
| 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; | 12816 | const operand_ptr_ty = if (capture_by_ref) sema.typeOf(operand_ptr) else undefined; |
| 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; | | |
| 13296 | | 12817 | |
| 13297 | // Validation above ensured these will succeed. | 12818 | if (is_special_prong) { |
| 13298 | const first_val = sema.resolveConstDefinedValue(child_block, LazySrcLoc.unneeded, range_items[0], undefined) catch unreachable; | 12819 | if (capture_by_ref) return operand_ptr; |
| 13299 | const last_val = sema.resolveConstDefinedValue(child_block, LazySrcLoc.unneeded, range_items[1], undefined) catch unreachable; | 12820 | return switch (operand_ty.zigTypeTag(zcu)) { |
| 13300 | if ((try sema.compareAll(resolved_operand_val, .gte, first_val, operand_ty)) and | 12821 | .error_set => e: { |
| 13301 | (try sema.compareAll(resolved_operand_val, .lte, last_val, operand_ty))) | 12822 | if (else_err_ty) |err_ty| { |
| 13302 | { | 12823 | break :e sema.bitCast(case_block, err_ty, operand_val, operand_src, null); |
| 13303 | if (err_set) try sema.maybeErrorUnwrapComptime(child_block, body, cond_operand); | 12824 | } else { |
| 13304 | return spa.resolveProngComptime( | 12825 | try sema.analyzeUnreachable(case_block, operand_src, false); |
| 13305 | child_block, | 12826 | break :e .unreachable_value; |
| 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 | ); | | |
| 13318 | } | 12827 | } |
| 13319 | } | 12828 | }, |
| 13320 | | 12829 | else => operand_val, |
| 13321 | extra_index += info.body_len; | 12830 | }; |
| 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 | } | | |
| 13346 | } | 12831 | } |
| 13347 | | 12832 | |
| 13348 | return spa.resolveProngComptime( | 12833 | switch (operand_ty.zigTypeTag(zcu)) { |
| 13349 | child_block, | 12834 | .@"union" => { |
| 13350 | .special, | 12835 | const union_obj = zcu.typeToUnion(operand_ty).?; |
| 13351 | special_generic.body, | 12836 | const first_item_val = sema.resolveConstDefinedValue(case_block, .unneeded, case_vals[0], undefined) catch unreachable; |
| 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 | } | | |
| 13366 | | 12837 | |
| 13367 | const RangeSetUnhandledIterator = struct { | 12838 | const first_field_index: u32 = zcu.unionTagFieldIndex(union_obj, first_item_val).?; |
| 13368 | pt: Zcu.PerThread, | 12839 | const first_field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[first_field_index]); |
| 13369 | cur: ?InternPool.Index, | | |
| 13370 | max: InternPool.Index, | | |
| 13371 | range_i: usize, | | |
| 13372 | ranges: []const RangeSet.Range, | | |
| 13373 | limbs: []math.big.Limb, | | |
| 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 { | 12847 | // Fast path: if all the operands are the same type already, we don't need to hit |
| 13378 | const pt = sema.pt; | 12848 | // PTR! This will also allow us to emit simpler code. |
| 13379 | const int_type = pt.zcu.intern_pool.indexToKey(ty.toIntern()).int_type; | 12849 | const same_types = for (field_indices[1..]) |field_idx| { |
| 13380 | const needed_limbs = math.big.int.calcTwosCompLimbCount(int_type.bits); | 12850 | const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]); |
| 13381 | return .{ | 12851 | if (!field_ty.eql(first_field_ty, zcu)) break false; |
| 13382 | .pt = pt, | 12852 | } else true; |
| 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 | } | | |
| 13393 | | 12853 | |
| 13394 | fn addOne(it: *const RangeSetUnhandledIterator, val: InternPool.Index) !?InternPool.Index { | 12854 | const capture_ty: Type = capture_ty: { |
| 13395 | if (val == it.max) return null; | 12855 | if (same_types) break :capture_ty first_field_ty; |
| 13396 | const int = it.pt.zcu.intern_pool.indexToKey(val).int; | 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) { | 12863 | const case_srcs = try sema.arena.alloc(?LazySrcLoc, case_vals.len); |
| 13399 | inline .u64, .i64 => |val_int| { | 12864 | for (case_srcs, 0..) |*case_src, item_i| { |
| 13400 | const next_int = @addWithOverflow(val_int, 1); | 12865 | case_src.* = .{ |
| 13401 | if (next_int[1] == 0) | 12866 | .base_node_inst = capture_src.base_node_inst, |
| 13402 | return (try it.pt.intValue(.fromInterned(int.ty), next_int[0])).toIntern(); | 12867 | .offset = .{ .switch_case_item = .{ |
| 13403 | }, | 12868 | .switch_node_offset = switch_node_offset, |
| 13404 | .big_int => {}, | 12869 | .case_idx = capture_src.offset.switch_capture.case_idx, |
| 13405 | .lazy_align, .lazy_size => unreachable, | 12870 | .item_idx = .{ .kind = .single, .value = @intCast(item_i) }, |
| 13406 | } | 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; | 12890 | // By-reference captures have some further restrictions which make them easier to emit |
| 13409 | const val_bigint = int.storage.toBigInt(&val_space); | 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; | 12923 | break :resolve sema.resolvePeerTypes( |
| 13412 | var result_bigint = math.big.int.Mutable.init( | 12924 | case_block, |
| 13413 | if (it.limbs.len > 0) it.limbs else &result_limbs, | 12925 | capture_src, |
| 13414 | 0, | 12926 | dummy_captures, |
| 13415 | ); | 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); | 12939 | if (try sema.resolveDefinedValue(case_block, operand_src, operand_ptr)) |op_ptr_val| { |
| 13418 | return (try it.pt.intValue_big(.fromInterned(int.ty), result_bigint.toConst())).toIntern(); | 12940 | if (op_ptr_val.isUndef(zcu)) return pt.undefRef(capture_ptr_ty); |
| 13419 | } | 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 { | 12945 | try sema.requireRuntimeBlock(case_block, operand_src, null); |
| 13422 | var cur = it.cur orelse return null; | 12946 | return case_block.addStructFieldPtr(operand_ptr, first_field_index, capture_ptr_ty); |
| 13423 | while (it.range_i < it.ranges.len and cur == it.ranges[it.range_i].first) { | 12947 | } |
| 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 | }; | | |
| 13434 | | 12948 | |
| 13435 | const ResolvedSwitchItem = struct { | 12949 | if (try sema.resolveDefinedValue(case_block, operand_src, operand_val)) |operand_val_val| { |
| 13436 | ref: Air.Inst.Ref, | 12950 | if (operand_val_val.isUndef(zcu)) return pt.undefRef(capture_ty); |
| 13437 | val: InternPool.Index, | 12951 | const union_val = ip.indexToKey(operand_val_val.toIntern()).un; |
| 13438 | }; | 12952 | if (Value.fromInterned(union_val.tag).isUndef(zcu)) return pt.undefRef(capture_ty); |
| 13439 | fn resolveSwitchItemVal( | 12953 | const uncoerced: Air.Inst.Ref = .fromIntern(union_val.val); |
| 13440 | sema: *Sema, | 12954 | return sema.coerce(case_block, capture_ty, uncoerced, operand_src); |
| 13441 | block: *Block, | 12955 | } |
| 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); | | |
| 13448 | | 12956 | |
| 13449 | // Constructing a LazySrcLoc is costly because we only have the switch AST node. | 12957 | try sema.requireRuntimeBlock(case_block, operand_src, null); |
| 13450 | // Only if we know for sure we need to report a compile error do we resolve the | | |
| 13451 | // full source locations. | | |
| 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); | 12978 | // By-val capture with heterogeneous types which are not all in-memory coercible to |
| 13458 | const new_item = if (val.toIntern() != maybe_lazy.toIntern()) blk: { | 12979 | // the resolved capture type. We finally have to fall back to the ugly method. |
| 13459 | break :blk Air.internedToRef(val.toIntern()); | | |
| 13460 | } else item; | | |
| 13461 | | 12980 | |
| 13462 | return .{ .ref = new_item, .val = val.toIntern() }; | 12981 | // However, let's first track which operands are in-memory coercible. There may well |
| 13463 | } | 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( | 12985 | var in_mem_coercible: std.DynamicBitSet = try .initFull(sema.arena, field_indices.len); |
| 13466 | sema: *Sema, | 12986 | in_mem_coercible.unset(first_non_imc); |
| 13467 | block: *Block, | 12987 | { |
| 13468 | seen_errors: *SwitchErrorSet, | 12988 | const next = first_non_imc + 1; |
| 13469 | case_vals: *std.ArrayList(Air.Inst.Ref), | 12989 | for (field_indices[next..], next..) |field_idx, i| { |
| 13470 | operand_ty: Type, | 12990 | const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]); |
| 13471 | inst_data: @FieldType(Zir.Inst.Data, "pl_node"), | 12991 | if (.ok != try sema.coerceInMemoryAllowed(case_block, capture_ty, field_ty, false, zcu.getTarget(), .unneeded, .unneeded, null)) { |
| 13472 | scalar_cases_len: u32, | 12992 | in_mem_coercible.unset(i); |
| 13473 | multi_cases_len: u32, | 12993 | } |
| 13474 | else_case: struct { body: []const Zir.Inst.Index, end: usize, src: LazySrcLoc }, | 12994 | } |
| 13475 | has_else: bool, | 12995 | } |
| 13476 | ) CompileError!?Type { | | |
| 13477 | const gpa = sema.gpa; | | |
| 13478 | const pt = sema.pt; | | |
| 13479 | const zcu = pt.zcu; | | |
| 13480 | const ip = &zcu.intern_pool; | | |
| 13481 | | 12996 | |
| 13482 | const src_node_offset = inst_data.src_node; | 12997 | const capture_block_inst = try case_block.addInstAsIndex(.{ |
| 13483 | const src = block.nodeOffset(src_node_offset); | 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; | 13007 | const prong_count = field_indices.len - in_mem_coercible.count(); |
| 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; | | |
| 13493 | | 13008 | |
| 13494 | case_vals.appendAssumeCapacity(try sema.validateSwitchItemError( | 13009 | const estimated_extra = prong_count * 6 + (prong_count / 10); // 2 for Case, 1 item, probably 3 insts; plus hints |
| 13495 | block, | 13010 | var cases_extra = try std.array_list.Managed(u32).initCapacity(sema.gpa, estimated_extra); |
| 13496 | seen_errors, | 13011 | defer cases_extra.deinit(); |
| 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; | | |
| 13518 | | 13012 | |
| 13519 | try case_vals.ensureUnusedCapacity(gpa, items.len); | 13013 | { |
| 13520 | for (items, 0..) |item_ref, item_i| { | 13014 | // All branch hints are `.none`, so just add zero elems. |
| 13521 | case_vals.appendAssumeCapacity(try sema.validateSwitchItemError( | 13015 | comptime assert(@intFromEnum(std.builtin.BranchHint.none) == 0); |
| 13522 | block, | 13016 | const need_elems = std.math.divCeil(usize, prong_count + 1, 10) catch unreachable; |
| 13523 | seen_errors, | 13017 | try cases_extra.appendNTimes(0, need_elems); |
| 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 | )); | | |
| 13532 | } | 13018 | } |
| 13533 | | 13019 | |
| 13534 | try sema.validateSwitchNoRange(block, ranges_len, operand_ty, src_node_offset); | 13020 | { |
| 13535 | } | 13021 | // Non-bitcast cases |
| 13536 | } | 13022 | var it = in_mem_coercible.iterator(.{ .kind = .unset }); |
| 13537 | | 13023 | while (it.next()) |idx| { |
| 13538 | switch (try sema.resolveInferredErrorSetTy(block, src, operand_ty.toIntern())) { | 13024 | var coerce_block = case_block.makeSubBlock(); |
| 13539 | .anyerror_type => { | 13025 | defer coerce_block.instructions.deinit(sema.gpa); |
| 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); | | |
| 13554 | | 13026 | |
| 13555 | for (error_names.get(ip)) |error_name| { | 13027 | const case_src: LazySrcLoc = .{ |
| 13556 | if (!seen_errors.contains(error_name) and !has_else) { | 13028 | .base_node_inst = capture_src.base_node_inst, |
| 13557 | const msg = maybe_msg orelse blk: { | 13029 | .offset = .{ .switch_case_item = .{ |
| 13558 | maybe_msg = try sema.errMsg( | 13030 | .switch_node_offset = switch_node_offset, |
| 13559 | src, | 13031 | .case_idx = capture_src.offset.switch_capture.case_idx, |
| 13560 | "switch must handle all possibilities", | 13032 | .item_idx = .{ .kind = .single, .value = @intCast(idx) }, |
| 13561 | .{}, | 13033 | } }, |
| 13562 | ); | | |
| 13563 | break :blk maybe_msg.?; | | |
| 13564 | }; | 13034 | }; |
| 13565 | | 13035 | |
| 13566 | try sema.errNote( | 13036 | const field_idx = field_indices[idx]; |
| 13567 | src, | 13037 | const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]); |
| 13568 | msg, | 13038 | const uncoerced = try coerce_block.addStructFieldVal(operand_val, field_idx, field_ty); |
| 13569 | "unhandled error value: 'error.{f}'", | 13039 | const coerced = try sema.coerce(&coerce_block, capture_ty, uncoerced, case_src); |
| 13570 | .{error_name.fmt(ip)}, | 13040 | _ = try coerce_block.addBr(capture_block_inst, coerced); |
| 13571 | ); | 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| { | 13059 | const first_imc_item_idx = in_mem_coercible.findFirstSet().?; |
| 13576 | maybe_msg = null; | 13060 | const first_imc_field_idx = field_indices[first_imc_item_idx]; |
| 13577 | try sema.addDeclaredHereNote(msg, operand_ty); | 13061 | const first_imc_field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[first_imc_field_idx]); |
| 13578 | return sema.failWithOwnedErrorMsg(block, msg); | 13062 | const uncoerced = try coerce_block.addStructFieldVal(operand_val, first_imc_field_idx, first_imc_field_ty); |
| 13579 | } | 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) { | 13070 | try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.SwitchBr).@"struct".fields.len + |
| 13582 | // In order to enable common patterns for generic code allow simple else bodies | 13071 | cases_extra.items.len + |
| 13583 | // else => unreachable, | 13072 | @typeInfo(Air.Block).@"struct".fields.len + |
| 13584 | // else => return, | 13073 | 1); |
| 13585 | // else => |e| return e, | 13074 | |
| 13586 | // even if all the possible errors were already handled. | 13075 | const switch_br_inst: u32 = @intCast(sema.air_instructions.len); |
| 13587 | const tags = sema.code.instructions.items(.tag); | 13076 | try sema.air_instructions.append(sema.gpa, .{ |
| 13588 | const datas = sema.code.instructions.items(.data); | 13077 | .tag = .switch_br, |
| 13589 | for (else_case.body) |else_inst| switch (tags[@intFromEnum(else_inst)]) { | 13078 | .data = .{ |
| 13590 | .dbg_stmt, | 13079 | .pl_op = .{ |
| 13591 | .dbg_var_val, | 13080 | .operand = undefined, // set by switch below |
| 13592 | .ret_type, | 13081 | .payload = sema.addExtraAssumeCapacity(Air.SwitchBr{ |
| 13593 | .as_node, | 13082 | .cases_len = @intCast(prong_count), |
| 13594 | .ret_node, | 13083 | .else_body_len = @intCast(else_body_len), |
| 13595 | .@"unreachable", | 13084 | }), |
| 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, | | |
| 13610 | }, | 13085 | }, |
| 13611 | else => break, | 13086 | }, |
| 13612 | } else break :else_validation; | 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 | return sema.fail( | 13122 | return sema.fail( |
| 13615 | block, | 13123 | case_block, |
| 13616 | else_case.src, | 13124 | capture_src, |
| 13617 | "unreachable else prong; all cases already handled", | 13125 | "error set cannot be captured by reference", |
| 13618 | .{}, | 13126 | .{}, |
| 13619 | ); | 13127 | ); |
| 13620 | } | 13128 | } |
| 13621 | | 13129 | |
| 13622 | var names: InferredErrorSet.NameMap = .{}; | 13130 | if (case_vals.len == 1) { |
| 13623 | try names.ensureUnusedCapacity(sema.arena, error_names.len); | 13131 | const item_val = sema.resolveConstDefinedValue(case_block, .unneeded, case_vals[0], undefined) catch unreachable; |
| 13624 | for (error_names.get(ip)) |error_name| { | 13132 | const item_ty = try pt.singleErrorSetType(item_val.getErrorName(zcu).unwrap().?); |
| 13625 | if (seen_errors.contains(error_name)) continue; | 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( | 13157 | const ResolvedSwitchItem = struct { |
| 13638 | sema: *Sema, | 13158 | ref: Air.Inst.Ref, |
| 13639 | block: *Block, | 13159 | val: Value, |
| 13640 | range_set: *RangeSet, | 13160 | }; |
| 13641 | first_ref: Zir.Inst.Ref, | 13161 | const ResolvedSwitchItemAndExtraIndex = struct { ResolvedSwitchItem, usize }; |
| 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 | } | | |
| 13663 | | 13162 | |
| 13664 | fn validateSwitchItemInt( | 13163 | fn resolveSwitchItem( |
| 13665 | sema: *Sema, | 13164 | sema: *Sema, |
| 13666 | block: *Block, | 13165 | block: *Block, |
| 13667 | range_set: *RangeSet, | | |
| 13668 | item_ref: Zir.Inst.Ref, | | |
| 13669 | operand_ty: Type, | | |
| 13670 | item_src: LazySrcLoc, | 13166 | item_src: LazySrcLoc, |
| 13671 | ) CompileError!Air.Inst.Ref { | 13167 | item_ty: Type, |
| 13672 | const item = try sema.resolveSwitchItemVal(block, item_ref, operand_ty, item_src); | 13168 | item_info: Zir.Inst.SwitchBlock.ItemInfo, |
| 13673 | const maybe_prev_src = try range_set.add(item.val, item.val, item_src); | 13169 | extra_index: usize, |
| 13674 | try sema.validateSwitchDupe(block, maybe_prev_src, item_src); | 13170 | switch_inst: Zir.Inst.Index, |
| 13675 | return item.ref; | 13171 | prong_is_comptime_unreach: bool, |
| 13676 | } | 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( | 13179 | var end = extra_index; |
| 13679 | sema: *Sema, | 13180 | const uncoerced: Air.Inst.Ref, const uncoerced_ty: Type = uncoerced: switch (item_info.unwrap()) { |
| 13680 | block: *Block, | 13181 | .enum_literal => |str_index| { |
| 13681 | seen_fields: []?LazySrcLoc, | 13182 | const zir_str = sema.code.nullTerminatedString(str_index); |
| 13682 | range_set: *RangeSet, | 13183 | const name = try ip.getOrPutString(gpa, pt.tid, zir_str, .no_embedded_nulls); |
| 13683 | item_ref: Zir.Inst.Ref, | 13184 | const uncoerced = try sema.analyzeDeclLiteral(block, item_src, name, item_ty, false); |
| 13684 | operand_ty: Type, | 13185 | break :uncoerced .{ uncoerced, .enum_literal }; |
| 13685 | item_src: LazySrcLoc, | 13186 | }, |
| 13686 | ) CompileError!Air.Inst.Ref { | 13187 | .error_value => |str_index| { |
| 13687 | const ip = &sema.pt.zcu.intern_pool; | 13188 | const zir_str = sema.code.nullTerminatedString(str_index); |
| 13688 | const item = try sema.resolveSwitchItemVal(block, item_ref, operand_ty, item_src); | 13189 | const name = try ip.getOrPutString(gpa, pt.tid, zir_str, .no_embedded_nulls); |
| 13689 | const int = ip.indexToKey(item.val).enum_tag.int; | 13190 | // Make sure there's an error integer value associated with `name`. |
| 13690 | const field_index = ip.loadEnumType(ip.typeOf(item.val)).tagValueIndex(ip, int) orelse { | 13191 | _ = try pt.getErrorValue(name); |
| 13691 | const maybe_prev_src = try range_set.add(int, int, item_src); | 13192 | const err_set_ty = try pt.singleErrorSetType(name); |
| 13692 | try sema.validateSwitchDupe(block, maybe_prev_src, item_src); | 13193 | const uncoerced = Air.internedToRef(try pt.intern(.{ .err = .{ |
| 13693 | return item.ref; | 13194 | .ty = err_set_ty.toIntern(), |
| 13694 | }; | 13195 | .name = name, |
| 13695 | const maybe_prev_src = seen_fields[field_index]; | 13196 | } })); |
| 13696 | seen_fields[field_index] = item_src; | 13197 | break :uncoerced .{ uncoerced, err_set_ty }; |
| 13697 | try sema.validateSwitchDupe(block, maybe_prev_src, item_src); | 13198 | }, |
| 13698 | return item.ref; | 13199 | .number_literal => |zir_ref| { |
| 13699 | } | 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( | 13207 | const uncoerced = ref: { |
| 13702 | sema: *Sema, | 13208 | // The result location of item bodies is `.{ .coerce_ty = switch_inst }`. |
| 13703 | block: *Block, | 13209 | sema.inst_map.putAssumeCapacity(switch_inst, .fromType(item_ty)); |
| 13704 | seen_errors: *SwitchErrorSet, | 13210 | defer assert(sema.inst_map.remove(switch_inst)); |
| 13705 | item_ref: Zir.Inst.Ref, | 13211 | break :ref try sema.resolveInlineBody(block, body, switch_inst); |
| 13706 | operand_ty: Type, | 13212 | }; |
| 13707 | item_src: LazySrcLoc, | 13213 | break :uncoerced .{ uncoerced, sema.typeOf(uncoerced) }; |
| 13708 | ) CompileError!Air.Inst.Ref { | 13214 | }, |
| 13709 | const item = try sema.resolveSwitchItemVal(block, item_ref, operand_ty, item_src); | 13215 | }; |
| 13710 | const error_name = sema.pt.zcu.intern_pool.indexToKey(item.val).err.name; | 13216 | const item_ref: Air.Inst.Ref = item_ref: { |
| 13711 | const maybe_prev_src = if (try seen_errors.fetchPut(error_name, item_src)) |prev| | 13217 | if (item_ty.zigTypeTag(zcu) == .error_set and |
| 13712 | prev.value | 13218 | uncoerced_ty.zigTypeTag(zcu) == .error_set) |
| 13713 | else | 13219 | { |
| 13714 | null; | 13220 | // We allow prongs with errors which are not part of the error set |
| 13715 | try sema.validateSwitchDupe(block, maybe_prev_src, item_src); | 13221 | // being switched on if their prong body is `=> comptime unreachable,`. |
| 13716 | return item.ref; | 13222 | switch (try sema.coerceInMemoryAllowedErrorSets(block, item_ty, uncoerced_ty, item_src, item_src)) { |
| 13717 | } | 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( | 13237 | // We have to resolve lazy values here to avoid false negatives when detecting |
| 13720 | sema: *Sema, | 13238 | // duplicate items and comparing items to a comptime-known switch operand. |
| 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 | } | | |
| 13742 | | 13239 | |
| 13743 | fn validateSwitchItemBool( | 13240 | const val = try sema.resolveLazyValue(maybe_lazy); |
| 13744 | sema: *Sema, | 13241 | const ref: Air.Inst.Ref = if (val.toIntern() == maybe_lazy.toIntern()) |
| 13745 | block: *Block, | 13242 | item_ref |
| 13746 | true_count: *u8, | 13243 | else |
| 13747 | false_count: *u8, | 13244 | .fromValue(val); |
| 13748 | item_ref: Zir.Inst.Ref, | 13245 | return .{ .{ .ref = ref, .val = val }, end }; |
| 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; | | |
| 13761 | } | 13246 | } |
| 13762 | | 13247 | |
| 13763 | const ValueSrcMap = std.AutoHashMapUnmanaged(InternPool.Index, LazySrcLoc); | 13248 | fn validateSwitchItemOrRange( |
| 13764 | | | |
| 13765 | fn validateSwitchItemSparse( | | |
| 13766 | sema: *Sema, | 13249 | sema: *Sema, |
| 13767 | block: *Block, | 13250 | block: *Block, |
| 13768 | seen_values: *ValueSrcMap, | | |
| 13769 | item_ref: Zir.Inst.Ref, | | |
| 13770 | operand_ty: Type, | | |
| 13771 | item_src: LazySrcLoc, | 13251 | item_src: LazySrcLoc, |
| 13772 | ) CompileError!Air.Inst.Ref { | 13252 | /// If `opt_last_val` is not `null`, this refers to the first val of a range. |
| 13773 | const item = try sema.resolveSwitchItemVal(block, item_ref, operand_ty, item_src); | 13253 | item_val: Value, |
| 13774 | const kv = try seen_values.fetchPut(sema.gpa, item.val, item_src) orelse return item.ref; | 13254 | opt_last_val: ?Value, |
| 13775 | try sema.validateSwitchDupe(block, kv.value, item_src); | 13255 | item_ty: Type, |
| 13776 | unreachable; | 13256 | seen_enum_fields: []?LazySrcLoc, |
| 13777 | } | 13257 | seen_errors: *std.AutoHashMapUnmanaged(InternPool.NullTerminatedString, LazySrcLoc), |
| 13778 | | 13258 | seen_sparse_values: *std.AutoHashMapUnmanaged(InternPool.Index, LazySrcLoc), |
| 13779 | fn validateSwitchNoRange( | 13259 | range_set: *RangeSet, |
| 13780 | sema: *Sema, | 13260 | true_src: *?LazySrcLoc, |
| 13781 | block: *Block, | 13261 | false_src: *?LazySrcLoc, |
| 13782 | ranges_len: u32, | 13262 | void_src: *?LazySrcLoc, |
| 13783 | operand_ty: Type, | | |
| 13784 | src_node_offset: std.zig.Ast.Node.Offset, | | |
| 13785 | ) CompileError!void { | 13263 | ) CompileError!void { |
| 13786 | if (ranges_len == 0) | 13264 | const pt = sema.pt; |
| 13787 | return; | 13265 | const zcu = pt.zcu; |
| 13788 | | 13266 | const ip = &zcu.intern_pool; |
| 13789 | const operand_src = block.src(.{ .node_offset_switch_operand = src_node_offset }); | 13267 | const maybe_prev_src: ?LazySrcLoc = maybe_prev_src: switch (item_ty.zigTypeTag(zcu)) { |
| 13790 | const range_src = block.src(.{ .node_offset_switch_range = src_node_offset }); | 13268 | .@"union" => unreachable, |
| 13791 | | 13269 | .@"enum" => { |
| 13792 | const msg = msg: { | 13270 | const int = ip.indexToKey(item_val.toIntern()).enum_tag.int; |
| 13793 | const msg = try sema.errMsg( | 13271 | if (ip.loadEnumType(item_ty.toIntern()).tagValueIndex(ip, int)) |field_index| { |
| 13794 | operand_src, | 13272 | const maybe_prev_src = seen_enum_fields[field_index]; |
| 13795 | "ranges not allowed when switching on type '{f}'", | 13273 | seen_enum_fields[field_index] = item_src; |
| 13796 | .{operand_ty.fmt(sema.pt)}, | 13274 | break :maybe_prev_src maybe_prev_src; |
| 13797 | ); | 13275 | } else { |
| 13798 | errdefer msg.destroy(sema.gpa); | 13276 | break :maybe_prev_src try range_set.add(sema.arena, .{ |
| 13799 | try sema.errNote( | 13277 | .first = .fromInterned(int), |
| 13800 | range_src, | 13278 | .last = .fromInterned(int), |
| 13801 | msg, | 13279 | .src = item_src, |
| 13802 | "range here", | 13280 | }, .fromInterned(ip.typeOf(int)), zcu); |
| 13803 | .{}, | 13281 | } |
| 13804 | ); | 13282 | }, |
| 13805 | break :msg msg; | 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 | fn maybeErrorUnwrap( | 13351 | fn maybeErrorUnwrap( |