| ... | ... | @@ -2415,6 +2415,32 @@ fn failWithModRemNegative(sema: *Sema, block: *Block, src: LazySrcLoc, lhs_ty: T |
| 2415 | 2415 | }); |
| 2416 | 2416 | } |
| 2417 | 2417 | |
| 2418 | fn failWithInvalidSwitchTagCapture(sema: *Sema, block: *Block, tag_capture_src: LazySrcLoc, operand_ty: Type) CompileError { |
| 2419 | const pt = sema.pt; |
| 2420 | const zcu = pt.zcu; |
| 2421 | |
| 2422 | if (operand_ty.zigTypeTag(zcu) == .@"union") { |
| 2423 | assert(operand_ty.containerLayout(zcu) == .@"packed"); |
| 2424 | return sema.failWithOwnedErrorMsg(block, msg: { |
| 2425 | const msg = try sema.errMsg(tag_capture_src, "cannot capture tag of packed union", .{}); |
| 2426 | errdefer msg.destroy(sema.gpa); |
| 2427 | try sema.addDeclaredHereNote(msg, operand_ty); |
| 2428 | if (operand_ty.srcLocOrNull(zcu)) |ty_src| { |
| 2429 | try sema.errNote(ty_src, msg, "consider using a tagged union", .{}); |
| 2430 | } |
| 2431 | break :msg msg; |
| 2432 | }); |
| 2433 | } |
| 2434 | return sema.failWithOwnedErrorMsg(block, msg: { |
| 2435 | const msg = try sema.errMsg(tag_capture_src, "cannot capture tag of non-union type '{f}'", .{ |
| 2436 | operand_ty.fmt(pt), |
| 2437 | }); |
| 2438 | errdefer msg.destroy(sema.gpa); |
| 2439 | try sema.addDeclaredHereNote(msg, operand_ty); |
| 2440 | break :msg msg; |
| 2441 | }); |
| 2442 | } |
| 2443 | |
| 2418 | 2444 | fn failWithExpectedOptionalType(sema: *Sema, block: *Block, src: LazySrcLoc, non_optional_ty: Type) CompileError { |
| 2419 | 2445 | const pt = sema.pt; |
| 2420 | 2446 | const msg = msg: { |
| ... | ... | @@ -10022,7 +10048,7 @@ fn analyzeSwitchBlock( |
| 10022 | 10048 | |
| 10023 | 10049 | const case_vals = validated_switch.case_vals; |
| 10024 | 10050 | |
| 10025 | | const index, const body, const capture, const has_tag_capture, const is_inline, const is_special = find_prong: { |
| 10051 | const case_idx, const body, const capture, const has_tag_capture = find_prong: { |
| 10026 | 10052 | var case_val_idx: usize = 0; |
| 10027 | 10053 | var case_it = zir_switch.iterateCases(); |
| 10028 | 10054 | var extra_index = zir_switch.end; |
| ... | ... | @@ -10045,12 +10071,12 @@ fn analyzeSwitchBlock( |
| 10045 | 10071 | } |
| 10046 | 10072 | continue; |
| 10047 | 10073 | } |
| 10048 | | break :find_prong .{ case.index, prong_body, prong_info.capture, prong_info.has_tag_capture, prong_info.is_inline, false }; |
| 10074 | break :find_prong .{ case.index, prong_body, prong_info.capture, prong_info.has_tag_capture }; |
| 10049 | 10075 | } |
| 10050 | 10076 | if (has_else) { |
| 10051 | 10077 | // This *has* to be checked after iterating all regular cases because |
| 10052 | 10078 | // we allow simple noreturn else prongs when switching on error sets! |
| 10053 | | break :find_prong .{ else_case.index, else_case.body, else_case.capture, else_case.has_tag_capture, else_case.is_inline, true }; |
| 10079 | break :find_prong .{ else_case.index, else_case.body, else_case.capture, else_case.has_tag_capture }; |
| 10054 | 10080 | } |
| 10055 | 10081 | unreachable; // malformed validated switch |
| 10056 | 10082 | }; |
| ... | ... | @@ -10061,58 +10087,33 @@ fn analyzeSwitchBlock( |
| 10061 | 10087 | if (!(err_set and |
| 10062 | 10088 | try sema.maybeErrorUnwrap(&case_block, body, cond_ref, operand_src, true))) |
| 10063 | 10089 | { |
| 10064 | | // Set up captures manually to avoid special cases in the main logic. |
| 10065 | | const payload_inst: Zir.Inst.Index = if (capture != .none) inst: { |
| 10090 | const payload_inst = if (capture != .none) inst: { |
| 10066 | 10091 | const payload_inst = zir_switch.payload_capture_placeholder.unwrap() orelse switch_inst; |
| 10067 | 10092 | const payload_ref: Air.Inst.Ref = payload_ref: { |
| 10068 | | const item_val: Value = item_val: { |
| 10093 | const captured_opv: Value = captured_opv: { |
| 10069 | 10094 | if (!tagged_union_originally) { |
| 10070 | | break :item_val item_opv; |
| 10095 | break :captured_opv item_opv; |
| 10071 | 10096 | } |
| 10072 | 10097 | if (maybe_operand_opv) |operand_opv| { |
| 10073 | | break :item_val .fromInterned(zcu.intern_pool.indexToKey(operand_opv.toIntern()).un.val); |
| 10098 | break :captured_opv .fromInterned(zcu.intern_pool.indexToKey(operand_opv.toIntern()).un.val); |
| 10074 | 10099 | } |
| 10075 | 10100 | assert(zir_switch.any_maybe_runtime_capture); // there's a payload capture |
| 10076 | | const operand_val, const operand_ref = switch (operand) { |
| 10077 | | .simple => unreachable, |
| 10078 | | .loop => |l| load_operand: { |
| 10079 | | const loaded = try sema.analyzeLoad(block, src, l.operand_alloc, src); |
| 10080 | | if (l.operand_is_ref) { |
| 10081 | | const by_val = try sema.analyzeLoad(block, src, loaded, src); |
| 10082 | | break :load_operand .{ by_val, loaded }; |
| 10083 | | } else { |
| 10084 | | break :load_operand .{ loaded, .none }; |
| 10085 | | } |
| 10086 | | }, |
| 10087 | | }; |
| 10088 | | const prong_kind: SwitchProngKind = kind: { |
| 10089 | | if (is_inline) break :kind .{ .inline_ref = .fromValue(item_opv) }; |
| 10090 | | if (is_special) break :kind .special; |
| 10091 | | break :kind .{ .item_refs = &.{.fromValue(item_opv)} }; |
| 10092 | | }; |
| 10093 | | break :payload_ref try sema.analyzeSwitchPayloadCapture( |
| 10101 | const loaded_operand = try sema.analyzeSwitchOperandLoad(&case_block, operand, operand_src, capture == .by_ref); |
| 10102 | break :payload_ref try sema.resolveSwitchPayloadCaptureTaggedUnion( |
| 10094 | 10103 | &case_block, |
| 10095 | | operand, |
| 10096 | | operand_val, |
| 10097 | | operand_ref, |
| 10098 | | operand_ty, |
| 10104 | loaded_operand, |
| 10099 | 10105 | operand_src, |
| 10100 | | block.src(.{ .switch_capture = .{ |
| 10101 | | .switch_node_offset = src_node_offset, |
| 10102 | | .case_idx = index, |
| 10103 | | } }), |
| 10106 | operand_ty, |
| 10107 | item_opv, |
| 10104 | 10108 | capture == .by_ref, |
| 10105 | | prong_kind, |
| 10106 | | validated_switch.else_err_ty, |
| 10107 | 10109 | ); |
| 10108 | 10110 | }; |
| 10109 | 10111 | break :payload_ref switch (capture) { |
| 10110 | | .by_val => .fromValue(item_val), |
| 10111 | | .by_ref => try sema.uavRef(item_val), |
| 10112 | .by_val => .fromValue(captured_opv), |
| 10113 | .by_ref => try sema.uavRef(captured_opv), |
| 10112 | 10114 | .none => unreachable, |
| 10113 | 10115 | }; |
| 10114 | 10116 | }; |
| 10115 | | assert(!sema.typeOf(payload_ref).isNoReturn(sema.pt.zcu)); |
| 10116 | 10117 | sema.inst_map.putAssumeCapacity(payload_inst, payload_ref); |
| 10117 | 10118 | break :inst payload_inst; |
| 10118 | 10119 | } else undefined; |
| ... | ... | @@ -10120,6 +10121,13 @@ fn analyzeSwitchBlock( |
| 10120 | 10121 | |
| 10121 | 10122 | const tag_inst: Zir.Inst.Index = if (has_tag_capture) inst: { |
| 10122 | 10123 | const tag_inst = zir_switch.tag_capture_placeholder.unwrap() orelse switch_inst; |
| 10124 | if (!tagged_union_originally) { |
| 10125 | const tag_capture_src = block.src(.{ .switch_tag_capture = .{ |
| 10126 | .switch_node_offset = src_node_offset, |
| 10127 | .case_idx = case_idx, |
| 10128 | } }); |
| 10129 | return sema.failWithInvalidSwitchTagCapture(block, tag_capture_src, operand_ty); |
| 10130 | } |
| 10123 | 10131 | sema.inst_map.putAssumeCapacity(tag_inst, .fromValue(item_opv)); |
| 10124 | 10132 | break :inst tag_inst; |
| 10125 | 10133 | } else undefined; |
| ... | ... | @@ -10405,7 +10413,7 @@ fn finishSwitchBr( |
| 10405 | 10413 | } }), |
| 10406 | 10414 | prong_info.capture, |
| 10407 | 10415 | prong_info.has_tag_capture, |
| 10408 | | .{ .inline_ref = item_ref }, |
| 10416 | .{ .@"inline" = item_ref }, |
| 10409 | 10417 | validated_switch.else_err_ty, |
| 10410 | 10418 | switch_inst, |
| 10411 | 10419 | zir_switch, |
| ... | ... | @@ -10495,7 +10503,7 @@ fn finishSwitchBr( |
| 10495 | 10503 | } }), |
| 10496 | 10504 | prong_info.capture, |
| 10497 | 10505 | prong_info.has_tag_capture, |
| 10498 | | .{ .inline_ref = item_ref }, |
| 10506 | .{ .@"inline" = item_ref }, |
| 10499 | 10507 | validated_switch.else_err_ty, |
| 10500 | 10508 | switch_inst, |
| 10501 | 10509 | zir_switch, |
| ... | ... | @@ -10635,7 +10643,7 @@ fn finishSwitchBr( |
| 10635 | 10643 | } }), |
| 10636 | 10644 | else_case.capture, |
| 10637 | 10645 | else_case.has_tag_capture, |
| 10638 | | .{ .inline_ref = item_ref }, |
| 10646 | .{ .@"inline" = item_ref }, |
| 10639 | 10647 | validated_switch.else_err_ty, |
| 10640 | 10648 | switch_inst, |
| 10641 | 10649 | zir_switch, |
| ... | ... | @@ -11075,8 +11083,7 @@ fn validateSwitchBlock( |
| 11075 | 11083 | const has_else = zir_switch.else_case != null; |
| 11076 | 11084 | const has_under = zir_switch.has_under; |
| 11077 | 11085 | |
| 11078 | | var case_vals: std.ArrayList(Air.Inst.Ref) = .empty; |
| 11079 | | try case_vals.ensureUnusedCapacity(arena, zir_switch.item_infos.len); |
| 11086 | var case_vals: std.ArrayList(Air.Inst.Ref) = try .initCapacity(arena, zir_switch.item_infos.len); |
| 11080 | 11087 | |
| 11081 | 11088 | // Duplicate checking variables later also used for `inline else`. |
| 11082 | 11089 | var seen_enum_fields: []?LazySrcLoc = &.{}; |
| ... | ... | @@ -11482,10 +11489,10 @@ fn resolveSwitchBlock( |
| 11482 | 11489 | // This prong should be unreachable! |
| 11483 | 11490 | return .unreachable_value; |
| 11484 | 11491 | } |
| 11485 | | const prong_kind: SwitchProngKind = kind: { |
| 11486 | | if (prong_info.is_inline) break :kind .{ .inline_ref = cond_ref }; |
| 11487 | | if (range_refs.len > 0) break :kind .has_ranges; |
| 11488 | | break :kind .{ .item_refs = item_refs }; |
| 11492 | const prong_items: SwitchProngItems = prong_items: { |
| 11493 | if (prong_info.is_inline) break :prong_items .{ .@"inline" = cond_ref }; |
| 11494 | if (range_refs.len > 0) break :prong_items .has_ranges; |
| 11495 | break :prong_items .{ .item_refs = item_refs }; |
| 11489 | 11496 | }; |
| 11490 | 11497 | return sema.resolveSwitchProng( |
| 11491 | 11498 | block, |
| ... | ... | @@ -11499,7 +11506,7 @@ fn resolveSwitchBlock( |
| 11499 | 11506 | } }), |
| 11500 | 11507 | prong_info.capture, |
| 11501 | 11508 | prong_info.has_tag_capture, |
| 11502 | | prong_kind, |
| 11509 | prong_items, |
| 11503 | 11510 | validated_switch.else_err_ty, |
| 11504 | 11511 | merges, |
| 11505 | 11512 | switch_inst, |
| ... | ... | @@ -11513,8 +11520,8 @@ fn resolveSwitchBlock( |
| 11513 | 11520 | if ((try sema.compareAll(cond_val, .gte, first_val, item_ty)) and |
| 11514 | 11521 | (try sema.compareAll(cond_val, .lte, last_val, item_ty))) |
| 11515 | 11522 | { |
| 11516 | | const prong_kind: SwitchProngKind = if (prong_info.is_inline) |
| 11517 | | .{ .inline_ref = cond_ref } |
| 11523 | const prong_items: SwitchProngItems = if (prong_info.is_inline) |
| 11524 | .{ .@"inline" = cond_ref } |
| 11518 | 11525 | else |
| 11519 | 11526 | .has_ranges; |
| 11520 | 11527 | return sema.resolveSwitchProng( |
| ... | ... | @@ -11529,7 +11536,7 @@ fn resolveSwitchBlock( |
| 11529 | 11536 | } }), |
| 11530 | 11537 | prong_info.capture, |
| 11531 | 11538 | prong_info.has_tag_capture, |
| 11532 | | prong_kind, |
| 11539 | prong_items, |
| 11533 | 11540 | validated_switch.else_err_ty, |
| 11534 | 11541 | merges, |
| 11535 | 11542 | switch_inst, |
| ... | ... | @@ -11548,8 +11555,8 @@ fn resolveSwitchBlock( |
| 11548 | 11555 | |
| 11549 | 11556 | if (else_is_named_only and item_ty.enumTagFieldIndex(cond_val, zcu) != null) { |
| 11550 | 11557 | assert(item_ty.isNonexhaustiveEnum(zcu)); |
| 11551 | | const prong_kind: SwitchProngKind = if (else_case.is_inline) |
| 11552 | | .{ .inline_ref = cond_ref } |
| 11558 | const prong_items: SwitchProngItems = if (else_case.is_inline) |
| 11559 | .{ .@"inline" = cond_ref } |
| 11553 | 11560 | else |
| 11554 | 11561 | .special; |
| 11555 | 11562 | return sema.resolveSwitchProng( |
| ... | ... | @@ -11564,7 +11571,7 @@ fn resolveSwitchBlock( |
| 11564 | 11571 | } }), |
| 11565 | 11572 | else_case.capture, |
| 11566 | 11573 | else_case.has_tag_capture, |
| 11567 | | prong_kind, |
| 11574 | prong_items, |
| 11568 | 11575 | validated_switch.else_err_ty, |
| 11569 | 11576 | merges, |
| 11570 | 11577 | switch_inst, |
| ... | ... | @@ -11588,8 +11595,8 @@ fn resolveSwitchBlock( |
| 11588 | 11595 | return .unreachable_value; |
| 11589 | 11596 | } |
| 11590 | 11597 | } |
| 11591 | | const prong_kind: SwitchProngKind = if (is_inline) |
| 11592 | | .{ .inline_ref = cond_ref } |
| 11598 | const prong_items: SwitchProngItems = if (is_inline) |
| 11599 | .{ .@"inline" = cond_ref } |
| 11593 | 11600 | else |
| 11594 | 11601 | .special; |
| 11595 | 11602 | return sema.resolveSwitchProng( |
| ... | ... | @@ -11604,7 +11611,7 @@ fn resolveSwitchBlock( |
| 11604 | 11611 | } }), |
| 11605 | 11612 | capture, |
| 11606 | 11613 | has_tag_capture, |
| 11607 | | prong_kind, |
| 11614 | prong_items, |
| 11608 | 11615 | validated_switch.else_err_ty, |
| 11609 | 11616 | merges, |
| 11610 | 11617 | switch_inst, |
| ... | ... | @@ -11640,14 +11647,59 @@ const SwitchOperand = union(enum) { |
| 11640 | 11647 | }, |
| 11641 | 11648 | }; |
| 11642 | 11649 | |
| 11643 | | const SwitchProngKind = union(enum) { |
| 11644 | | /// Prefer populating this field over the others, if possible. |
| 11645 | | inline_ref: Air.Inst.Ref, |
| 11650 | fn analyzeSwitchOperandLoad( |
| 11651 | sema: *Sema, |
| 11652 | block: *Block, |
| 11653 | operand: SwitchOperand, |
| 11654 | operand_src: LazySrcLoc, |
| 11655 | by_ref: bool, |
| 11656 | ) CompileError!Air.Inst.Ref { |
| 11657 | switch (operand) { |
| 11658 | .simple => |s| { |
| 11659 | if (by_ref) { |
| 11660 | assert(s.by_ref != .none); |
| 11661 | return s.by_ref; |
| 11662 | } else { |
| 11663 | return s.by_val; |
| 11664 | } |
| 11665 | }, |
| 11666 | .loop => |l| { |
| 11667 | const loaded = try sema.analyzeLoad(block, operand_src, l.operand_alloc, operand_src); |
| 11668 | assert(loaded != .none); // there are no captures, so no need to load the switch operand |
| 11669 | if (by_ref) { |
| 11670 | assert(l.operand_is_ref); |
| 11671 | return loaded; |
| 11672 | } |
| 11673 | return if (l.operand_is_ref) |
| 11674 | try sema.analyzeLoad(block, operand_src, loaded, operand_src) |
| 11675 | else |
| 11676 | loaded; |
| 11677 | }, |
| 11678 | } |
| 11679 | } |
| 11680 | |
| 11681 | const SwitchProngItems = union(enum) { |
| 11682 | @"inline": Air.Inst.Ref, |
| 11646 | 11683 | item_refs: []const Air.Inst.Ref, |
| 11647 | 11684 | has_ranges, |
| 11648 | 11685 | special, |
| 11649 | 11686 | }; |
| 11650 | 11687 | |
| 11688 | /// A switch capture is comptime-known if it is `inline` and/or it is a by-value |
| 11689 | /// capture of a prong with a single item. |
| 11690 | fn resolveSwitchCaptureFromProngItems( |
| 11691 | sema: *Sema, |
| 11692 | prong_items: SwitchProngItems, |
| 11693 | by_ref: bool, |
| 11694 | ) ?Value { |
| 11695 | const ref: Air.Inst.Ref = switch (prong_items) { |
| 11696 | .@"inline" => |ref| ref, |
| 11697 | .item_refs => |refs| if (refs.len == 1 and !by_ref) refs[0] else return null, |
| 11698 | .has_ranges, .special => return null, |
| 11699 | }; |
| 11700 | return sema.resolveValue(ref).?; |
| 11701 | } |
| 11702 | |
| 11651 | 11703 | /// Resolve a switch prong which is determined at comptime to have no peers. |
| 11652 | 11704 | /// Sets up captures as needed. Uses `analyzeBodyRuntimeBreak`. |
| 11653 | 11705 | fn resolveSwitchProng( |
| ... | ... | @@ -11661,7 +11713,7 @@ fn resolveSwitchProng( |
| 11661 | 11713 | capture_src: LazySrcLoc, |
| 11662 | 11714 | capture: Zir.Inst.SwitchBlock.ProngInfo.Capture, |
| 11663 | 11715 | has_tag_capture: bool, |
| 11664 | | kind: SwitchProngKind, |
| 11716 | prong_items: SwitchProngItems, |
| 11665 | 11717 | else_err_ty: ?Type, |
| 11666 | 11718 | merges: *Block.Merges, |
| 11667 | 11719 | switch_inst: Zir.Inst.Index, |
| ... | ... | @@ -11676,36 +11728,28 @@ fn resolveSwitchProng( |
| 11676 | 11728 | const parent_hint = sema.branch_hint; |
| 11677 | 11729 | defer sema.branch_hint = parent_hint orelse if (sema.branch_hint == .cold) .cold else null; |
| 11678 | 11730 | |
| 11679 | | const payload_inst: Zir.Inst.Index = if (capture != .none) inst: { |
| 11731 | const analyzed_captures = try sema.analyzeSwitchCaptures( |
| 11732 | child_block, |
| 11733 | operand, |
| 11734 | operand_src, |
| 11735 | sema.typeOf(operand.simple.by_val), |
| 11736 | capture_src, |
| 11737 | capture, |
| 11738 | has_tag_capture, |
| 11739 | prong_items, |
| 11740 | else_err_ty, |
| 11741 | ); |
| 11742 | |
| 11743 | const payload_inst = if (capture != .none) inst: { |
| 11680 | 11744 | const payload_inst = zir_switch.payload_capture_placeholder.unwrap() orelse switch_inst; |
| 11681 | | const payload_ref = try sema.analyzeSwitchPayloadCapture( |
| 11682 | | child_block, |
| 11683 | | operand, |
| 11684 | | operand.simple.by_val, |
| 11685 | | operand.simple.by_ref, |
| 11686 | | sema.typeOf(operand.simple.by_val), |
| 11687 | | operand_src, |
| 11688 | | capture_src, |
| 11689 | | capture == .by_ref, |
| 11690 | | kind, |
| 11691 | | else_err_ty, |
| 11692 | | ); |
| 11693 | | assert(!sema.typeOf(payload_ref).isNoReturn(sema.pt.zcu)); |
| 11694 | | sema.inst_map.putAssumeCapacity(payload_inst, payload_ref); |
| 11745 | sema.inst_map.putAssumeCapacity(payload_inst, analyzed_captures.payload_ref); |
| 11695 | 11746 | break :inst payload_inst; |
| 11696 | 11747 | } else undefined; |
| 11697 | 11748 | defer if (capture != .none) assert(sema.inst_map.remove(payload_inst)); |
| 11698 | 11749 | |
| 11699 | 11750 | const tag_inst: Zir.Inst.Index = if (has_tag_capture) inst: { |
| 11700 | 11751 | const tag_inst = zir_switch.tag_capture_placeholder.unwrap() orelse switch_inst; |
| 11701 | | const tag_ref = try sema.analyzeSwitchTagCapture( |
| 11702 | | child_block, |
| 11703 | | operand.simple.by_val, |
| 11704 | | sema.typeOf(operand.simple.by_val), |
| 11705 | | capture_src, |
| 11706 | | kind, |
| 11707 | | ); |
| 11708 | | sema.inst_map.putAssumeCapacity(tag_inst, tag_ref); |
| 11752 | sema.inst_map.putAssumeCapacity(tag_inst, analyzed_captures.tag_ref); |
| 11709 | 11753 | break :inst tag_inst; |
| 11710 | 11754 | } else undefined; |
| 11711 | 11755 | defer if (has_tag_capture) assert(sema.inst_map.remove(tag_inst)); |
| ... | ... | @@ -11751,7 +11795,7 @@ fn analyzeSwitchProng( |
| 11751 | 11795 | capture_src: LazySrcLoc, |
| 11752 | 11796 | capture: Zir.Inst.SwitchBlock.ProngInfo.Capture, |
| 11753 | 11797 | has_tag_capture: bool, |
| 11754 | | kind: SwitchProngKind, |
| 11798 | prong_items: SwitchProngItems, |
| 11755 | 11799 | else_err_ty: ?Type, |
| 11756 | 11800 | switch_inst: Zir.Inst.Index, |
| 11757 | 11801 | zir_switch: *const Zir.UnwrappedSwitchBlock, |
| ... | ... | @@ -11772,77 +11816,28 @@ fn analyzeSwitchProng( |
| 11772 | 11816 | } |
| 11773 | 11817 | } |
| 11774 | 11818 | |
| 11775 | | const need_load: bool = need_load: { |
| 11776 | | if (capture == .none and !has_tag_capture) { |
| 11777 | | // No need to load the operand for this prong! |
| 11778 | | break :need_load false; |
| 11779 | | } |
| 11780 | | if (capture != .none and operand_ty.zigTypeTag(zcu) == .@"union" and |
| 11781 | | operand_ty.containerLayout(zcu) != .@"packed") |
| 11782 | | { |
| 11783 | | // Non-OPV tagged union payload captures are always runtime-known. |
| 11784 | | break :need_load true; |
| 11785 | | } |
| 11786 | | if (kind == .inline_ref) { |
| 11787 | | // `inline_ref` *is* the (comptime-known) capture. |
| 11788 | | break :need_load false; |
| 11789 | | } |
| 11790 | | assert(zir_switch.any_maybe_runtime_capture); // should have caught everything else by now |
| 11791 | | if (capture != .by_ref and |
| 11792 | | kind == .item_refs and kind.item_refs.len == 1) |
| 11793 | | { |
| 11794 | | // Capture is comptime-known because it's the only prong item |
| 11795 | | break :need_load false; |
| 11796 | | } |
| 11797 | | break :need_load true; |
| 11798 | | }; |
| 11799 | | |
| 11800 | | const operand_val: Air.Inst.Ref, const operand_ptr: Air.Inst.Ref = load_operand: { |
| 11801 | | if (!need_load) break :load_operand .{ .none, .none }; |
| 11802 | | switch (operand) { |
| 11803 | | .simple => |s| break :load_operand .{ s.by_val, s.by_ref }, |
| 11804 | | .loop => |l| { |
| 11805 | | const loaded = try sema.analyzeLoad(case_block, operand_src, l.operand_alloc, operand_src); |
| 11806 | | if (l.operand_is_ref) { |
| 11807 | | const by_val = try sema.analyzeLoad(case_block, operand_src, loaded, operand_src); |
| 11808 | | break :load_operand .{ by_val, loaded }; |
| 11809 | | } else { |
| 11810 | | break :load_operand .{ loaded, .none }; |
| 11811 | | } |
| 11812 | | }, |
| 11813 | | } |
| 11814 | | }; |
| 11819 | const analyzed_captures = try sema.analyzeSwitchCaptures( |
| 11820 | case_block, |
| 11821 | operand, |
| 11822 | operand_src, |
| 11823 | operand_ty, |
| 11824 | capture_src, |
| 11825 | capture, |
| 11826 | has_tag_capture, |
| 11827 | prong_items, |
| 11828 | else_err_ty, |
| 11829 | ); |
| 11815 | 11830 | |
| 11816 | | const payload_inst: Zir.Inst.Index = if (capture != .none) inst: { |
| 11831 | const payload_inst = if (capture != .none) inst: { |
| 11817 | 11832 | const payload_inst = zir_switch.payload_capture_placeholder.unwrap() orelse switch_inst; |
| 11818 | | const payload_ref = try sema.analyzeSwitchPayloadCapture( |
| 11819 | | case_block, |
| 11820 | | operand, |
| 11821 | | operand_val, |
| 11822 | | operand_ptr, |
| 11823 | | operand_ty, |
| 11824 | | operand_src, |
| 11825 | | capture_src, |
| 11826 | | capture == .by_ref, |
| 11827 | | kind, |
| 11828 | | else_err_ty, |
| 11829 | | ); |
| 11830 | | assert(!sema.typeOf(payload_ref).isNoReturn(sema.pt.zcu)); |
| 11831 | | sema.inst_map.putAssumeCapacity(payload_inst, payload_ref); |
| 11833 | sema.inst_map.putAssumeCapacity(payload_inst, analyzed_captures.payload_ref); |
| 11832 | 11834 | break :inst payload_inst; |
| 11833 | 11835 | } else undefined; |
| 11834 | 11836 | defer if (capture != .none) assert(sema.inst_map.remove(payload_inst)); |
| 11835 | 11837 | |
| 11836 | 11838 | const tag_inst: Zir.Inst.Index = if (has_tag_capture) inst: { |
| 11837 | 11839 | const tag_inst = zir_switch.tag_capture_placeholder.unwrap() orelse switch_inst; |
| 11838 | | const tag_ref = try sema.analyzeSwitchTagCapture( |
| 11839 | | case_block, |
| 11840 | | operand_val, |
| 11841 | | operand_ty, |
| 11842 | | capture_src, |
| 11843 | | kind, |
| 11844 | | ); |
| 11845 | | sema.inst_map.putAssumeCapacity(tag_inst, tag_ref); |
| 11840 | sema.inst_map.putAssumeCapacity(tag_inst, analyzed_captures.tag_ref); |
| 11846 | 11841 | break :inst tag_inst; |
| 11847 | 11842 | } else undefined; |
| 11848 | 11843 | defer if (has_tag_capture) assert(sema.inst_map.remove(tag_inst)); |
| ... | ... | @@ -11853,157 +11848,295 @@ fn analyzeSwitchProng( |
| 11853 | 11848 | return sema.analyzeBodyRuntimeBreak(case_block, prong_body); |
| 11854 | 11849 | } |
| 11855 | 11850 | |
| 11856 | | fn analyzeSwitchTagCapture( |
| 11851 | fn analyzeSwitchCaptures( |
| 11857 | 11852 | sema: *Sema, |
| 11858 | 11853 | case_block: *Block, |
| 11859 | | /// May be `none` if this is an inline capture or if `kind.item_refs.len == 1`. |
| 11860 | | operand_val: Air.Inst.Ref, |
| 11854 | operand: SwitchOperand, |
| 11855 | operand_src: LazySrcLoc, |
| 11861 | 11856 | operand_ty: Type, |
| 11862 | 11857 | capture_src: LazySrcLoc, |
| 11863 | | kind: SwitchProngKind, |
| 11864 | | ) CompileError!Air.Inst.Ref { |
| 11858 | capture: Zir.Inst.SwitchBlock.ProngInfo.Capture, |
| 11859 | has_tag_capture: bool, |
| 11860 | prong_items: SwitchProngItems, |
| 11861 | else_err_ty: ?Type, |
| 11862 | ) CompileError!struct { |
| 11863 | payload_ref: Air.Inst.Ref, |
| 11864 | tag_ref: Air.Inst.Ref, |
| 11865 | } { |
| 11865 | 11866 | const pt = sema.pt; |
| 11866 | 11867 | const zcu = pt.zcu; |
| 11867 | 11868 | |
| 11868 | | const tag_capture_src: LazySrcLoc = .{ |
| 11869 | | .base_node_inst = capture_src.base_node_inst, |
| 11870 | | .offset = .{ .switch_tag_capture = capture_src.offset.switch_capture }, |
| 11871 | | }; |
| 11869 | if (operand_ty.zigTypeTag(zcu) == .@"union" and |
| 11870 | operand_ty.containerLayout(zcu) != .@"packed") |
| 11871 | { |
| 11872 | if (capture == .none) { |
| 11873 | const tag_ref: Air.Inst.Ref = tag_ref: { |
| 11874 | if (!has_tag_capture) break :tag_ref .none; |
| 11875 | if (sema.resolveSwitchCaptureFromProngItems(prong_items, false)) |tag_val| { |
| 11876 | break :tag_ref .fromValue(tag_val); |
| 11877 | } |
| 11878 | const loaded_operand = try sema.analyzeSwitchOperandLoad(case_block, operand, operand_src, false); |
| 11879 | break :tag_ref try sema.unionToTag(case_block, loaded_operand); |
| 11880 | }; |
| 11881 | return .{ .payload_ref = .none, .tag_ref = tag_ref }; |
| 11882 | } |
| 11872 | 11883 | |
| 11873 | | if (operand_ty.zigTypeTag(zcu) != .@"union") { |
| 11874 | | return sema.fail(case_block, tag_capture_src, "cannot capture tag of non-union type '{f}'", .{ |
| 11875 | | operand_ty.fmt(pt), |
| 11876 | | }); |
| 11877 | | } |
| 11878 | | if (operand_ty.containerLayout(zcu) == .@"packed") { |
| 11879 | | return sema.fail(case_block, tag_capture_src, "cannot capture tag of packed union", .{}); |
| 11884 | // We always have to load the operand for tagged union payload captures |
| 11885 | // since we can't derive the payload value from the tag (except for OPV |
| 11886 | // types, for which the load is always basically a noop anyway). |
| 11887 | |
| 11888 | const loaded_operand = try sema.analyzeSwitchOperandLoad(case_block, operand, operand_src, capture == .by_ref); |
| 11889 | |
| 11890 | if (sema.resolveSwitchCaptureFromProngItems(prong_items, capture == .by_ref)) |tag_val| { |
| 11891 | const payload_ref = try sema.resolveSwitchPayloadCaptureTaggedUnion( |
| 11892 | case_block, |
| 11893 | loaded_operand, |
| 11894 | operand_src, |
| 11895 | operand_ty, |
| 11896 | tag_val, |
| 11897 | capture == .by_ref, |
| 11898 | ); |
| 11899 | const tag_ref: Air.Inst.Ref = if (has_tag_capture) .fromValue(tag_val) else .none; |
| 11900 | return .{ .payload_ref = payload_ref, .tag_ref = tag_ref }; |
| 11901 | } |
| 11902 | |
| 11903 | const payload_ref = try sema.analyzeSwitchPayloadCaptureTaggedUnion( |
| 11904 | case_block, |
| 11905 | operand, |
| 11906 | loaded_operand, |
| 11907 | operand_src, |
| 11908 | operand_ty, |
| 11909 | capture == .by_ref, |
| 11910 | capture_src, |
| 11911 | prong_items, |
| 11912 | ); |
| 11913 | |
| 11914 | const tag_ref: Air.Inst.Ref = tag_ref: { |
| 11915 | if (!has_tag_capture) break :tag_ref .none; |
| 11916 | const operand_val = switch (capture) { |
| 11917 | .none => unreachable, // handled above |
| 11918 | .by_val => loaded_operand, |
| 11919 | .by_ref => try sema.analyzeLoad(case_block, operand_src, loaded_operand, operand_src), |
| 11920 | }; |
| 11921 | break :tag_ref try sema.unionToTag(case_block, operand_val); |
| 11922 | }; |
| 11923 | |
| 11924 | assert(!sema.typeOf(payload_ref).isNoReturn(zcu)); |
| 11925 | return .{ .payload_ref = payload_ref, .tag_ref = tag_ref }; |
| 11880 | 11926 | } |
| 11881 | | switch (kind) { |
| 11882 | | .has_ranges => unreachable, |
| 11883 | | .inline_ref => |ref| return ref, |
| 11884 | | .item_refs => |refs| if (refs.len == 1) return refs[0], |
| 11885 | | .special => {}, |
| 11927 | |
| 11928 | const payload_ref: Air.Inst.Ref = payload_ref: { |
| 11929 | if (capture == .none) break :payload_ref .none; |
| 11930 | |
| 11931 | if (operand_ty.zigTypeTag(zcu) == .error_set) { |
| 11932 | // Error captures need to have their type narrowed! |
| 11933 | |
| 11934 | if (capture == .by_ref) { |
| 11935 | return sema.fail( |
| 11936 | case_block, |
| 11937 | capture_src, |
| 11938 | "error set cannot be captured by reference", |
| 11939 | .{}, |
| 11940 | ); |
| 11941 | } |
| 11942 | assert(capture == .by_val); |
| 11943 | |
| 11944 | if (sema.resolveSwitchCaptureFromProngItems(prong_items, false)) |err_val| { |
| 11945 | const err_name = err_val.getErrorName(zcu).unwrap().?; |
| 11946 | break :payload_ref .fromIntern((try pt.intern(.{ .err = .{ |
| 11947 | .ty = (try pt.singleErrorSetType(err_name)).toIntern(), |
| 11948 | .name = err_name, |
| 11949 | } }))); |
| 11950 | } |
| 11951 | |
| 11952 | const loaded_operand = try sema.analyzeSwitchOperandLoad(case_block, operand, operand_src, false); |
| 11953 | |
| 11954 | switch (prong_items) { |
| 11955 | .@"inline" => unreachable, // handled above |
| 11956 | .has_ranges => unreachable, // not possible for error set |
| 11957 | .special => { |
| 11958 | if (else_err_ty) |err_ty| { |
| 11959 | break :payload_ref try sema.bitCast(case_block, err_ty, loaded_operand, operand_src, null); |
| 11960 | } else { |
| 11961 | try sema.analyzeUnreachable(case_block, operand_src, false); |
| 11962 | break :payload_ref .unreachable_value; |
| 11963 | } |
| 11964 | }, |
| 11965 | .item_refs => |item_refs| { |
| 11966 | var names: InferredErrorSet.NameMap = .{}; |
| 11967 | try names.ensureUnusedCapacity(sema.arena, item_refs.len); |
| 11968 | for (item_refs) |item_ref| { |
| 11969 | const item_val = sema.resolveValue(item_ref).?; |
| 11970 | names.putAssumeCapacityNoClobber(item_val.getErrorName(zcu).unwrap().?, {}); |
| 11971 | } |
| 11972 | const narrowed_ty = try pt.errorSetFromUnsortedNames(names.keys()); |
| 11973 | break :payload_ref try sema.bitCast(case_block, narrowed_ty, loaded_operand, operand_src, null); |
| 11974 | }, |
| 11975 | } |
| 11976 | } |
| 11977 | |
| 11978 | // We try to make the capture comptime-known based on `prong_items` first: |
| 11979 | |
| 11980 | if (sema.resolveSwitchCaptureFromProngItems(prong_items, capture == .by_ref)) |item_val| { |
| 11981 | break :payload_ref switch (capture) { |
| 11982 | .none => unreachable, // handled above |
| 11983 | .by_val => .fromValue(item_val), |
| 11984 | .by_ref => try sema.uavRef(item_val), |
| 11985 | }; |
| 11986 | } |
| 11987 | |
| 11988 | // Otherwise the capture value is just the passed-through value of the |
| 11989 | // switch condition (which we might have to load first). |
| 11990 | |
| 11991 | break :payload_ref try sema.analyzeSwitchOperandLoad(case_block, operand, operand_src, capture == .by_ref); |
| 11992 | }; |
| 11993 | |
| 11994 | if (has_tag_capture) { |
| 11995 | const tag_capture_src: LazySrcLoc = .{ |
| 11996 | .base_node_inst = capture_src.base_node_inst, |
| 11997 | .offset = .{ .switch_tag_capture = capture_src.offset.switch_capture }, |
| 11998 | }; |
| 11999 | return sema.failWithInvalidSwitchTagCapture(case_block, tag_capture_src, operand_ty); |
| 11886 | 12000 | } |
| 11887 | | return sema.unionToTag(case_block, operand_val); |
| 12001 | |
| 12002 | return .{ .payload_ref = payload_ref, .tag_ref = .none }; |
| 11888 | 12003 | } |
| 11889 | 12004 | |
| 11890 | | fn analyzeSwitchPayloadCapture( |
| 12005 | fn resolveSwitchPayloadCaptureTaggedUnion( |
| 11891 | 12006 | sema: *Sema, |
| 11892 | 12007 | case_block: *Block, |
| 11893 | | operand: SwitchOperand, |
| 11894 | | /// Always has to be not-`none` if this is a tagged union payload capture. |
| 11895 | | /// For non-tagged-union captures, this may be `none` if this is an inline |
| 11896 | | /// capture or if `kind.item_refs.len == 1` and capture is by val. |
| 11897 | | operand_val: Air.Inst.Ref, |
| 11898 | | /// May be `none` if `capture_by_ref` is `false` or if `operand_val` is also `none`. |
| 11899 | | operand_ptr: Air.Inst.Ref, |
| 12008 | loaded_operand: Air.Inst.Ref, |
| 12009 | operand_src: LazySrcLoc, |
| 11900 | 12010 | operand_ty: Type, |
| 12011 | tag_val: Value, |
| 12012 | capture_by_ref: bool, |
| 12013 | ) CompileError!Air.Inst.Ref { |
| 12014 | const pt = sema.pt; |
| 12015 | const zcu = pt.zcu; |
| 12016 | const ip = &zcu.intern_pool; |
| 12017 | |
| 12018 | const field_index: u32 = @intCast(operand_ty.unionTagFieldIndex(tag_val, zcu).?); |
| 12019 | const union_obj = zcu.typeToUnion(operand_ty).?; |
| 12020 | const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]); |
| 12021 | const payload_ref: Air.Inst.Ref = payload_ref: { |
| 12022 | if (capture_by_ref) { |
| 12023 | const operand_ptr_info = sema.typeOf(loaded_operand).ptrInfo(zcu); |
| 12024 | const ptr_field_ty = try pt.ptrType(.{ |
| 12025 | .child = field_ty.toIntern(), |
| 12026 | .flags = .{ |
| 12027 | .is_const = operand_ptr_info.flags.is_const, |
| 12028 | .is_volatile = operand_ptr_info.flags.is_volatile, |
| 12029 | .address_space = operand_ptr_info.flags.address_space, |
| 12030 | }, |
| 12031 | }); |
| 12032 | break :payload_ref try case_block.addStructFieldPtr(loaded_operand, field_index, ptr_field_ty); |
| 12033 | } |
| 12034 | if (try sema.resolveDefinedValue(case_block, operand_src, loaded_operand)) |union_val| { |
| 12035 | const tag_and_val = ip.indexToKey(union_val.toIntern()).un; |
| 12036 | break :payload_ref .fromIntern(tag_and_val.val); |
| 12037 | } |
| 12038 | if (try field_ty.onePossibleValue(pt)) |opv| break :payload_ref .fromValue(opv); |
| 12039 | break :payload_ref try case_block.addStructFieldVal(loaded_operand, field_index, field_ty); |
| 12040 | }; |
| 12041 | assert(!sema.typeOf(payload_ref).isNoReturn(zcu)); |
| 12042 | return payload_ref; |
| 12043 | } |
| 12044 | |
| 12045 | fn analyzeSwitchPayloadCaptureTaggedUnion( |
| 12046 | sema: *Sema, |
| 12047 | case_block: *Block, |
| 12048 | operand: SwitchOperand, |
| 12049 | loaded_operand: Air.Inst.Ref, |
| 11901 | 12050 | operand_src: LazySrcLoc, |
| 11902 | | capture_src: LazySrcLoc, |
| 12051 | operand_ty: Type, |
| 11903 | 12052 | capture_by_ref: bool, |
| 11904 | | kind: SwitchProngKind, |
| 11905 | | else_err_ty: ?Type, |
| 12053 | capture_src: LazySrcLoc, |
| 12054 | prong_items: SwitchProngItems, |
| 11906 | 12055 | ) CompileError!Air.Inst.Ref { |
| 11907 | 12056 | const pt = sema.pt; |
| 11908 | 12057 | const zcu = pt.zcu; |
| 11909 | 12058 | const ip = &zcu.intern_pool; |
| 12059 | const gpa = sema.gpa; |
| 12060 | |
| 12061 | const item_refs: []const Air.Inst.Ref = switch (prong_items) { |
| 12062 | .@"inline" => unreachable, // handled above |
| 12063 | .has_ranges => unreachable, // not possible for tagged union |
| 12064 | .special => return loaded_operand, |
| 12065 | .item_refs => |item_refs| item_refs, |
| 12066 | }; |
| 11910 | 12067 | |
| 11911 | 12068 | const switch_node_offset = operand_src.offset.node_offset_switch_operand; |
| 11912 | 12069 | |
| 11913 | | const tagged_union_originally = operand_ty.zigTypeTag(zcu) == .@"union" and |
| 11914 | | operand_ty.containerLayout(zcu) != .@"packed"; |
| 11915 | | const err_set = operand_ty.zigTypeTag(zcu) == .error_set; |
| 12070 | const union_obj = zcu.typeToUnion(operand_ty).?; |
| 11916 | 12071 | |
| 11917 | | if (err_set and capture_by_ref) { |
| 11918 | | return sema.fail( |
| 11919 | | case_block, |
| 11920 | | capture_src, |
| 11921 | | "error set cannot be captured by reference", |
| 11922 | | .{}, |
| 11923 | | ); |
| 11924 | | } |
| 12072 | const first_item_val = sema.resolveValue(item_refs[0]).?; |
| 12073 | const first_field_index: u32 = zcu.unionTagFieldIndex(union_obj, first_item_val).?; |
| 12074 | const first_field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[first_field_index]); |
| 11925 | 12075 | |
| 11926 | | if (kind == .inline_ref) { |
| 11927 | | const item_val = sema.resolveValue(kind.inline_ref).?; |
| 11928 | | if (tagged_union_originally) { |
| 11929 | | const field_index: u32 = @intCast(operand_ty.unionTagFieldIndex(item_val, zcu).?); |
| 11930 | | const union_obj = zcu.typeToUnion(operand_ty).?; |
| 11931 | | const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]); |
| 11932 | | if (capture_by_ref) { |
| 11933 | | const operand_ptr_info = sema.typeOf(operand_ptr).ptrInfo(zcu); |
| 11934 | | const ptr_field_ty = try pt.ptrType(.{ |
| 11935 | | .child = field_ty.toIntern(), |
| 11936 | | .flags = .{ |
| 11937 | | .is_const = operand_ptr_info.flags.is_const, |
| 11938 | | .is_volatile = operand_ptr_info.flags.is_volatile, |
| 11939 | | .address_space = operand_ptr_info.flags.address_space, |
| 11940 | | }, |
| 11941 | | }); |
| 11942 | | return case_block.addStructFieldPtr(operand_ptr, field_index, ptr_field_ty); |
| 11943 | | } else { |
| 11944 | | if (try sema.resolveDefinedValue(case_block, operand_src, operand_val)) |union_val| { |
| 11945 | | const tag_and_val = ip.indexToKey(union_val.toIntern()).un; |
| 11946 | | return .fromIntern(tag_and_val.val); |
| 11947 | | } |
| 11948 | | if (try field_ty.onePossibleValue(pt)) |opv| return .fromValue(opv); |
| 11949 | | return case_block.addStructFieldVal(operand_val, field_index, field_ty); |
| 11950 | | } |
| 11951 | | } else if (capture_by_ref) { |
| 11952 | | return sema.uavRef(item_val); |
| 11953 | | } else { |
| 11954 | | return kind.inline_ref; |
| 11955 | | } |
| 12076 | const field_indices = try sema.arena.alloc(u32, item_refs.len); |
| 12077 | for (item_refs, field_indices) |item_ref, *field_idx| { |
| 12078 | const item_val = sema.resolveValue(item_ref).?; |
| 12079 | field_idx.* = zcu.unionTagFieldIndex(union_obj, item_val).?; |
| 11956 | 12080 | } |
| 11957 | 12081 | |
| 11958 | | if (kind == .special) { |
| 11959 | | if (err_set) { |
| 11960 | | if (else_err_ty) |err_ty| { |
| 11961 | | return sema.bitCast(case_block, err_ty, operand_val, operand_src, null); |
| 11962 | | } else { |
| 11963 | | try sema.analyzeUnreachable(case_block, operand_src, false); |
| 11964 | | return .unreachable_value; |
| 11965 | | } |
| 11966 | | } |
| 11967 | | if (capture_by_ref) { |
| 11968 | | return operand_ptr; |
| 11969 | | } |
| 11970 | | return operand_val; |
| 11971 | | } |
| 12082 | // Fast path: if all the operands are the same type already, we don't need to hit |
| 12083 | // PTR! This will also allow us to emit simpler code. |
| 12084 | const same_types = for (field_indices[1..]) |field_idx| { |
| 12085 | const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]); |
| 12086 | if (!field_ty.eql(first_field_ty, zcu)) break false; |
| 12087 | } else true; |
| 11972 | 12088 | |
| 11973 | | if (tagged_union_originally) { |
| 11974 | | const case_vals = kind.item_refs; |
| 11975 | | |
| 11976 | | const union_obj = zcu.typeToUnion(operand_ty).?; |
| 11977 | | const first_item_val = sema.resolveValue(case_vals[0]).?; |
| 11978 | | |
| 11979 | | const first_field_index: u32 = zcu.unionTagFieldIndex(union_obj, first_item_val).?; |
| 11980 | | const first_field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[first_field_index]); |
| 12089 | const capture_ty: Type = capture_ty: { |
| 12090 | if (same_types) break :capture_ty first_field_ty; |
| 12091 | // We need values to run PTR on, so make a bunch of undef constants. |
| 12092 | const dummy_captures = try sema.arena.alloc(Air.Inst.Ref, item_refs.len); |
| 12093 | for (dummy_captures, field_indices) |*dummy, field_idx| { |
| 12094 | const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]); |
| 12095 | dummy.* = try pt.undefRef(field_ty); |
| 12096 | } |
| 11981 | 12097 | |
| 11982 | | const field_indices = try sema.arena.alloc(u32, case_vals.len); |
| 11983 | | for (case_vals, field_indices) |item, *field_idx| { |
| 11984 | | const item_val = sema.resolveValue(item).?; |
| 11985 | | field_idx.* = zcu.unionTagFieldIndex(union_obj, item_val).?; |
| 12098 | const item_srcs = try sema.arena.alloc(?LazySrcLoc, item_refs.len); |
| 12099 | for (item_srcs, 0..) |*item_src, item_i| { |
| 12100 | item_src.* = .{ |
| 12101 | .base_node_inst = capture_src.base_node_inst, |
| 12102 | .offset = .{ .switch_case_item = .{ |
| 12103 | .switch_node_offset = switch_node_offset, |
| 12104 | .case_idx = capture_src.offset.switch_capture.case_idx, |
| 12105 | .item_idx = .{ .kind = .single, .value = @intCast(item_i) }, |
| 12106 | } }, |
| 12107 | }; |
| 11986 | 12108 | } |
| 11987 | 12109 | |
| 11988 | | // Fast path: if all the operands are the same type already, we don't need to hit |
| 11989 | | // PTR! This will also allow us to emit simpler code. |
| 11990 | | const same_types = for (field_indices[1..]) |field_idx| { |
| 11991 | | const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]); |
| 11992 | | if (!field_ty.eql(first_field_ty, zcu)) break false; |
| 11993 | | } else true; |
| 12110 | break :capture_ty sema.resolvePeerTypes( |
| 12111 | case_block, |
| 12112 | capture_src, |
| 12113 | dummy_captures, |
| 12114 | .{ .override = item_srcs }, |
| 12115 | ) catch |err| switch (err) { |
| 12116 | error.AnalysisFail => { |
| 12117 | const msg = sema.err orelse return error.AnalysisFail; |
| 12118 | try sema.reparentOwnedErrorMsg(capture_src, msg, "capture group with incompatible types", .{}); |
| 12119 | return error.AnalysisFail; |
| 12120 | }, |
| 12121 | else => |e| return e, |
| 12122 | }; |
| 12123 | }; |
| 11994 | 12124 | |
| 11995 | | const capture_ty: Type = capture_ty: { |
| 11996 | | if (same_types) break :capture_ty first_field_ty; |
| 12125 | // By-reference captures have some further restrictions which make them easier to emit |
| 12126 | if (capture_by_ref) { |
| 12127 | const operand_ptr_ty = sema.typeOf(loaded_operand); |
| 12128 | const capture_ptr_ty = resolve: { |
| 12129 | // By-ref captures of hetereogeneous types are only allowed if all field |
| 12130 | // pointer types are peer resolvable to each other. |
| 11997 | 12131 | // We need values to run PTR on, so make a bunch of undef constants. |
| 11998 | | const dummy_captures = try sema.arena.alloc(Air.Inst.Ref, case_vals.len); |
| 11999 | | for (dummy_captures, field_indices) |*dummy, field_idx| { |
| 12000 | | const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]); |
| 12001 | | dummy.* = try pt.undefRef(field_ty); |
| 12002 | | } |
| 12003 | | |
| 12004 | | const case_srcs = try sema.arena.alloc(?LazySrcLoc, case_vals.len); |
| 12005 | | for (case_srcs, 0..) |*case_src, item_i| { |
| 12006 | | case_src.* = .{ |
| 12132 | const dummy_captures = try sema.arena.alloc(Air.Inst.Ref, item_refs.len); |
| 12133 | for (field_indices, dummy_captures) |field_index, *dummy| { |
| 12134 | const field_ptr_ty = try operand_ptr_ty.fieldPtrType(field_index, pt); |
| 12135 | dummy.* = try pt.undefRef(field_ptr_ty); |
| 12136 | } |
| 12137 | const item_srcs = try sema.arena.alloc(?LazySrcLoc, item_refs.len); |
| 12138 | for (item_srcs, 0..) |*item_src, item_i| { |
| 12139 | item_src.* = .{ |
| 12007 | 12140 | .base_node_inst = capture_src.base_node_inst, |
| 12008 | 12141 | .offset = .{ .switch_case_item = .{ |
| 12009 | 12142 | .switch_node_offset = switch_node_offset, |
| ... | ... | @@ -12013,14 +12146,15 @@ fn analyzeSwitchPayloadCapture( |
| 12013 | 12146 | }; |
| 12014 | 12147 | } |
| 12015 | 12148 | |
| 12016 | | break :capture_ty sema.resolvePeerTypes( |
| 12149 | break :resolve sema.resolvePeerTypes( |
| 12017 | 12150 | case_block, |
| 12018 | 12151 | capture_src, |
| 12019 | 12152 | dummy_captures, |
| 12020 | | .{ .override = case_srcs }, |
| 12153 | .{ .override = item_srcs }, |
| 12021 | 12154 | ) catch |err| switch (err) { |
| 12022 | 12155 | error.AnalysisFail => { |
| 12023 | 12156 | const msg = sema.err orelse return error.AnalysisFail; |
| 12157 | try sema.errNote(capture_src, msg, "this coercion is only possible when capturing by value", .{}); |
| 12024 | 12158 | try sema.reparentOwnedErrorMsg(capture_src, msg, "capture group with incompatible types", .{}); |
| 12025 | 12159 | return error.AnalysisFail; |
| 12026 | 12160 | }, |
| ... | ... | @@ -12028,262 +12162,188 @@ fn analyzeSwitchPayloadCapture( |
| 12028 | 12162 | }; |
| 12029 | 12163 | }; |
| 12030 | 12164 | |
| 12031 | | // By-reference captures have some further restrictions which make them easier to emit |
| 12032 | | if (capture_by_ref) { |
| 12033 | | const operand_ptr_ty = sema.typeOf(operand_ptr); |
| 12034 | | const capture_ptr_ty = resolve: { |
| 12035 | | // By-ref captures of hetereogeneous types are only allowed if all field |
| 12036 | | // pointer types are peer resolvable to each other. |
| 12037 | | // We need values to run PTR on, so make a bunch of undef constants. |
| 12038 | | const dummy_captures = try sema.arena.alloc(Air.Inst.Ref, case_vals.len); |
| 12039 | | for (field_indices, dummy_captures) |field_index, *dummy| { |
| 12040 | | const field_ptr_ty = try operand_ptr_ty.fieldPtrType(field_index, pt); |
| 12041 | | dummy.* = try pt.undefRef(field_ptr_ty); |
| 12042 | | } |
| 12043 | | const case_srcs = try sema.arena.alloc(?LazySrcLoc, case_vals.len); |
| 12044 | | for (case_srcs, 0..) |*case_src, item_i| { |
| 12045 | | case_src.* = .{ |
| 12046 | | .base_node_inst = capture_src.base_node_inst, |
| 12047 | | .offset = .{ .switch_case_item = .{ |
| 12048 | | .switch_node_offset = switch_node_offset, |
| 12049 | | .case_idx = capture_src.offset.switch_capture.case_idx, |
| 12050 | | .item_idx = .{ .kind = .single, .value = @intCast(item_i) }, |
| 12051 | | } }, |
| 12052 | | }; |
| 12053 | | } |
| 12054 | | |
| 12055 | | break :resolve sema.resolvePeerTypes( |
| 12056 | | case_block, |
| 12057 | | capture_src, |
| 12058 | | dummy_captures, |
| 12059 | | .{ .override = case_srcs }, |
| 12060 | | ) catch |err| switch (err) { |
| 12061 | | error.AnalysisFail => { |
| 12062 | | const msg = sema.err orelse return error.AnalysisFail; |
| 12063 | | try sema.errNote(capture_src, msg, "this coercion is only possible when capturing by value", .{}); |
| 12064 | | try sema.reparentOwnedErrorMsg(capture_src, msg, "capture group with incompatible types", .{}); |
| 12065 | | return error.AnalysisFail; |
| 12066 | | }, |
| 12067 | | else => |e| return e, |
| 12068 | | }; |
| 12069 | | }; |
| 12070 | | |
| 12071 | | if (try sema.resolveDefinedValue(case_block, operand_src, operand_ptr)) |op_ptr_val| { |
| 12072 | | if (op_ptr_val.isUndef(zcu)) return pt.undefRef(capture_ptr_ty); |
| 12073 | | const field_ptr_val = try op_ptr_val.ptrField(first_field_index, pt); |
| 12074 | | return .fromValue(try pt.getCoerced(field_ptr_val, capture_ptr_ty)); |
| 12075 | | } |
| 12076 | | |
| 12077 | | try sema.requireRuntimeBlock(case_block, operand_src, null); |
| 12078 | | return case_block.addStructFieldPtr(operand_ptr, first_field_index, capture_ptr_ty); |
| 12079 | | } |
| 12080 | | |
| 12081 | | if (try capture_ty.onePossibleValue(pt)) |opv| return .fromValue(opv); |
| 12082 | | |
| 12083 | | if (try sema.resolveDefinedValue(case_block, operand_src, operand_val)) |operand_val_val| { |
| 12084 | | if (operand_val_val.isUndef(zcu)) return pt.undefRef(capture_ty); |
| 12085 | | const union_val = ip.indexToKey(operand_val_val.toIntern()).un; |
| 12086 | | if (Value.fromInterned(union_val.tag).isUndef(zcu)) return pt.undefRef(capture_ty); |
| 12087 | | const uncoerced: Air.Inst.Ref = .fromIntern(union_val.val); |
| 12088 | | return sema.coerce(case_block, capture_ty, uncoerced, operand_src); |
| 12165 | if (try sema.resolveDefinedValue(case_block, operand_src, loaded_operand)) |op_ptr_val| { |
| 12166 | if (op_ptr_val.isUndef(zcu)) return pt.undefRef(capture_ptr_ty); |
| 12167 | const field_ptr_val = try op_ptr_val.ptrField(first_field_index, pt); |
| 12168 | return .fromValue(try pt.getCoerced(field_ptr_val, capture_ptr_ty)); |
| 12089 | 12169 | } |
| 12090 | 12170 | |
| 12091 | 12171 | try sema.requireRuntimeBlock(case_block, operand_src, null); |
| 12172 | return case_block.addStructFieldPtr(loaded_operand, first_field_index, capture_ptr_ty); |
| 12173 | } |
| 12092 | 12174 | |
| 12093 | | if (same_types) { |
| 12094 | | return case_block.addStructFieldVal(operand_val, first_field_index, capture_ty); |
| 12095 | | } |
| 12175 | if (try capture_ty.onePossibleValue(pt)) |opv| return .fromValue(opv); |
| 12096 | 12176 | |
| 12097 | | // We may have to emit a switch block which coerces the operand to the capture type. |
| 12098 | | // If we can, try to avoid that using in-memory coercions. |
| 12099 | | const first_non_imc = in_mem: { |
| 12100 | | for (field_indices, 0..) |field_idx, i| { |
| 12101 | | const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]); |
| 12102 | | if (.ok != try sema.coerceInMemoryAllowed(case_block, capture_ty, field_ty, false, zcu.getTarget(), .unneeded, .unneeded, null)) { |
| 12103 | | break :in_mem i; |
| 12104 | | } |
| 12105 | | } |
| 12106 | | // All fields are in-memory coercible to the resolved type! |
| 12107 | | // Just take the first field and bitcast the result. |
| 12108 | | const uncoerced = try case_block.addStructFieldVal(operand_val, first_field_index, first_field_ty); |
| 12109 | | return case_block.addBitCast(capture_ty, uncoerced); |
| 12110 | | }; |
| 12177 | if (try sema.resolveDefinedValue(case_block, operand_src, loaded_operand)) |operand_val| { |
| 12178 | if (operand_val.isUndef(zcu)) return pt.undefRef(capture_ty); |
| 12179 | const union_val = ip.indexToKey(operand_val.toIntern()).un; |
| 12180 | if (Value.fromInterned(union_val.tag).isUndef(zcu)) return pt.undefRef(capture_ty); |
| 12181 | const uncoerced: Air.Inst.Ref = .fromIntern(union_val.val); |
| 12182 | return sema.coerce(case_block, capture_ty, uncoerced, operand_src); |
| 12183 | } |
| 12111 | 12184 | |
| 12112 | | // By-val capture with heterogeneous types which are not all in-memory coercible to |
| 12113 | | // the resolved capture type. We finally have to fall back to the ugly method. |
| 12185 | try sema.requireRuntimeBlock(case_block, operand_src, null); |
| 12114 | 12186 | |
| 12115 | | // However, let's first track which operands are in-memory coercible. There may well |
| 12116 | | // be several, and we can squash all of these cases into the same switch prong using |
| 12117 | | // a simple bitcast. We'll make this the 'else' prong. |
| 12187 | if (same_types) { |
| 12188 | return case_block.addStructFieldVal(loaded_operand, first_field_index, capture_ty); |
| 12189 | } |
| 12118 | 12190 | |
| 12119 | | var in_mem_coercible: std.bit_set.Dynamic = try .initFull(sema.arena, field_indices.len); |
| 12120 | | in_mem_coercible.unset(first_non_imc); |
| 12121 | | { |
| 12122 | | const next = first_non_imc + 1; |
| 12123 | | for (field_indices[next..], next..) |field_idx, i| { |
| 12124 | | const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]); |
| 12125 | | if (.ok != try sema.coerceInMemoryAllowed(case_block, capture_ty, field_ty, false, zcu.getTarget(), .unneeded, .unneeded, null)) { |
| 12126 | | in_mem_coercible.unset(i); |
| 12127 | | } |
| 12191 | // We may have to emit a switch block which coerces the operand to the capture type. |
| 12192 | // If we can, try to avoid that using in-memory coercions. |
| 12193 | const first_non_imc = in_mem: { |
| 12194 | for (field_indices, 0..) |field_idx, i| { |
| 12195 | const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]); |
| 12196 | if (.ok != try sema.coerceInMemoryAllowed(case_block, capture_ty, field_ty, false, zcu.getTarget(), .unneeded, .unneeded, null)) { |
| 12197 | break :in_mem i; |
| 12128 | 12198 | } |
| 12129 | 12199 | } |
| 12200 | // All fields are in-memory coercible to the resolved type! |
| 12201 | // Just take the first field and bitcast the result. |
| 12202 | const uncoerced = try case_block.addStructFieldVal(loaded_operand, first_field_index, first_field_ty); |
| 12203 | return case_block.addBitCast(capture_ty, uncoerced); |
| 12204 | }; |
| 12130 | 12205 | |
| 12131 | | const capture_block_inst = try case_block.addInstAsIndex(.{ |
| 12132 | | .tag = .block, |
| 12133 | | .data = .{ |
| 12134 | | .ty_pl = .{ |
| 12135 | | .ty = .fromType(capture_ty), |
| 12136 | | .payload = undefined, // updated below |
| 12137 | | }, |
| 12138 | | }, |
| 12139 | | }); |
| 12140 | | |
| 12141 | | const prong_count = field_indices.len - in_mem_coercible.count(); |
| 12206 | // By-val capture with heterogeneous types which are not all in-memory coercible to |
| 12207 | // the resolved capture type. We finally have to fall back to the ugly method. |
| 12142 | 12208 | |
| 12143 | | const estimated_extra = prong_count * 6 + (prong_count / 10); // 2 for Case, 1 item, probably 3 insts; plus hints |
| 12144 | | var cases_extra = try std.array_list.Managed(u32).initCapacity(sema.gpa, estimated_extra); |
| 12145 | | defer cases_extra.deinit(); |
| 12209 | // However, let's first track which operands are in-memory coercible. There may well |
| 12210 | // be several, and we can squash all of these cases into the same switch prong using |
| 12211 | // a simple bitcast. We'll make this the 'else' prong. |
| 12146 | 12212 | |
| 12147 | | { |
| 12148 | | // All branch hints are `.none`, so just add zero elems. |
| 12149 | | comptime assert(@intFromEnum(std.lang.BranchHint.none) == 0); |
| 12150 | | const need_elems = std.math.divCeil(usize, prong_count + 1, 10) catch unreachable; |
| 12151 | | try cases_extra.appendNTimes(0, need_elems); |
| 12213 | var in_mem_coercible: std.bit_set.Dynamic = try .initFull(sema.arena, field_indices.len); |
| 12214 | in_mem_coercible.unset(first_non_imc); |
| 12215 | { |
| 12216 | const next = first_non_imc + 1; |
| 12217 | for (field_indices[next..], next..) |field_idx, i| { |
| 12218 | const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]); |
| 12219 | if (.ok != try sema.coerceInMemoryAllowed(case_block, capture_ty, field_ty, false, zcu.getTarget(), .unneeded, .unneeded, null)) { |
| 12220 | in_mem_coercible.unset(i); |
| 12221 | } |
| 12152 | 12222 | } |
| 12223 | } |
| 12153 | 12224 | |
| 12154 | | { |
| 12155 | | // Non-bitcast cases |
| 12156 | | var it = in_mem_coercible.iterator(.{ .kind = .unset }); |
| 12157 | | while (it.next()) |idx| { |
| 12158 | | var coerce_block = case_block.makeSubBlock(); |
| 12159 | | defer coerce_block.instructions.deinit(sema.gpa); |
| 12225 | const capture_block_inst = try case_block.addInstAsIndex(.{ |
| 12226 | .tag = .block, |
| 12227 | .data = .{ |
| 12228 | .ty_pl = .{ |
| 12229 | .ty = .fromType(capture_ty), |
| 12230 | .payload = undefined, // updated below |
| 12231 | }, |
| 12232 | }, |
| 12233 | }); |
| 12160 | 12234 | |
| 12161 | | const case_src: LazySrcLoc = .{ |
| 12162 | | .base_node_inst = capture_src.base_node_inst, |
| 12163 | | .offset = .{ .switch_case_item = .{ |
| 12164 | | .switch_node_offset = switch_node_offset, |
| 12165 | | .case_idx = capture_src.offset.switch_capture.case_idx, |
| 12166 | | .item_idx = .{ .kind = .single, .value = @intCast(idx) }, |
| 12167 | | } }, |
| 12168 | | }; |
| 12235 | const prong_count = field_indices.len - in_mem_coercible.count(); |
| 12169 | 12236 | |
| 12170 | | const field_idx = field_indices[idx]; |
| 12171 | | const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]); |
| 12172 | | const uncoerced = try coerce_block.addStructFieldVal(operand_val, field_idx, field_ty); |
| 12173 | | const coerced = try sema.coerce(&coerce_block, capture_ty, uncoerced, case_src); |
| 12174 | | _ = try coerce_block.addBr(capture_block_inst, coerced); |
| 12237 | const estimated_extra = prong_count * 6 + (prong_count / 10); // 2 for Case, 1 item, probably 3 insts; plus hints |
| 12238 | var cases_extra = try std.ArrayList(u32).initCapacity(gpa, estimated_extra); |
| 12239 | defer cases_extra.deinit(gpa); |
| 12175 | 12240 | |
| 12176 | | try cases_extra.ensureUnusedCapacity(@typeInfo(Air.SwitchBr.Case).@"struct".field_names.len + |
| 12177 | | 1 + // `item`, no ranges |
| 12178 | | coerce_block.instructions.items.len); |
| 12179 | | cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{ |
| 12180 | | .items_len = 1, |
| 12181 | | .ranges_len = 0, |
| 12182 | | .body_len = @intCast(coerce_block.instructions.items.len), |
| 12183 | | })); |
| 12184 | | cases_extra.appendAssumeCapacity(@intFromEnum(case_vals[idx])); // item |
| 12185 | | cases_extra.appendSliceAssumeCapacity(@ptrCast(coerce_block.instructions.items)); // body |
| 12186 | | } |
| 12187 | | } |
| 12188 | | const else_body_len = len: { |
| 12189 | | // 'else' prong uses a bitcast |
| 12241 | { |
| 12242 | // All branch hints are `.none`, so just add zero elems. |
| 12243 | comptime assert(@intFromEnum(std.lang.BranchHint.none) == 0); |
| 12244 | const need_elems = std.math.divCeil(usize, prong_count + 1, 10) catch unreachable; |
| 12245 | try cases_extra.appendNTimes(gpa, 0, need_elems); |
| 12246 | } |
| 12247 | |
| 12248 | { |
| 12249 | // Non-bitcast cases |
| 12250 | var it = in_mem_coercible.iterator(.{ .kind = .unset }); |
| 12251 | while (it.next()) |idx| { |
| 12190 | 12252 | var coerce_block = case_block.makeSubBlock(); |
| 12191 | 12253 | defer coerce_block.instructions.deinit(sema.gpa); |
| 12192 | 12254 | |
| 12193 | | const first_imc_item_idx = in_mem_coercible.findFirstSet().?; |
| 12194 | | const first_imc_field_idx = field_indices[first_imc_item_idx]; |
| 12195 | | const first_imc_field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[first_imc_field_idx]); |
| 12196 | | const uncoerced = try coerce_block.addStructFieldVal(operand_val, first_imc_field_idx, first_imc_field_ty); |
| 12197 | | const coerced = try coerce_block.addBitCast(capture_ty, uncoerced); |
| 12198 | | _ = try coerce_block.addBr(capture_block_inst, coerced); |
| 12199 | | |
| 12200 | | try cases_extra.appendSlice(@ptrCast(coerce_block.instructions.items)); |
| 12201 | | break :len coerce_block.instructions.items.len; |
| 12202 | | }; |
| 12255 | const case_src: LazySrcLoc = .{ |
| 12256 | .base_node_inst = capture_src.base_node_inst, |
| 12257 | .offset = .{ .switch_case_item = .{ |
| 12258 | .switch_node_offset = switch_node_offset, |
| 12259 | .case_idx = capture_src.offset.switch_capture.case_idx, |
| 12260 | .item_idx = .{ .kind = .single, .value = @intCast(idx) }, |
| 12261 | } }, |
| 12262 | }; |
| 12203 | 12263 | |
| 12204 | | try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.SwitchBr).@"struct".field_names.len + |
| 12205 | | cases_extra.items.len + |
| 12206 | | @typeInfo(Air.Block).@"struct".field_names.len + |
| 12207 | | 1); |
| 12264 | const field_idx = field_indices[idx]; |
| 12265 | const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]); |
| 12266 | const uncoerced = try coerce_block.addStructFieldVal(loaded_operand, field_idx, field_ty); |
| 12267 | const coerced = try sema.coerce(&coerce_block, capture_ty, uncoerced, case_src); |
| 12268 | _ = try coerce_block.addBr(capture_block_inst, coerced); |
| 12208 | 12269 | |
| 12209 | | const switch_br_inst: u32 = @intCast(sema.air_instructions.len); |
| 12210 | | try sema.air_instructions.append(sema.gpa, .{ |
| 12211 | | .tag = .switch_br, |
| 12212 | | .data = .{ |
| 12213 | | .pl_op = .{ |
| 12214 | | .operand = undefined, // set by switch below |
| 12215 | | .payload = sema.addExtraAssumeCapacity(Air.SwitchBr{ |
| 12216 | | .cases_len = @intCast(prong_count), |
| 12217 | | .else_body_len = @intCast(else_body_len), |
| 12218 | | }), |
| 12219 | | }, |
| 12220 | | }, |
| 12221 | | }); |
| 12222 | | sema.air_extra.appendSliceAssumeCapacity(cases_extra.items); |
| 12223 | | |
| 12224 | | // Set up block body |
| 12225 | | switch (operand) { |
| 12226 | | .simple => |s| { |
| 12227 | | const air_datas = sema.air_instructions.items(.data); |
| 12228 | | air_datas[switch_br_inst].pl_op.operand = s.cond; |
| 12229 | | air_datas[@intFromEnum(capture_block_inst)].ty_pl.payload = |
| 12230 | | sema.addExtraAssumeCapacity(Air.Block{ .body_len = 1 }); |
| 12231 | | sema.air_extra.appendAssumeCapacity(switch_br_inst); |
| 12232 | | }, |
| 12233 | | .loop => { |
| 12234 | | // The block must first extract the tag from the loaded union. |
| 12235 | | const tag_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len); |
| 12236 | | try sema.air_instructions.append(sema.gpa, .{ |
| 12237 | | .tag = .get_union_tag, |
| 12238 | | .data = .{ .ty_op = .{ |
| 12239 | | .ty = .fromIntern(union_obj.enum_tag_type), |
| 12240 | | .operand = operand_val, |
| 12241 | | } }, |
| 12242 | | }); |
| 12243 | | const air_datas = sema.air_instructions.items(.data); |
| 12244 | | air_datas[switch_br_inst].pl_op.operand = tag_inst.toRef(); |
| 12245 | | air_datas[@intFromEnum(capture_block_inst)].ty_pl.payload = |
| 12246 | | sema.addExtraAssumeCapacity(Air.Block{ .body_len = 2 }); |
| 12247 | | sema.air_extra.appendAssumeCapacity(@intFromEnum(tag_inst)); |
| 12248 | | sema.air_extra.appendAssumeCapacity(switch_br_inst); |
| 12249 | | }, |
| 12270 | try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".field_names.len + |
| 12271 | 1 + // `item`, no ranges |
| 12272 | coerce_block.instructions.items.len); |
| 12273 | cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{ |
| 12274 | .items_len = 1, |
| 12275 | .ranges_len = 0, |
| 12276 | .body_len = @intCast(coerce_block.instructions.items.len), |
| 12277 | })); |
| 12278 | cases_extra.appendAssumeCapacity(@intFromEnum(item_refs[idx])); // item |
| 12279 | cases_extra.appendSliceAssumeCapacity(@ptrCast(coerce_block.instructions.items)); // body |
| 12250 | 12280 | } |
| 12251 | | |
| 12252 | | return capture_block_inst.toRef(); |
| 12253 | 12281 | } |
| 12282 | const else_body_len = len: { |
| 12283 | // 'else' prong uses a bitcast |
| 12284 | var coerce_block = case_block.makeSubBlock(); |
| 12285 | defer coerce_block.instructions.deinit(sema.gpa); |
| 12254 | 12286 | |
| 12255 | | if (err_set) { |
| 12256 | | const case_vals = kind.item_refs; |
| 12257 | | if (case_vals.len == 1) { |
| 12258 | | const item_val = sema.resolveValue(case_vals[0]).?; |
| 12259 | | const item_ty = try pt.singleErrorSetType(item_val.getErrorName(zcu).unwrap().?); |
| 12260 | | return sema.bitCast(case_block, item_ty, .fromValue(item_val), operand_src, null); |
| 12261 | | } |
| 12287 | const first_imc_item_idx = in_mem_coercible.findFirstSet().?; |
| 12288 | const first_imc_field_idx = field_indices[first_imc_item_idx]; |
| 12289 | const first_imc_field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[first_imc_field_idx]); |
| 12290 | const uncoerced = try coerce_block.addStructFieldVal(loaded_operand, first_imc_field_idx, first_imc_field_ty); |
| 12291 | const coerced = try coerce_block.addBitCast(capture_ty, uncoerced); |
| 12292 | _ = try coerce_block.addBr(capture_block_inst, coerced); |
| 12262 | 12293 | |
| 12263 | | var names: InferredErrorSet.NameMap = .{}; |
| 12264 | | try names.ensureUnusedCapacity(sema.arena, case_vals.len); |
| 12265 | | for (case_vals) |err| { |
| 12266 | | const err_val = sema.resolveValue(err).?; |
| 12267 | | names.putAssumeCapacityNoClobber(err_val.getErrorName(zcu).unwrap().?, {}); |
| 12268 | | } |
| 12269 | | const error_ty = try pt.errorSetFromUnsortedNames(names.keys()); |
| 12270 | | return sema.bitCast(case_block, error_ty, operand_val, operand_src, null); |
| 12271 | | } |
| 12294 | try cases_extra.appendSlice(gpa, @ptrCast(coerce_block.instructions.items)); |
| 12295 | break :len coerce_block.instructions.items.len; |
| 12296 | }; |
| 12272 | 12297 | |
| 12273 | | // In this case the capture value is just the passed-through value of the |
| 12274 | | // switch condition. It is comptime-known if there is only one item. |
| 12275 | | if (capture_by_ref) { |
| 12276 | | return operand_ptr; |
| 12277 | | } |
| 12278 | | switch (kind) { |
| 12279 | | .inline_ref, .special => unreachable, |
| 12280 | | .item_refs => |case_vals| { |
| 12281 | | // If there's only a single item, the capture is comptime-known! |
| 12282 | | if (case_vals.len == 1) return case_vals[0]; |
| 12298 | try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr).@"struct".field_names.len + |
| 12299 | cases_extra.items.len + |
| 12300 | @typeInfo(Air.Block).@"struct".field_names.len + |
| 12301 | 1); |
| 12302 | |
| 12303 | const switch_br_inst: u32 = @intCast(sema.air_instructions.len); |
| 12304 | try sema.air_instructions.append(gpa, .{ |
| 12305 | .tag = .switch_br, |
| 12306 | .data = .{ |
| 12307 | .pl_op = .{ |
| 12308 | .operand = undefined, // set by switch below |
| 12309 | .payload = sema.addExtraAssumeCapacity(Air.SwitchBr{ |
| 12310 | .cases_len = @intCast(prong_count), |
| 12311 | .else_body_len = @intCast(else_body_len), |
| 12312 | }), |
| 12313 | }, |
| 12314 | }, |
| 12315 | }); |
| 12316 | sema.air_extra.appendSliceAssumeCapacity(cases_extra.items); |
| 12317 | |
| 12318 | // Set up block body |
| 12319 | switch (operand) { |
| 12320 | .simple => |s| { |
| 12321 | const air_datas = sema.air_instructions.items(.data); |
| 12322 | air_datas[switch_br_inst].pl_op.operand = s.cond; |
| 12323 | air_datas[@intFromEnum(capture_block_inst)].ty_pl.payload = |
| 12324 | sema.addExtraAssumeCapacity(Air.Block{ .body_len = 1 }); |
| 12325 | sema.air_extra.appendAssumeCapacity(switch_br_inst); |
| 12326 | }, |
| 12327 | .loop => { |
| 12328 | // The block must first extract the tag from the loaded union. |
| 12329 | const tag_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len); |
| 12330 | try sema.air_instructions.append(sema.gpa, .{ |
| 12331 | .tag = .get_union_tag, |
| 12332 | .data = .{ .ty_op = .{ |
| 12333 | .ty = .fromIntern(union_obj.enum_tag_type), |
| 12334 | .operand = loaded_operand, |
| 12335 | } }, |
| 12336 | }); |
| 12337 | const air_datas = sema.air_instructions.items(.data); |
| 12338 | air_datas[switch_br_inst].pl_op.operand = tag_inst.toRef(); |
| 12339 | air_datas[@intFromEnum(capture_block_inst)].ty_pl.payload = |
| 12340 | sema.addExtraAssumeCapacity(Air.Block{ .body_len = 2 }); |
| 12341 | sema.air_extra.appendAssumeCapacity(@intFromEnum(tag_inst)); |
| 12342 | sema.air_extra.appendAssumeCapacity(switch_br_inst); |
| 12283 | 12343 | }, |
| 12284 | | .has_ranges => {}, |
| 12285 | 12344 | } |
| 12286 | | return operand_val; |
| 12345 | |
| 12346 | return capture_block_inst.toRef(); |
| 12287 | 12347 | } |
| 12288 | 12348 | |
| 12289 | 12349 | const ResolvedSwitchItem = struct { |