| ... | ... | @@ -10216,7 +10216,7 @@ fn analyzeSwitchBlock( |
| 10216 | 10216 | |
| 10217 | 10217 | const case_vals = validated_switch.case_vals; |
| 10218 | 10218 | |
| 10219 | | const index, const body, const capture, const has_tag_capture, const is_inline, const is_special = find_prong: { |
| 10219 | const body, const capture, const has_tag_capture = find_prong: { |
| 10220 | 10220 | var case_val_idx: usize = 0; |
| 10221 | 10221 | var case_it = zir_switch.iterateCases(); |
| 10222 | 10222 | var extra_index = zir_switch.end; |
| ... | ... | @@ -10239,12 +10239,12 @@ fn analyzeSwitchBlock( |
| 10239 | 10239 | } |
| 10240 | 10240 | continue; |
| 10241 | 10241 | } |
| 10242 | | break :find_prong .{ case.index, prong_body, prong_info.capture, prong_info.has_tag_capture, prong_info.is_inline, false }; |
| 10242 | break :find_prong .{ prong_body, prong_info.capture, prong_info.has_tag_capture }; |
| 10243 | 10243 | } |
| 10244 | 10244 | if (has_else) { |
| 10245 | 10245 | // This *has* to be checked after iterating all regular cases because |
| 10246 | 10246 | // we allow simple noreturn else prongs when switching on error sets! |
| 10247 | | break :find_prong .{ else_case.index, else_case.body, else_case.capture, else_case.has_tag_capture, else_case.is_inline, true }; |
| 10247 | break :find_prong .{ else_case.body, else_case.capture, else_case.has_tag_capture }; |
| 10248 | 10248 | } |
| 10249 | 10249 | unreachable; // malformed validated switch |
| 10250 | 10250 | }; |
| ... | ... | @@ -10255,58 +10255,33 @@ fn analyzeSwitchBlock( |
| 10255 | 10255 | if (!(err_set and |
| 10256 | 10256 | try sema.maybeErrorUnwrap(&case_block, body, cond_ref, operand_src, true))) |
| 10257 | 10257 | { |
| 10258 | | // Set up captures manually to avoid special cases in the main logic. |
| 10259 | | const payload_inst: Zir.Inst.Index = if (capture != .none) inst: { |
| 10258 | const payload_inst = if (capture != .none) inst: { |
| 10260 | 10259 | const payload_inst = zir_switch.payload_capture_placeholder.unwrap() orelse switch_inst; |
| 10261 | 10260 | const payload_ref: Air.Inst.Ref = payload_ref: { |
| 10262 | | const item_val: Value = item_val: { |
| 10261 | const captured_opv: Value = captured_opv: { |
| 10263 | 10262 | if (!tagged_union_originally) { |
| 10264 | | break :item_val item_opv; |
| 10263 | break :captured_opv item_opv; |
| 10265 | 10264 | } |
| 10266 | 10265 | if (maybe_operand_opv) |operand_opv| { |
| 10267 | | break :item_val .fromInterned(zcu.intern_pool.indexToKey(operand_opv.toIntern()).un.val); |
| 10266 | break :captured_opv .fromInterned(zcu.intern_pool.indexToKey(operand_opv.toIntern()).un.val); |
| 10268 | 10267 | } |
| 10269 | 10268 | assert(zir_switch.any_maybe_runtime_capture); // there's a payload capture |
| 10270 | | const operand_val, const operand_ref = switch (operand) { |
| 10271 | | .simple => unreachable, |
| 10272 | | .loop => |l| load_operand: { |
| 10273 | | const loaded = try sema.analyzeLoad(block, src, l.operand_alloc, src); |
| 10274 | | if (l.operand_is_ref) { |
| 10275 | | const by_val = try sema.analyzeLoad(block, src, loaded, src); |
| 10276 | | break :load_operand .{ by_val, loaded }; |
| 10277 | | } else { |
| 10278 | | break :load_operand .{ loaded, .none }; |
| 10279 | | } |
| 10280 | | }, |
| 10281 | | }; |
| 10282 | | const prong_kind: SwitchProngKind = kind: { |
| 10283 | | if (is_inline) break :kind .{ .inline_ref = .fromValue(item_opv) }; |
| 10284 | | if (is_special) break :kind .special; |
| 10285 | | break :kind .{ .item_refs = &.{.fromValue(item_opv)} }; |
| 10286 | | }; |
| 10287 | | break :payload_ref try sema.analyzeSwitchPayloadCapture( |
| 10269 | const loaded_operand = try sema.analyzeSwitchOperandLoad(&case_block, operand, operand_src, capture == .by_ref); |
| 10270 | break :payload_ref try sema.resolveSwitchPayloadCaptureTaggedUnion( |
| 10288 | 10271 | &case_block, |
| 10289 | | operand, |
| 10290 | | operand_val, |
| 10291 | | operand_ref, |
| 10292 | | operand_ty, |
| 10272 | loaded_operand, |
| 10293 | 10273 | operand_src, |
| 10294 | | block.src(.{ .switch_capture = .{ |
| 10295 | | .switch_node_offset = src_node_offset, |
| 10296 | | .case_idx = index, |
| 10297 | | } }), |
| 10274 | operand_ty, |
| 10275 | item_opv, |
| 10298 | 10276 | capture == .by_ref, |
| 10299 | | prong_kind, |
| 10300 | | validated_switch.else_err_ty, |
| 10301 | 10277 | ); |
| 10302 | 10278 | }; |
| 10303 | 10279 | break :payload_ref switch (capture) { |
| 10304 | | .by_val => .fromValue(item_val), |
| 10305 | | .by_ref => try sema.uavRef(item_val), |
| 10280 | .by_val => .fromValue(captured_opv), |
| 10281 | .by_ref => try sema.uavRef(captured_opv), |
| 10306 | 10282 | .none => unreachable, |
| 10307 | 10283 | }; |
| 10308 | 10284 | }; |
| 10309 | | assert(!sema.typeOf(payload_ref).isNoReturn(sema.pt.zcu)); |
| 10310 | 10285 | sema.inst_map.putAssumeCapacity(payload_inst, payload_ref); |
| 10311 | 10286 | break :inst payload_inst; |
| 10312 | 10287 | } else undefined; |
| ... | ... | @@ -10599,7 +10574,7 @@ fn finishSwitchBr( |
| 10599 | 10574 | } }), |
| 10600 | 10575 | prong_info.capture, |
| 10601 | 10576 | prong_info.has_tag_capture, |
| 10602 | | .{ .inline_ref = item_ref }, |
| 10577 | .{ .@"inline" = item_ref }, |
| 10603 | 10578 | validated_switch.else_err_ty, |
| 10604 | 10579 | switch_inst, |
| 10605 | 10580 | zir_switch, |
| ... | ... | @@ -10689,7 +10664,7 @@ fn finishSwitchBr( |
| 10689 | 10664 | } }), |
| 10690 | 10665 | prong_info.capture, |
| 10691 | 10666 | prong_info.has_tag_capture, |
| 10692 | | .{ .inline_ref = item_ref }, |
| 10667 | .{ .@"inline" = item_ref }, |
| 10693 | 10668 | validated_switch.else_err_ty, |
| 10694 | 10669 | switch_inst, |
| 10695 | 10670 | zir_switch, |
| ... | ... | @@ -10829,7 +10804,7 @@ fn finishSwitchBr( |
| 10829 | 10804 | } }), |
| 10830 | 10805 | else_case.capture, |
| 10831 | 10806 | else_case.has_tag_capture, |
| 10832 | | .{ .inline_ref = item_ref }, |
| 10807 | .{ .@"inline" = item_ref }, |
| 10833 | 10808 | validated_switch.else_err_ty, |
| 10834 | 10809 | switch_inst, |
| 10835 | 10810 | zir_switch, |
| ... | ... | @@ -11269,8 +11244,7 @@ fn validateSwitchBlock( |
| 11269 | 11244 | const has_else = zir_switch.else_case != null; |
| 11270 | 11245 | const has_under = zir_switch.has_under; |
| 11271 | 11246 | |
| 11272 | | var case_vals: std.ArrayList(Air.Inst.Ref) = .empty; |
| 11273 | | try case_vals.ensureUnusedCapacity(arena, zir_switch.item_infos.len); |
| 11247 | var case_vals: std.ArrayList(Air.Inst.Ref) = try .initCapacity(arena, zir_switch.item_infos.len); |
| 11274 | 11248 | |
| 11275 | 11249 | // Duplicate checking variables later also used for `inline else`. |
| 11276 | 11250 | var seen_enum_fields: []?LazySrcLoc = &.{}; |
| ... | ... | @@ -11676,10 +11650,10 @@ fn resolveSwitchBlock( |
| 11676 | 11650 | // This prong should be unreachable! |
| 11677 | 11651 | return .unreachable_value; |
| 11678 | 11652 | } |
| 11679 | | const prong_kind: SwitchProngKind = kind: { |
| 11680 | | if (prong_info.is_inline) break :kind .{ .inline_ref = cond_ref }; |
| 11681 | | if (range_refs.len > 0) break :kind .has_ranges; |
| 11682 | | break :kind .{ .item_refs = item_refs }; |
| 11653 | const prong_items: SwitchProngItems = prong_items: { |
| 11654 | if (prong_info.is_inline) break :prong_items .{ .@"inline" = cond_ref }; |
| 11655 | if (range_refs.len > 0) break :prong_items .has_ranges; |
| 11656 | break :prong_items .{ .item_refs = item_refs }; |
| 11683 | 11657 | }; |
| 11684 | 11658 | return sema.resolveSwitchProng( |
| 11685 | 11659 | block, |
| ... | ... | @@ -11693,7 +11667,7 @@ fn resolveSwitchBlock( |
| 11693 | 11667 | } }), |
| 11694 | 11668 | prong_info.capture, |
| 11695 | 11669 | prong_info.has_tag_capture, |
| 11696 | | prong_kind, |
| 11670 | prong_items, |
| 11697 | 11671 | validated_switch.else_err_ty, |
| 11698 | 11672 | merges, |
| 11699 | 11673 | switch_inst, |
| ... | ... | @@ -11707,8 +11681,8 @@ fn resolveSwitchBlock( |
| 11707 | 11681 | if ((try sema.compareAll(cond_val, .gte, first_val, item_ty)) and |
| 11708 | 11682 | (try sema.compareAll(cond_val, .lte, last_val, item_ty))) |
| 11709 | 11683 | { |
| 11710 | | const prong_kind: SwitchProngKind = if (prong_info.is_inline) |
| 11711 | | .{ .inline_ref = cond_ref } |
| 11684 | const prong_items: SwitchProngItems = if (prong_info.is_inline) |
| 11685 | .{ .@"inline" = cond_ref } |
| 11712 | 11686 | else |
| 11713 | 11687 | .has_ranges; |
| 11714 | 11688 | return sema.resolveSwitchProng( |
| ... | ... | @@ -11723,7 +11697,7 @@ fn resolveSwitchBlock( |
| 11723 | 11697 | } }), |
| 11724 | 11698 | prong_info.capture, |
| 11725 | 11699 | prong_info.has_tag_capture, |
| 11726 | | prong_kind, |
| 11700 | prong_items, |
| 11727 | 11701 | validated_switch.else_err_ty, |
| 11728 | 11702 | merges, |
| 11729 | 11703 | switch_inst, |
| ... | ... | @@ -11742,8 +11716,8 @@ fn resolveSwitchBlock( |
| 11742 | 11716 | |
| 11743 | 11717 | if (else_is_named_only and item_ty.enumTagFieldIndex(cond_val, zcu) != null) { |
| 11744 | 11718 | assert(item_ty.isNonexhaustiveEnum(zcu)); |
| 11745 | | const prong_kind: SwitchProngKind = if (else_case.is_inline) |
| 11746 | | .{ .inline_ref = cond_ref } |
| 11719 | const prong_items: SwitchProngItems = if (else_case.is_inline) |
| 11720 | .{ .@"inline" = cond_ref } |
| 11747 | 11721 | else |
| 11748 | 11722 | .special; |
| 11749 | 11723 | return sema.resolveSwitchProng( |
| ... | ... | @@ -11758,7 +11732,7 @@ fn resolveSwitchBlock( |
| 11758 | 11732 | } }), |
| 11759 | 11733 | else_case.capture, |
| 11760 | 11734 | else_case.has_tag_capture, |
| 11761 | | prong_kind, |
| 11735 | prong_items, |
| 11762 | 11736 | validated_switch.else_err_ty, |
| 11763 | 11737 | merges, |
| 11764 | 11738 | switch_inst, |
| ... | ... | @@ -11782,8 +11756,8 @@ fn resolveSwitchBlock( |
| 11782 | 11756 | return .unreachable_value; |
| 11783 | 11757 | } |
| 11784 | 11758 | } |
| 11785 | | const prong_kind: SwitchProngKind = if (is_inline) |
| 11786 | | .{ .inline_ref = cond_ref } |
| 11759 | const prong_items: SwitchProngItems = if (is_inline) |
| 11760 | .{ .@"inline" = cond_ref } |
| 11787 | 11761 | else |
| 11788 | 11762 | .special; |
| 11789 | 11763 | return sema.resolveSwitchProng( |
| ... | ... | @@ -11798,7 +11772,7 @@ fn resolveSwitchBlock( |
| 11798 | 11772 | } }), |
| 11799 | 11773 | capture, |
| 11800 | 11774 | has_tag_capture, |
| 11801 | | prong_kind, |
| 11775 | prong_items, |
| 11802 | 11776 | validated_switch.else_err_ty, |
| 11803 | 11777 | merges, |
| 11804 | 11778 | switch_inst, |
| ... | ... | @@ -11834,14 +11808,59 @@ const SwitchOperand = union(enum) { |
| 11834 | 11808 | }, |
| 11835 | 11809 | }; |
| 11836 | 11810 | |
| 11837 | | const SwitchProngKind = union(enum) { |
| 11838 | | /// Prefer populating this field over the others, if possible. |
| 11839 | | inline_ref: Air.Inst.Ref, |
| 11811 | fn analyzeSwitchOperandLoad( |
| 11812 | sema: *Sema, |
| 11813 | block: *Block, |
| 11814 | operand: SwitchOperand, |
| 11815 | operand_src: LazySrcLoc, |
| 11816 | by_ref: bool, |
| 11817 | ) CompileError!Air.Inst.Ref { |
| 11818 | switch (operand) { |
| 11819 | .simple => |s| { |
| 11820 | if (by_ref) { |
| 11821 | assert(s.by_ref != .none); |
| 11822 | return s.by_ref; |
| 11823 | } else { |
| 11824 | return s.by_val; |
| 11825 | } |
| 11826 | }, |
| 11827 | .loop => |l| { |
| 11828 | const loaded = try sema.analyzeLoad(block, operand_src, l.operand_alloc, operand_src); |
| 11829 | assert(loaded != .none); // there are no captures, so no need to load the switch operand |
| 11830 | if (by_ref) { |
| 11831 | assert(l.operand_is_ref); |
| 11832 | return loaded; |
| 11833 | } |
| 11834 | return if (l.operand_is_ref) |
| 11835 | try sema.analyzeLoad(block, operand_src, loaded, operand_src) |
| 11836 | else |
| 11837 | loaded; |
| 11838 | }, |
| 11839 | } |
| 11840 | } |
| 11841 | |
| 11842 | const SwitchProngItems = union(enum) { |
| 11843 | @"inline": Air.Inst.Ref, |
| 11840 | 11844 | item_refs: []const Air.Inst.Ref, |
| 11841 | 11845 | has_ranges, |
| 11842 | 11846 | special, |
| 11843 | 11847 | }; |
| 11844 | 11848 | |
| 11849 | /// A switch capture is comptime-known if it is `inline` and/or it is a by-value |
| 11850 | /// capture of a prong with a single item. |
| 11851 | fn resolveSwitchCaptureFromProngItems( |
| 11852 | sema: *Sema, |
| 11853 | prong_items: SwitchProngItems, |
| 11854 | by_ref: bool, |
| 11855 | ) ?Value { |
| 11856 | const ref: Air.Inst.Ref = switch (prong_items) { |
| 11857 | .@"inline" => |ref| ref, |
| 11858 | .item_refs => |refs| if (refs.len == 1 and !by_ref) refs[0] else return null, |
| 11859 | .has_ranges, .special => return null, |
| 11860 | }; |
| 11861 | return sema.resolveValue(ref).?; |
| 11862 | } |
| 11863 | |
| 11845 | 11864 | /// Resolve a switch prong which is determined at comptime to have no peers. |
| 11846 | 11865 | /// Sets up captures as needed. Uses `analyzeBodyRuntimeBreak`. |
| 11847 | 11866 | fn resolveSwitchProng( |
| ... | ... | @@ -11855,7 +11874,7 @@ fn resolveSwitchProng( |
| 11855 | 11874 | capture_src: LazySrcLoc, |
| 11856 | 11875 | capture: Zir.Inst.SwitchBlock.ProngInfo.Capture, |
| 11857 | 11876 | has_tag_capture: bool, |
| 11858 | | kind: SwitchProngKind, |
| 11877 | prong_items: SwitchProngItems, |
| 11859 | 11878 | else_err_ty: ?Type, |
| 11860 | 11879 | merges: *Block.Merges, |
| 11861 | 11880 | switch_inst: Zir.Inst.Index, |
| ... | ... | @@ -11870,36 +11889,28 @@ fn resolveSwitchProng( |
| 11870 | 11889 | const parent_hint = sema.branch_hint; |
| 11871 | 11890 | defer sema.branch_hint = parent_hint orelse if (sema.branch_hint == .cold) .cold else null; |
| 11872 | 11891 | |
| 11873 | | const payload_inst: Zir.Inst.Index = if (capture != .none) inst: { |
| 11892 | const analyzed_captures = try sema.analyzeSwitchCaptures( |
| 11893 | child_block, |
| 11894 | operand, |
| 11895 | operand_src, |
| 11896 | sema.typeOf(operand.simple.by_val), |
| 11897 | capture_src, |
| 11898 | capture, |
| 11899 | has_tag_capture, |
| 11900 | prong_items, |
| 11901 | else_err_ty, |
| 11902 | ); |
| 11903 | |
| 11904 | const payload_inst = if (capture != .none) inst: { |
| 11874 | 11905 | const payload_inst = zir_switch.payload_capture_placeholder.unwrap() orelse switch_inst; |
| 11875 | | const payload_ref = try sema.analyzeSwitchPayloadCapture( |
| 11876 | | child_block, |
| 11877 | | operand, |
| 11878 | | operand.simple.by_val, |
| 11879 | | operand.simple.by_ref, |
| 11880 | | sema.typeOf(operand.simple.by_val), |
| 11881 | | operand_src, |
| 11882 | | capture_src, |
| 11883 | | capture == .by_ref, |
| 11884 | | kind, |
| 11885 | | else_err_ty, |
| 11886 | | ); |
| 11887 | | assert(!sema.typeOf(payload_ref).isNoReturn(sema.pt.zcu)); |
| 11888 | | sema.inst_map.putAssumeCapacity(payload_inst, payload_ref); |
| 11906 | sema.inst_map.putAssumeCapacity(payload_inst, analyzed_captures.payload_ref); |
| 11889 | 11907 | break :inst payload_inst; |
| 11890 | 11908 | } else undefined; |
| 11891 | 11909 | defer if (capture != .none) assert(sema.inst_map.remove(payload_inst)); |
| 11892 | 11910 | |
| 11893 | 11911 | const tag_inst: Zir.Inst.Index = if (has_tag_capture) inst: { |
| 11894 | 11912 | const tag_inst = zir_switch.tag_capture_placeholder.unwrap() orelse switch_inst; |
| 11895 | | const tag_ref = try sema.analyzeSwitchTagCapture( |
| 11896 | | child_block, |
| 11897 | | operand.simple.by_val, |
| 11898 | | sema.typeOf(operand.simple.by_val), |
| 11899 | | capture_src, |
| 11900 | | kind, |
| 11901 | | ); |
| 11902 | | sema.inst_map.putAssumeCapacity(tag_inst, tag_ref); |
| 11913 | sema.inst_map.putAssumeCapacity(tag_inst, analyzed_captures.tag_ref); |
| 11903 | 11914 | break :inst tag_inst; |
| 11904 | 11915 | } else undefined; |
| 11905 | 11916 | defer if (has_tag_capture) assert(sema.inst_map.remove(tag_inst)); |
| ... | ... | @@ -11945,7 +11956,7 @@ fn analyzeSwitchProng( |
| 11945 | 11956 | capture_src: LazySrcLoc, |
| 11946 | 11957 | capture: Zir.Inst.SwitchBlock.ProngInfo.Capture, |
| 11947 | 11958 | has_tag_capture: bool, |
| 11948 | | kind: SwitchProngKind, |
| 11959 | prong_items: SwitchProngItems, |
| 11949 | 11960 | else_err_ty: ?Type, |
| 11950 | 11961 | switch_inst: Zir.Inst.Index, |
| 11951 | 11962 | zir_switch: *const Zir.UnwrappedSwitchBlock, |
| ... | ... | @@ -11966,77 +11977,28 @@ fn analyzeSwitchProng( |
| 11966 | 11977 | } |
| 11967 | 11978 | } |
| 11968 | 11979 | |
| 11969 | | const need_load: bool = need_load: { |
| 11970 | | if (capture == .none and !has_tag_capture) { |
| 11971 | | // No need to load the operand for this prong! |
| 11972 | | break :need_load false; |
| 11973 | | } |
| 11974 | | if (capture != .none and operand_ty.zigTypeTag(zcu) == .@"union" and |
| 11975 | | operand_ty.containerLayout(zcu) != .@"packed") |
| 11976 | | { |
| 11977 | | // Non-OPV tagged union payload captures are always runtime-known. |
| 11978 | | break :need_load true; |
| 11979 | | } |
| 11980 | | if (kind == .inline_ref) { |
| 11981 | | // `inline_ref` *is* the (comptime-known) capture. |
| 11982 | | break :need_load false; |
| 11983 | | } |
| 11984 | | assert(zir_switch.any_maybe_runtime_capture); // should have caught everything else by now |
| 11985 | | if (capture != .by_ref and |
| 11986 | | kind == .item_refs and kind.item_refs.len == 1) |
| 11987 | | { |
| 11988 | | // Capture is comptime-known because it's the only prong item |
| 11989 | | break :need_load false; |
| 11990 | | } |
| 11991 | | break :need_load true; |
| 11992 | | }; |
| 11993 | | |
| 11994 | | const operand_val: Air.Inst.Ref, const operand_ptr: Air.Inst.Ref = load_operand: { |
| 11995 | | if (!need_load) break :load_operand .{ .none, .none }; |
| 11996 | | switch (operand) { |
| 11997 | | .simple => |s| break :load_operand .{ s.by_val, s.by_ref }, |
| 11998 | | .loop => |l| { |
| 11999 | | const loaded = try sema.analyzeLoad(case_block, operand_src, l.operand_alloc, operand_src); |
| 12000 | | if (l.operand_is_ref) { |
| 12001 | | const by_val = try sema.analyzeLoad(case_block, operand_src, loaded, operand_src); |
| 12002 | | break :load_operand .{ by_val, loaded }; |
| 12003 | | } else { |
| 12004 | | break :load_operand .{ loaded, .none }; |
| 12005 | | } |
| 12006 | | }, |
| 12007 | | } |
| 12008 | | }; |
| 11980 | const analyzed_captures = try sema.analyzeSwitchCaptures( |
| 11981 | case_block, |
| 11982 | operand, |
| 11983 | operand_src, |
| 11984 | operand_ty, |
| 11985 | capture_src, |
| 11986 | capture, |
| 11987 | has_tag_capture, |
| 11988 | prong_items, |
| 11989 | else_err_ty, |
| 11990 | ); |
| 12009 | 11991 | |
| 12010 | | const payload_inst: Zir.Inst.Index = if (capture != .none) inst: { |
| 11992 | const payload_inst = if (capture != .none) inst: { |
| 12011 | 11993 | const payload_inst = zir_switch.payload_capture_placeholder.unwrap() orelse switch_inst; |
| 12012 | | const payload_ref = try sema.analyzeSwitchPayloadCapture( |
| 12013 | | case_block, |
| 12014 | | operand, |
| 12015 | | operand_val, |
| 12016 | | operand_ptr, |
| 12017 | | operand_ty, |
| 12018 | | operand_src, |
| 12019 | | capture_src, |
| 12020 | | capture == .by_ref, |
| 12021 | | kind, |
| 12022 | | else_err_ty, |
| 12023 | | ); |
| 12024 | | assert(!sema.typeOf(payload_ref).isNoReturn(sema.pt.zcu)); |
| 12025 | | sema.inst_map.putAssumeCapacity(payload_inst, payload_ref); |
| 11994 | sema.inst_map.putAssumeCapacity(payload_inst, analyzed_captures.payload_ref); |
| 12026 | 11995 | break :inst payload_inst; |
| 12027 | 11996 | } else undefined; |
| 12028 | 11997 | defer if (capture != .none) assert(sema.inst_map.remove(payload_inst)); |
| 12029 | 11998 | |
| 12030 | 11999 | const tag_inst: Zir.Inst.Index = if (has_tag_capture) inst: { |
| 12031 | 12000 | const tag_inst = zir_switch.tag_capture_placeholder.unwrap() orelse switch_inst; |
| 12032 | | const tag_ref = try sema.analyzeSwitchTagCapture( |
| 12033 | | case_block, |
| 12034 | | operand_val, |
| 12035 | | operand_ty, |
| 12036 | | capture_src, |
| 12037 | | kind, |
| 12038 | | ); |
| 12039 | | sema.inst_map.putAssumeCapacity(tag_inst, tag_ref); |
| 12001 | sema.inst_map.putAssumeCapacity(tag_inst, analyzed_captures.tag_ref); |
| 12040 | 12002 | break :inst tag_inst; |
| 12041 | 12003 | } else undefined; |
| 12042 | 12004 | defer if (has_tag_capture) assert(sema.inst_map.remove(tag_inst)); |
| ... | ... | @@ -12047,157 +12009,314 @@ fn analyzeSwitchProng( |
| 12047 | 12009 | return sema.analyzeBodyRuntimeBreak(case_block, prong_body); |
| 12048 | 12010 | } |
| 12049 | 12011 | |
| 12050 | | fn analyzeSwitchTagCapture( |
| 12012 | fn analyzeSwitchCaptures( |
| 12051 | 12013 | sema: *Sema, |
| 12052 | 12014 | case_block: *Block, |
| 12053 | | /// May be `none` if this is an inline capture or if `kind.item_refs.len == 1`. |
| 12054 | | operand_val: Air.Inst.Ref, |
| 12015 | operand: SwitchOperand, |
| 12016 | operand_src: LazySrcLoc, |
| 12055 | 12017 | operand_ty: Type, |
| 12056 | 12018 | capture_src: LazySrcLoc, |
| 12057 | | kind: SwitchProngKind, |
| 12058 | | ) CompileError!Air.Inst.Ref { |
| 12019 | capture: Zir.Inst.SwitchBlock.ProngInfo.Capture, |
| 12020 | has_tag_capture: bool, |
| 12021 | prong_items: SwitchProngItems, |
| 12022 | else_err_ty: ?Type, |
| 12023 | ) CompileError!struct { |
| 12024 | payload_ref: Air.Inst.Ref, |
| 12025 | tag_ref: Air.Inst.Ref, |
| 12026 | } { |
| 12059 | 12027 | const pt = sema.pt; |
| 12060 | 12028 | const zcu = pt.zcu; |
| 12061 | 12029 | |
| 12062 | | const tag_capture_src: LazySrcLoc = .{ |
| 12063 | | .base_node_inst = capture_src.base_node_inst, |
| 12064 | | .offset = .{ .switch_tag_capture = capture_src.offset.switch_capture }, |
| 12030 | if (operand_ty.zigTypeTag(zcu) == .@"union" and |
| 12031 | operand_ty.containerLayout(zcu) != .@"packed") |
| 12032 | { |
| 12033 | if (capture == .none) { |
| 12034 | const tag_ref: Air.Inst.Ref = tag_ref: { |
| 12035 | if (!has_tag_capture) break :tag_ref .none; |
| 12036 | if (sema.resolveSwitchCaptureFromProngItems(prong_items, false)) |tag_val| { |
| 12037 | break :tag_ref .fromValue(tag_val); |
| 12038 | } |
| 12039 | const loaded_operand = try sema.analyzeSwitchOperandLoad(case_block, operand, operand_src, false); |
| 12040 | break :tag_ref try sema.unionToTag(case_block, loaded_operand); |
| 12041 | }; |
| 12042 | return .{ .payload_ref = .none, .tag_ref = tag_ref }; |
| 12043 | } |
| 12044 | |
| 12045 | // We always have to load the operand for tagged union payload captures |
| 12046 | // since we can't derive the payload value from the tag (except for OPV |
| 12047 | // types, for which the load is always basically a noop anyway). |
| 12048 | |
| 12049 | const loaded_operand = try sema.analyzeSwitchOperandLoad(case_block, operand, operand_src, capture == .by_ref); |
| 12050 | |
| 12051 | if (sema.resolveSwitchCaptureFromProngItems(prong_items, capture == .by_ref)) |tag_val| { |
| 12052 | const payload_ref = try sema.resolveSwitchPayloadCaptureTaggedUnion( |
| 12053 | case_block, |
| 12054 | loaded_operand, |
| 12055 | operand_src, |
| 12056 | operand_ty, |
| 12057 | tag_val, |
| 12058 | capture == .by_ref, |
| 12059 | ); |
| 12060 | const tag_ref: Air.Inst.Ref = if (has_tag_capture) .fromValue(tag_val) else .none; |
| 12061 | return .{ .payload_ref = payload_ref, .tag_ref = tag_ref }; |
| 12062 | } |
| 12063 | |
| 12064 | const payload_ref = try sema.analyzeSwitchPayloadCaptureTaggedUnion( |
| 12065 | case_block, |
| 12066 | operand, |
| 12067 | loaded_operand, |
| 12068 | operand_src, |
| 12069 | operand_ty, |
| 12070 | capture == .by_ref, |
| 12071 | capture_src, |
| 12072 | prong_items, |
| 12073 | ); |
| 12074 | |
| 12075 | const tag_ref: Air.Inst.Ref = tag_ref: { |
| 12076 | if (!has_tag_capture) break :tag_ref .none; |
| 12077 | const operand_val = switch (capture) { |
| 12078 | .none => unreachable, // handled above |
| 12079 | .by_val => loaded_operand, |
| 12080 | .by_ref => try sema.analyzeLoad(case_block, operand_src, loaded_operand, operand_src), |
| 12081 | }; |
| 12082 | break :tag_ref try sema.unionToTag(case_block, operand_val); |
| 12083 | }; |
| 12084 | |
| 12085 | assert(!sema.typeOf(payload_ref).isNoReturn(zcu)); |
| 12086 | return .{ .payload_ref = payload_ref, .tag_ref = tag_ref }; |
| 12087 | } |
| 12088 | |
| 12089 | const payload_ref: Air.Inst.Ref = payload_ref: { |
| 12090 | if (capture == .none) break :payload_ref .none; |
| 12091 | |
| 12092 | if (operand_ty.zigTypeTag(zcu) == .error_set) { |
| 12093 | // Error captures need to have their type narrowed! |
| 12094 | |
| 12095 | if (capture == .by_ref) { |
| 12096 | return sema.fail( |
| 12097 | case_block, |
| 12098 | capture_src, |
| 12099 | "error set cannot be captured by reference", |
| 12100 | .{}, |
| 12101 | ); |
| 12102 | } |
| 12103 | assert(capture == .by_val); |
| 12104 | |
| 12105 | if (sema.resolveSwitchCaptureFromProngItems(prong_items, false)) |err_val| { |
| 12106 | const err_name = err_val.getErrorName(zcu).unwrap().?; |
| 12107 | break :payload_ref .fromIntern((try pt.intern(.{ .err = .{ |
| 12108 | .ty = (try pt.singleErrorSetType(err_name)).toIntern(), |
| 12109 | .name = err_name, |
| 12110 | } }))); |
| 12111 | } |
| 12112 | |
| 12113 | const loaded_operand = try sema.analyzeSwitchOperandLoad(case_block, operand, operand_src, false); |
| 12114 | |
| 12115 | switch (prong_items) { |
| 12116 | .@"inline" => unreachable, // handled above |
| 12117 | .has_ranges => unreachable, // not possible for error set |
| 12118 | .special => { |
| 12119 | if (else_err_ty) |err_ty| { |
| 12120 | break :payload_ref try sema.bitCast(case_block, err_ty, loaded_operand, operand_src, null); |
| 12121 | } else { |
| 12122 | try sema.analyzeUnreachable(case_block, operand_src, false); |
| 12123 | break :payload_ref .unreachable_value; |
| 12124 | } |
| 12125 | }, |
| 12126 | .item_refs => |item_refs| { |
| 12127 | var names: InferredErrorSet.NameMap = .{}; |
| 12128 | try names.ensureUnusedCapacity(sema.arena, item_refs.len); |
| 12129 | for (item_refs) |item_ref| { |
| 12130 | const item_val = sema.resolveValue(item_ref).?; |
| 12131 | names.putAssumeCapacityNoClobber(item_val.getErrorName(zcu).unwrap().?, {}); |
| 12132 | } |
| 12133 | const narrowed_ty = try pt.errorSetFromUnsortedNames(names.keys()); |
| 12134 | break :payload_ref try sema.bitCast(case_block, narrowed_ty, loaded_operand, operand_src, null); |
| 12135 | }, |
| 12136 | } |
| 12137 | } |
| 12138 | |
| 12139 | // We try to make the capture comptime-known based on `prong_items` first: |
| 12140 | |
| 12141 | if (sema.resolveSwitchCaptureFromProngItems(prong_items, capture == .by_ref)) |item_val| { |
| 12142 | break :payload_ref switch (capture) { |
| 12143 | .none => unreachable, // handled above |
| 12144 | .by_val => .fromValue(item_val), |
| 12145 | .by_ref => try sema.uavRef(item_val), |
| 12146 | }; |
| 12147 | } |
| 12148 | |
| 12149 | // Otherwise the capture value is just the passed-through value of the |
| 12150 | // switch condition (which we might have to load first). |
| 12151 | |
| 12152 | break :payload_ref try sema.analyzeSwitchOperandLoad(case_block, operand, operand_src, capture == .by_ref); |
| 12065 | 12153 | }; |
| 12066 | 12154 | |
| 12067 | | if (operand_ty.zigTypeTag(zcu) != .@"union") { |
| 12068 | | return sema.fail(case_block, tag_capture_src, "cannot capture tag of non-union type '{f}'", .{ |
| 12069 | | operand_ty.fmt(pt), |
| 12155 | if (has_tag_capture) { |
| 12156 | const tag_capture_src: LazySrcLoc = .{ |
| 12157 | .base_node_inst = capture_src.base_node_inst, |
| 12158 | .offset = .{ .switch_tag_capture = capture_src.offset.switch_capture }, |
| 12159 | }; |
| 12160 | if (operand_ty.zigTypeTag(zcu) == .@"union") { |
| 12161 | assert(operand_ty.containerLayout(zcu) == .@"packed"); |
| 12162 | return sema.failWithOwnedErrorMsg(case_block, msg: { |
| 12163 | const msg = try sema.errMsg(tag_capture_src, "cannot capture tag of packed union", .{}); |
| 12164 | errdefer msg.destroy(sema.gpa); |
| 12165 | try sema.addDeclaredHereNote(msg, operand_ty); |
| 12166 | if (operand_ty.srcLocOrNull(zcu)) |ty_src| { |
| 12167 | try sema.errNote(ty_src, msg, "consider using a tagged union", .{}); |
| 12168 | } |
| 12169 | break :msg msg; |
| 12170 | }); |
| 12171 | } |
| 12172 | return sema.failWithOwnedErrorMsg(case_block, msg: { |
| 12173 | const msg = try sema.errMsg(tag_capture_src, "cannot capture tag of non-union type '{f}'", .{ |
| 12174 | operand_ty.fmt(pt), |
| 12175 | }); |
| 12176 | errdefer msg.destroy(sema.gpa); |
| 12177 | try sema.addDeclaredHereNote(msg, operand_ty); |
| 12178 | break :msg msg; |
| 12070 | 12179 | }); |
| 12071 | 12180 | } |
| 12072 | | if (operand_ty.containerLayout(zcu) == .@"packed") { |
| 12073 | | return sema.fail(case_block, tag_capture_src, "cannot capture tag of packed union", .{}); |
| 12074 | | } |
| 12075 | | switch (kind) { |
| 12076 | | .has_ranges => unreachable, |
| 12077 | | .inline_ref => |ref| return ref, |
| 12078 | | .item_refs => |refs| if (refs.len == 1) return refs[0], |
| 12079 | | .special => {}, |
| 12080 | | } |
| 12081 | | return sema.unionToTag(case_block, operand_val); |
| 12181 | |
| 12182 | return .{ .payload_ref = payload_ref, .tag_ref = .none }; |
| 12082 | 12183 | } |
| 12083 | 12184 | |
| 12084 | | fn analyzeSwitchPayloadCapture( |
| 12185 | fn resolveSwitchPayloadCaptureTaggedUnion( |
| 12085 | 12186 | sema: *Sema, |
| 12086 | 12187 | case_block: *Block, |
| 12087 | | operand: SwitchOperand, |
| 12088 | | /// Always has to be not-`none` if this is a tagged union payload capture. |
| 12089 | | /// For non-tagged-union captures, this may be `none` if this is an inline |
| 12090 | | /// capture or if `kind.item_refs.len == 1` and capture is by val. |
| 12091 | | operand_val: Air.Inst.Ref, |
| 12092 | | /// May be `none` if `capture_by_ref` is `false` or if `operand_val` is also `none`. |
| 12093 | | operand_ptr: Air.Inst.Ref, |
| 12188 | loaded_operand: Air.Inst.Ref, |
| 12189 | operand_src: LazySrcLoc, |
| 12094 | 12190 | operand_ty: Type, |
| 12191 | tag_val: Value, |
| 12192 | capture_by_ref: bool, |
| 12193 | ) CompileError!Air.Inst.Ref { |
| 12194 | const pt = sema.pt; |
| 12195 | const zcu = pt.zcu; |
| 12196 | const ip = &zcu.intern_pool; |
| 12197 | |
| 12198 | const field_index: u32 = @intCast(operand_ty.unionTagFieldIndex(tag_val, zcu).?); |
| 12199 | const union_obj = zcu.typeToUnion(operand_ty).?; |
| 12200 | const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]); |
| 12201 | const payload_ref: Air.Inst.Ref = payload_ref: { |
| 12202 | if (capture_by_ref) { |
| 12203 | const operand_ptr_info = sema.typeOf(loaded_operand).ptrInfo(zcu); |
| 12204 | const ptr_field_ty = try pt.ptrType(.{ |
| 12205 | .child = field_ty.toIntern(), |
| 12206 | .flags = .{ |
| 12207 | .is_const = operand_ptr_info.flags.is_const, |
| 12208 | .is_volatile = operand_ptr_info.flags.is_volatile, |
| 12209 | .address_space = operand_ptr_info.flags.address_space, |
| 12210 | }, |
| 12211 | }); |
| 12212 | break :payload_ref try case_block.addStructFieldPtr(loaded_operand, field_index, ptr_field_ty); |
| 12213 | } |
| 12214 | if (try sema.resolveDefinedValue(case_block, operand_src, loaded_operand)) |union_val| { |
| 12215 | const tag_and_val = ip.indexToKey(union_val.toIntern()).un; |
| 12216 | break :payload_ref .fromIntern(tag_and_val.val); |
| 12217 | } |
| 12218 | if (try field_ty.onePossibleValue(pt)) |opv| break :payload_ref .fromValue(opv); |
| 12219 | break :payload_ref try case_block.addStructFieldVal(loaded_operand, field_index, field_ty); |
| 12220 | }; |
| 12221 | assert(!sema.typeOf(payload_ref).isNoReturn(zcu)); |
| 12222 | return payload_ref; |
| 12223 | } |
| 12224 | |
| 12225 | fn analyzeSwitchPayloadCaptureTaggedUnion( |
| 12226 | sema: *Sema, |
| 12227 | case_block: *Block, |
| 12228 | operand: SwitchOperand, |
| 12229 | loaded_operand: Air.Inst.Ref, |
| 12095 | 12230 | operand_src: LazySrcLoc, |
| 12096 | | capture_src: LazySrcLoc, |
| 12231 | operand_ty: Type, |
| 12097 | 12232 | capture_by_ref: bool, |
| 12098 | | kind: SwitchProngKind, |
| 12099 | | else_err_ty: ?Type, |
| 12233 | capture_src: LazySrcLoc, |
| 12234 | prong_items: SwitchProngItems, |
| 12100 | 12235 | ) CompileError!Air.Inst.Ref { |
| 12101 | 12236 | const pt = sema.pt; |
| 12102 | 12237 | const zcu = pt.zcu; |
| 12103 | 12238 | const ip = &zcu.intern_pool; |
| 12239 | const gpa = sema.gpa; |
| 12240 | |
| 12241 | const item_refs: []const Air.Inst.Ref = switch (prong_items) { |
| 12242 | .@"inline" => unreachable, // handled above |
| 12243 | .has_ranges => unreachable, // not possible for tagged union |
| 12244 | .special => return loaded_operand, |
| 12245 | .item_refs => |item_refs| item_refs, |
| 12246 | }; |
| 12104 | 12247 | |
| 12105 | 12248 | const switch_node_offset = operand_src.offset.node_offset_switch_operand; |
| 12106 | 12249 | |
| 12107 | | const tagged_union_originally = operand_ty.zigTypeTag(zcu) == .@"union" and |
| 12108 | | operand_ty.containerLayout(zcu) != .@"packed"; |
| 12109 | | const err_set = operand_ty.zigTypeTag(zcu) == .error_set; |
| 12250 | const union_obj = zcu.typeToUnion(operand_ty).?; |
| 12110 | 12251 | |
| 12111 | | if (err_set and capture_by_ref) { |
| 12112 | | return sema.fail( |
| 12113 | | case_block, |
| 12114 | | capture_src, |
| 12115 | | "error set cannot be captured by reference", |
| 12116 | | .{}, |
| 12117 | | ); |
| 12118 | | } |
| 12252 | const first_item_val = sema.resolveValue(item_refs[0]).?; |
| 12253 | const first_field_index: u32 = zcu.unionTagFieldIndex(union_obj, first_item_val).?; |
| 12254 | const first_field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[first_field_index]); |
| 12119 | 12255 | |
| 12120 | | if (kind == .inline_ref) { |
| 12121 | | const item_val = sema.resolveValue(kind.inline_ref).?; |
| 12122 | | if (tagged_union_originally) { |
| 12123 | | const field_index: u32 = @intCast(operand_ty.unionTagFieldIndex(item_val, zcu).?); |
| 12124 | | const union_obj = zcu.typeToUnion(operand_ty).?; |
| 12125 | | const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]); |
| 12126 | | if (capture_by_ref) { |
| 12127 | | const operand_ptr_info = sema.typeOf(operand_ptr).ptrInfo(zcu); |
| 12128 | | const ptr_field_ty = try pt.ptrType(.{ |
| 12129 | | .child = field_ty.toIntern(), |
| 12130 | | .flags = .{ |
| 12131 | | .is_const = operand_ptr_info.flags.is_const, |
| 12132 | | .is_volatile = operand_ptr_info.flags.is_volatile, |
| 12133 | | .address_space = operand_ptr_info.flags.address_space, |
| 12134 | | }, |
| 12135 | | }); |
| 12136 | | return case_block.addStructFieldPtr(operand_ptr, field_index, ptr_field_ty); |
| 12137 | | } else { |
| 12138 | | if (try sema.resolveDefinedValue(case_block, operand_src, operand_val)) |union_val| { |
| 12139 | | const tag_and_val = ip.indexToKey(union_val.toIntern()).un; |
| 12140 | | return .fromIntern(tag_and_val.val); |
| 12141 | | } |
| 12142 | | if (try field_ty.onePossibleValue(pt)) |opv| return .fromValue(opv); |
| 12143 | | return case_block.addStructFieldVal(operand_val, field_index, field_ty); |
| 12144 | | } |
| 12145 | | } else if (capture_by_ref) { |
| 12146 | | return sema.uavRef(item_val); |
| 12147 | | } else { |
| 12148 | | return kind.inline_ref; |
| 12149 | | } |
| 12150 | | } |
| 12151 | | |
| 12152 | | if (kind == .special) { |
| 12153 | | if (err_set) { |
| 12154 | | if (else_err_ty) |err_ty| { |
| 12155 | | return sema.bitCast(case_block, err_ty, operand_val, operand_src, null); |
| 12156 | | } else { |
| 12157 | | try sema.analyzeUnreachable(case_block, operand_src, false); |
| 12158 | | return .unreachable_value; |
| 12159 | | } |
| 12160 | | } |
| 12161 | | if (capture_by_ref) { |
| 12162 | | return operand_ptr; |
| 12163 | | } |
| 12164 | | return operand_val; |
| 12256 | const field_indices = try sema.arena.alloc(u32, item_refs.len); |
| 12257 | for (item_refs, field_indices) |item_ref, *field_idx| { |
| 12258 | const item_val = sema.resolveValue(item_ref).?; |
| 12259 | field_idx.* = zcu.unionTagFieldIndex(union_obj, item_val).?; |
| 12165 | 12260 | } |
| 12166 | 12261 | |
| 12167 | | if (tagged_union_originally) { |
| 12168 | | const case_vals = kind.item_refs; |
| 12169 | | |
| 12170 | | const union_obj = zcu.typeToUnion(operand_ty).?; |
| 12171 | | const first_item_val = sema.resolveValue(case_vals[0]).?; |
| 12262 | // Fast path: if all the operands are the same type already, we don't need to hit |
| 12263 | // PTR! This will also allow us to emit simpler code. |
| 12264 | const same_types = for (field_indices[1..]) |field_idx| { |
| 12265 | const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]); |
| 12266 | if (!field_ty.eql(first_field_ty, zcu)) break false; |
| 12267 | } else true; |
| 12172 | 12268 | |
| 12173 | | const first_field_index: u32 = zcu.unionTagFieldIndex(union_obj, first_item_val).?; |
| 12174 | | const first_field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[first_field_index]); |
| 12269 | const capture_ty: Type = capture_ty: { |
| 12270 | if (same_types) break :capture_ty first_field_ty; |
| 12271 | // We need values to run PTR on, so make a bunch of undef constants. |
| 12272 | const dummy_captures = try sema.arena.alloc(Air.Inst.Ref, item_refs.len); |
| 12273 | for (dummy_captures, field_indices) |*dummy, field_idx| { |
| 12274 | const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]); |
| 12275 | dummy.* = try pt.undefRef(field_ty); |
| 12276 | } |
| 12175 | 12277 | |
| 12176 | | const field_indices = try sema.arena.alloc(u32, case_vals.len); |
| 12177 | | for (case_vals, field_indices) |item, *field_idx| { |
| 12178 | | const item_val = sema.resolveValue(item).?; |
| 12179 | | field_idx.* = zcu.unionTagFieldIndex(union_obj, item_val).?; |
| 12278 | const item_srcs = try sema.arena.alloc(?LazySrcLoc, item_refs.len); |
| 12279 | for (item_srcs, 0..) |*item_src, item_i| { |
| 12280 | item_src.* = .{ |
| 12281 | .base_node_inst = capture_src.base_node_inst, |
| 12282 | .offset = .{ .switch_case_item = .{ |
| 12283 | .switch_node_offset = switch_node_offset, |
| 12284 | .case_idx = capture_src.offset.switch_capture.case_idx, |
| 12285 | .item_idx = .{ .kind = .single, .value = @intCast(item_i) }, |
| 12286 | } }, |
| 12287 | }; |
| 12180 | 12288 | } |
| 12181 | 12289 | |
| 12182 | | // Fast path: if all the operands are the same type already, we don't need to hit |
| 12183 | | // PTR! This will also allow us to emit simpler code. |
| 12184 | | const same_types = for (field_indices[1..]) |field_idx| { |
| 12185 | | const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]); |
| 12186 | | if (!field_ty.eql(first_field_ty, zcu)) break false; |
| 12187 | | } else true; |
| 12290 | break :capture_ty sema.resolvePeerTypes( |
| 12291 | case_block, |
| 12292 | capture_src, |
| 12293 | dummy_captures, |
| 12294 | .{ .override = item_srcs }, |
| 12295 | ) catch |err| switch (err) { |
| 12296 | error.AnalysisFail => { |
| 12297 | const msg = sema.err orelse return error.AnalysisFail; |
| 12298 | try sema.reparentOwnedErrorMsg(capture_src, msg, "capture group with incompatible types", .{}); |
| 12299 | return error.AnalysisFail; |
| 12300 | }, |
| 12301 | else => |e| return e, |
| 12302 | }; |
| 12303 | }; |
| 12188 | 12304 | |
| 12189 | | const capture_ty: Type = capture_ty: { |
| 12190 | | if (same_types) break :capture_ty first_field_ty; |
| 12305 | // By-reference captures have some further restrictions which make them easier to emit |
| 12306 | if (capture_by_ref) { |
| 12307 | const operand_ptr_ty = sema.typeOf(loaded_operand); |
| 12308 | const capture_ptr_ty = resolve: { |
| 12309 | // By-ref captures of hetereogeneous types are only allowed if all field |
| 12310 | // pointer types are peer resolvable to each other. |
| 12191 | 12311 | // We need values to run PTR on, so make a bunch of undef constants. |
| 12192 | | const dummy_captures = try sema.arena.alloc(Air.Inst.Ref, case_vals.len); |
| 12193 | | for (dummy_captures, field_indices) |*dummy, field_idx| { |
| 12194 | | const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]); |
| 12195 | | dummy.* = try pt.undefRef(field_ty); |
| 12196 | | } |
| 12197 | | |
| 12198 | | const case_srcs = try sema.arena.alloc(?LazySrcLoc, case_vals.len); |
| 12199 | | for (case_srcs, 0..) |*case_src, item_i| { |
| 12200 | | case_src.* = .{ |
| 12312 | const dummy_captures = try sema.arena.alloc(Air.Inst.Ref, item_refs.len); |
| 12313 | for (field_indices, dummy_captures) |field_index, *dummy| { |
| 12314 | const field_ptr_ty = try operand_ptr_ty.fieldPtrType(field_index, pt); |
| 12315 | dummy.* = try pt.undefRef(field_ptr_ty); |
| 12316 | } |
| 12317 | const item_srcs = try sema.arena.alloc(?LazySrcLoc, item_refs.len); |
| 12318 | for (item_srcs, 0..) |*item_src, item_i| { |
| 12319 | item_src.* = .{ |
| 12201 | 12320 | .base_node_inst = capture_src.base_node_inst, |
| 12202 | 12321 | .offset = .{ .switch_case_item = .{ |
| 12203 | 12322 | .switch_node_offset = switch_node_offset, |
| ... | ... | @@ -12207,14 +12326,15 @@ fn analyzeSwitchPayloadCapture( |
| 12207 | 12326 | }; |
| 12208 | 12327 | } |
| 12209 | 12328 | |
| 12210 | | break :capture_ty sema.resolvePeerTypes( |
| 12329 | break :resolve sema.resolvePeerTypes( |
| 12211 | 12330 | case_block, |
| 12212 | 12331 | capture_src, |
| 12213 | 12332 | dummy_captures, |
| 12214 | | .{ .override = case_srcs }, |
| 12333 | .{ .override = item_srcs }, |
| 12215 | 12334 | ) catch |err| switch (err) { |
| 12216 | 12335 | error.AnalysisFail => { |
| 12217 | 12336 | const msg = sema.err orelse return error.AnalysisFail; |
| 12337 | try sema.errNote(capture_src, msg, "this coercion is only possible when capturing by value", .{}); |
| 12218 | 12338 | try sema.reparentOwnedErrorMsg(capture_src, msg, "capture group with incompatible types", .{}); |
| 12219 | 12339 | return error.AnalysisFail; |
| 12220 | 12340 | }, |
| ... | ... | @@ -12222,262 +12342,188 @@ fn analyzeSwitchPayloadCapture( |
| 12222 | 12342 | }; |
| 12223 | 12343 | }; |
| 12224 | 12344 | |
| 12225 | | // By-reference captures have some further restrictions which make them easier to emit |
| 12226 | | if (capture_by_ref) { |
| 12227 | | const operand_ptr_ty = sema.typeOf(operand_ptr); |
| 12228 | | const capture_ptr_ty = resolve: { |
| 12229 | | // By-ref captures of hetereogeneous types are only allowed if all field |
| 12230 | | // pointer types are peer resolvable to each other. |
| 12231 | | // We need values to run PTR on, so make a bunch of undef constants. |
| 12232 | | const dummy_captures = try sema.arena.alloc(Air.Inst.Ref, case_vals.len); |
| 12233 | | for (field_indices, dummy_captures) |field_index, *dummy| { |
| 12234 | | const field_ptr_ty = try operand_ptr_ty.fieldPtrType(field_index, pt); |
| 12235 | | dummy.* = try pt.undefRef(field_ptr_ty); |
| 12236 | | } |
| 12237 | | const case_srcs = try sema.arena.alloc(?LazySrcLoc, case_vals.len); |
| 12238 | | for (case_srcs, 0..) |*case_src, item_i| { |
| 12239 | | case_src.* = .{ |
| 12240 | | .base_node_inst = capture_src.base_node_inst, |
| 12241 | | .offset = .{ .switch_case_item = .{ |
| 12242 | | .switch_node_offset = switch_node_offset, |
| 12243 | | .case_idx = capture_src.offset.switch_capture.case_idx, |
| 12244 | | .item_idx = .{ .kind = .single, .value = @intCast(item_i) }, |
| 12245 | | } }, |
| 12246 | | }; |
| 12247 | | } |
| 12248 | | |
| 12249 | | break :resolve sema.resolvePeerTypes( |
| 12250 | | case_block, |
| 12251 | | capture_src, |
| 12252 | | dummy_captures, |
| 12253 | | .{ .override = case_srcs }, |
| 12254 | | ) catch |err| switch (err) { |
| 12255 | | error.AnalysisFail => { |
| 12256 | | const msg = sema.err orelse return error.AnalysisFail; |
| 12257 | | try sema.errNote(capture_src, msg, "this coercion is only possible when capturing by value", .{}); |
| 12258 | | try sema.reparentOwnedErrorMsg(capture_src, msg, "capture group with incompatible types", .{}); |
| 12259 | | return error.AnalysisFail; |
| 12260 | | }, |
| 12261 | | else => |e| return e, |
| 12262 | | }; |
| 12263 | | }; |
| 12264 | | |
| 12265 | | if (try sema.resolveDefinedValue(case_block, operand_src, operand_ptr)) |op_ptr_val| { |
| 12266 | | if (op_ptr_val.isUndef(zcu)) return pt.undefRef(capture_ptr_ty); |
| 12267 | | const field_ptr_val = try op_ptr_val.ptrField(first_field_index, pt); |
| 12268 | | return .fromValue(try pt.getCoerced(field_ptr_val, capture_ptr_ty)); |
| 12269 | | } |
| 12270 | | |
| 12271 | | try sema.requireRuntimeBlock(case_block, operand_src, null); |
| 12272 | | return case_block.addStructFieldPtr(operand_ptr, first_field_index, capture_ptr_ty); |
| 12273 | | } |
| 12274 | | |
| 12275 | | if (try capture_ty.onePossibleValue(pt)) |opv| return .fromValue(opv); |
| 12276 | | |
| 12277 | | if (try sema.resolveDefinedValue(case_block, operand_src, operand_val)) |operand_val_val| { |
| 12278 | | if (operand_val_val.isUndef(zcu)) return pt.undefRef(capture_ty); |
| 12279 | | const union_val = ip.indexToKey(operand_val_val.toIntern()).un; |
| 12280 | | if (Value.fromInterned(union_val.tag).isUndef(zcu)) return pt.undefRef(capture_ty); |
| 12281 | | const uncoerced: Air.Inst.Ref = .fromIntern(union_val.val); |
| 12282 | | return sema.coerce(case_block, capture_ty, uncoerced, operand_src); |
| 12345 | if (try sema.resolveDefinedValue(case_block, operand_src, loaded_operand)) |op_ptr_val| { |
| 12346 | if (op_ptr_val.isUndef(zcu)) return pt.undefRef(capture_ptr_ty); |
| 12347 | const field_ptr_val = try op_ptr_val.ptrField(first_field_index, pt); |
| 12348 | return .fromValue(try pt.getCoerced(field_ptr_val, capture_ptr_ty)); |
| 12283 | 12349 | } |
| 12284 | 12350 | |
| 12285 | 12351 | try sema.requireRuntimeBlock(case_block, operand_src, null); |
| 12352 | return case_block.addStructFieldPtr(loaded_operand, first_field_index, capture_ptr_ty); |
| 12353 | } |
| 12286 | 12354 | |
| 12287 | | if (same_types) { |
| 12288 | | return case_block.addStructFieldVal(operand_val, first_field_index, capture_ty); |
| 12289 | | } |
| 12355 | if (try capture_ty.onePossibleValue(pt)) |opv| return .fromValue(opv); |
| 12290 | 12356 | |
| 12291 | | // We may have to emit a switch block which coerces the operand to the capture type. |
| 12292 | | // If we can, try to avoid that using in-memory coercions. |
| 12293 | | const first_non_imc = in_mem: { |
| 12294 | | for (field_indices, 0..) |field_idx, i| { |
| 12295 | | const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]); |
| 12296 | | if (.ok != try sema.coerceInMemoryAllowed(case_block, capture_ty, field_ty, false, zcu.getTarget(), .unneeded, .unneeded, null)) { |
| 12297 | | break :in_mem i; |
| 12298 | | } |
| 12299 | | } |
| 12300 | | // All fields are in-memory coercible to the resolved type! |
| 12301 | | // Just take the first field and bitcast the result. |
| 12302 | | const uncoerced = try case_block.addStructFieldVal(operand_val, first_field_index, first_field_ty); |
| 12303 | | return case_block.addBitCast(capture_ty, uncoerced); |
| 12304 | | }; |
| 12357 | if (try sema.resolveDefinedValue(case_block, operand_src, loaded_operand)) |operand_val| { |
| 12358 | if (operand_val.isUndef(zcu)) return pt.undefRef(capture_ty); |
| 12359 | const union_val = ip.indexToKey(operand_val.toIntern()).un; |
| 12360 | if (Value.fromInterned(union_val.tag).isUndef(zcu)) return pt.undefRef(capture_ty); |
| 12361 | const uncoerced: Air.Inst.Ref = .fromIntern(union_val.val); |
| 12362 | return sema.coerce(case_block, capture_ty, uncoerced, operand_src); |
| 12363 | } |
| 12305 | 12364 | |
| 12306 | | // By-val capture with heterogeneous types which are not all in-memory coercible to |
| 12307 | | // the resolved capture type. We finally have to fall back to the ugly method. |
| 12365 | try sema.requireRuntimeBlock(case_block, operand_src, null); |
| 12308 | 12366 | |
| 12309 | | // However, let's first track which operands are in-memory coercible. There may well |
| 12310 | | // be several, and we can squash all of these cases into the same switch prong using |
| 12311 | | // a simple bitcast. We'll make this the 'else' prong. |
| 12367 | if (same_types) { |
| 12368 | return case_block.addStructFieldVal(loaded_operand, first_field_index, capture_ty); |
| 12369 | } |
| 12312 | 12370 | |
| 12313 | | var in_mem_coercible: std.bit_set.Dynamic = try .initFull(sema.arena, field_indices.len); |
| 12314 | | in_mem_coercible.unset(first_non_imc); |
| 12315 | | { |
| 12316 | | const next = first_non_imc + 1; |
| 12317 | | for (field_indices[next..], next..) |field_idx, i| { |
| 12318 | | const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]); |
| 12319 | | if (.ok != try sema.coerceInMemoryAllowed(case_block, capture_ty, field_ty, false, zcu.getTarget(), .unneeded, .unneeded, null)) { |
| 12320 | | in_mem_coercible.unset(i); |
| 12321 | | } |
| 12371 | // We may have to emit a switch block which coerces the operand to the capture type. |
| 12372 | // If we can, try to avoid that using in-memory coercions. |
| 12373 | const first_non_imc = in_mem: { |
| 12374 | for (field_indices, 0..) |field_idx, i| { |
| 12375 | const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]); |
| 12376 | if (.ok != try sema.coerceInMemoryAllowed(case_block, capture_ty, field_ty, false, zcu.getTarget(), .unneeded, .unneeded, null)) { |
| 12377 | break :in_mem i; |
| 12322 | 12378 | } |
| 12323 | 12379 | } |
| 12380 | // All fields are in-memory coercible to the resolved type! |
| 12381 | // Just take the first field and bitcast the result. |
| 12382 | const uncoerced = try case_block.addStructFieldVal(loaded_operand, first_field_index, first_field_ty); |
| 12383 | return case_block.addBitCast(capture_ty, uncoerced); |
| 12384 | }; |
| 12324 | 12385 | |
| 12325 | | const capture_block_inst = try case_block.addInstAsIndex(.{ |
| 12326 | | .tag = .block, |
| 12327 | | .data = .{ |
| 12328 | | .ty_pl = .{ |
| 12329 | | .ty = .fromType(capture_ty), |
| 12330 | | .payload = undefined, // updated below |
| 12331 | | }, |
| 12332 | | }, |
| 12333 | | }); |
| 12334 | | |
| 12335 | | const prong_count = field_indices.len - in_mem_coercible.count(); |
| 12386 | // By-val capture with heterogeneous types which are not all in-memory coercible to |
| 12387 | // the resolved capture type. We finally have to fall back to the ugly method. |
| 12336 | 12388 | |
| 12337 | | const estimated_extra = prong_count * 6 + (prong_count / 10); // 2 for Case, 1 item, probably 3 insts; plus hints |
| 12338 | | var cases_extra = try std.array_list.Managed(u32).initCapacity(sema.gpa, estimated_extra); |
| 12339 | | defer cases_extra.deinit(); |
| 12389 | // However, let's first track which operands are in-memory coercible. There may well |
| 12390 | // be several, and we can squash all of these cases into the same switch prong using |
| 12391 | // a simple bitcast. We'll make this the 'else' prong. |
| 12340 | 12392 | |
| 12341 | | { |
| 12342 | | // All branch hints are `.none`, so just add zero elems. |
| 12343 | | comptime assert(@intFromEnum(std.lang.BranchHint.none) == 0); |
| 12344 | | const need_elems = std.math.divCeil(usize, prong_count + 1, 10) catch unreachable; |
| 12345 | | try cases_extra.appendNTimes(0, need_elems); |
| 12393 | var in_mem_coercible: std.bit_set.Dynamic = try .initFull(sema.arena, field_indices.len); |
| 12394 | in_mem_coercible.unset(first_non_imc); |
| 12395 | { |
| 12396 | const next = first_non_imc + 1; |
| 12397 | for (field_indices[next..], next..) |field_idx, i| { |
| 12398 | const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]); |
| 12399 | if (.ok != try sema.coerceInMemoryAllowed(case_block, capture_ty, field_ty, false, zcu.getTarget(), .unneeded, .unneeded, null)) { |
| 12400 | in_mem_coercible.unset(i); |
| 12401 | } |
| 12346 | 12402 | } |
| 12403 | } |
| 12347 | 12404 | |
| 12348 | | { |
| 12349 | | // Non-bitcast cases |
| 12350 | | var it = in_mem_coercible.iterator(.{ .kind = .unset }); |
| 12351 | | while (it.next()) |idx| { |
| 12352 | | var coerce_block = case_block.makeSubBlock(); |
| 12353 | | defer coerce_block.instructions.deinit(sema.gpa); |
| 12405 | const capture_block_inst = try case_block.addInstAsIndex(.{ |
| 12406 | .tag = .block, |
| 12407 | .data = .{ |
| 12408 | .ty_pl = .{ |
| 12409 | .ty = .fromType(capture_ty), |
| 12410 | .payload = undefined, // updated below |
| 12411 | }, |
| 12412 | }, |
| 12413 | }); |
| 12354 | 12414 | |
| 12355 | | const case_src: LazySrcLoc = .{ |
| 12356 | | .base_node_inst = capture_src.base_node_inst, |
| 12357 | | .offset = .{ .switch_case_item = .{ |
| 12358 | | .switch_node_offset = switch_node_offset, |
| 12359 | | .case_idx = capture_src.offset.switch_capture.case_idx, |
| 12360 | | .item_idx = .{ .kind = .single, .value = @intCast(idx) }, |
| 12361 | | } }, |
| 12362 | | }; |
| 12415 | const prong_count = field_indices.len - in_mem_coercible.count(); |
| 12363 | 12416 | |
| 12364 | | const field_idx = field_indices[idx]; |
| 12365 | | const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]); |
| 12366 | | const uncoerced = try coerce_block.addStructFieldVal(operand_val, field_idx, field_ty); |
| 12367 | | const coerced = try sema.coerce(&coerce_block, capture_ty, uncoerced, case_src); |
| 12368 | | _ = try coerce_block.addBr(capture_block_inst, coerced); |
| 12417 | const estimated_extra = prong_count * 6 + (prong_count / 10); // 2 for Case, 1 item, probably 3 insts; plus hints |
| 12418 | var cases_extra = try std.ArrayList(u32).initCapacity(gpa, estimated_extra); |
| 12419 | defer cases_extra.deinit(gpa); |
| 12369 | 12420 | |
| 12370 | | try cases_extra.ensureUnusedCapacity(@typeInfo(Air.SwitchBr.Case).@"struct".field_names.len + |
| 12371 | | 1 + // `item`, no ranges |
| 12372 | | coerce_block.instructions.items.len); |
| 12373 | | cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{ |
| 12374 | | .items_len = 1, |
| 12375 | | .ranges_len = 0, |
| 12376 | | .body_len = @intCast(coerce_block.instructions.items.len), |
| 12377 | | })); |
| 12378 | | cases_extra.appendAssumeCapacity(@intFromEnum(case_vals[idx])); // item |
| 12379 | | cases_extra.appendSliceAssumeCapacity(@ptrCast(coerce_block.instructions.items)); // body |
| 12380 | | } |
| 12381 | | } |
| 12382 | | const else_body_len = len: { |
| 12383 | | // 'else' prong uses a bitcast |
| 12421 | { |
| 12422 | // All branch hints are `.none`, so just add zero elems. |
| 12423 | comptime assert(@intFromEnum(std.lang.BranchHint.none) == 0); |
| 12424 | const need_elems = std.math.divCeil(usize, prong_count + 1, 10) catch unreachable; |
| 12425 | try cases_extra.appendNTimes(gpa, 0, need_elems); |
| 12426 | } |
| 12427 | |
| 12428 | { |
| 12429 | // Non-bitcast cases |
| 12430 | var it = in_mem_coercible.iterator(.{ .kind = .unset }); |
| 12431 | while (it.next()) |idx| { |
| 12384 | 12432 | var coerce_block = case_block.makeSubBlock(); |
| 12385 | 12433 | defer coerce_block.instructions.deinit(sema.gpa); |
| 12386 | 12434 | |
| 12387 | | const first_imc_item_idx = in_mem_coercible.findFirstSet().?; |
| 12388 | | const first_imc_field_idx = field_indices[first_imc_item_idx]; |
| 12389 | | const first_imc_field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[first_imc_field_idx]); |
| 12390 | | const uncoerced = try coerce_block.addStructFieldVal(operand_val, first_imc_field_idx, first_imc_field_ty); |
| 12391 | | const coerced = try coerce_block.addBitCast(capture_ty, uncoerced); |
| 12392 | | _ = try coerce_block.addBr(capture_block_inst, coerced); |
| 12393 | | |
| 12394 | | try cases_extra.appendSlice(@ptrCast(coerce_block.instructions.items)); |
| 12395 | | break :len coerce_block.instructions.items.len; |
| 12396 | | }; |
| 12435 | const case_src: LazySrcLoc = .{ |
| 12436 | .base_node_inst = capture_src.base_node_inst, |
| 12437 | .offset = .{ .switch_case_item = .{ |
| 12438 | .switch_node_offset = switch_node_offset, |
| 12439 | .case_idx = capture_src.offset.switch_capture.case_idx, |
| 12440 | .item_idx = .{ .kind = .single, .value = @intCast(idx) }, |
| 12441 | } }, |
| 12442 | }; |
| 12397 | 12443 | |
| 12398 | | try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.SwitchBr).@"struct".field_names.len + |
| 12399 | | cases_extra.items.len + |
| 12400 | | @typeInfo(Air.Block).@"struct".field_names.len + |
| 12401 | | 1); |
| 12444 | const field_idx = field_indices[idx]; |
| 12445 | const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]); |
| 12446 | const uncoerced = try coerce_block.addStructFieldVal(loaded_operand, field_idx, field_ty); |
| 12447 | const coerced = try sema.coerce(&coerce_block, capture_ty, uncoerced, case_src); |
| 12448 | _ = try coerce_block.addBr(capture_block_inst, coerced); |
| 12402 | 12449 | |
| 12403 | | const switch_br_inst: u32 = @intCast(sema.air_instructions.len); |
| 12404 | | try sema.air_instructions.append(sema.gpa, .{ |
| 12405 | | .tag = .switch_br, |
| 12406 | | .data = .{ |
| 12407 | | .pl_op = .{ |
| 12408 | | .operand = undefined, // set by switch below |
| 12409 | | .payload = sema.addExtraAssumeCapacity(Air.SwitchBr{ |
| 12410 | | .cases_len = @intCast(prong_count), |
| 12411 | | .else_body_len = @intCast(else_body_len), |
| 12412 | | }), |
| 12413 | | }, |
| 12414 | | }, |
| 12415 | | }); |
| 12416 | | sema.air_extra.appendSliceAssumeCapacity(cases_extra.items); |
| 12417 | | |
| 12418 | | // Set up block body |
| 12419 | | switch (operand) { |
| 12420 | | .simple => |s| { |
| 12421 | | const air_datas = sema.air_instructions.items(.data); |
| 12422 | | air_datas[switch_br_inst].pl_op.operand = s.cond; |
| 12423 | | air_datas[@intFromEnum(capture_block_inst)].ty_pl.payload = |
| 12424 | | sema.addExtraAssumeCapacity(Air.Block{ .body_len = 1 }); |
| 12425 | | sema.air_extra.appendAssumeCapacity(switch_br_inst); |
| 12426 | | }, |
| 12427 | | .loop => { |
| 12428 | | // The block must first extract the tag from the loaded union. |
| 12429 | | const tag_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len); |
| 12430 | | try sema.air_instructions.append(sema.gpa, .{ |
| 12431 | | .tag = .get_union_tag, |
| 12432 | | .data = .{ .ty_op = .{ |
| 12433 | | .ty = .fromIntern(union_obj.enum_tag_type), |
| 12434 | | .operand = operand_val, |
| 12435 | | } }, |
| 12436 | | }); |
| 12437 | | const air_datas = sema.air_instructions.items(.data); |
| 12438 | | air_datas[switch_br_inst].pl_op.operand = tag_inst.toRef(); |
| 12439 | | air_datas[@intFromEnum(capture_block_inst)].ty_pl.payload = |
| 12440 | | sema.addExtraAssumeCapacity(Air.Block{ .body_len = 2 }); |
| 12441 | | sema.air_extra.appendAssumeCapacity(@intFromEnum(tag_inst)); |
| 12442 | | sema.air_extra.appendAssumeCapacity(switch_br_inst); |
| 12443 | | }, |
| 12450 | try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".field_names.len + |
| 12451 | 1 + // `item`, no ranges |
| 12452 | coerce_block.instructions.items.len); |
| 12453 | cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{ |
| 12454 | .items_len = 1, |
| 12455 | .ranges_len = 0, |
| 12456 | .body_len = @intCast(coerce_block.instructions.items.len), |
| 12457 | })); |
| 12458 | cases_extra.appendAssumeCapacity(@intFromEnum(item_refs[idx])); // item |
| 12459 | cases_extra.appendSliceAssumeCapacity(@ptrCast(coerce_block.instructions.items)); // body |
| 12444 | 12460 | } |
| 12445 | | |
| 12446 | | return capture_block_inst.toRef(); |
| 12447 | 12461 | } |
| 12462 | const else_body_len = len: { |
| 12463 | // 'else' prong uses a bitcast |
| 12464 | var coerce_block = case_block.makeSubBlock(); |
| 12465 | defer coerce_block.instructions.deinit(sema.gpa); |
| 12448 | 12466 | |
| 12449 | | if (err_set) { |
| 12450 | | const case_vals = kind.item_refs; |
| 12451 | | if (case_vals.len == 1) { |
| 12452 | | const item_val = sema.resolveValue(case_vals[0]).?; |
| 12453 | | const item_ty = try pt.singleErrorSetType(item_val.getErrorName(zcu).unwrap().?); |
| 12454 | | return sema.bitCast(case_block, item_ty, .fromValue(item_val), operand_src, null); |
| 12455 | | } |
| 12467 | const first_imc_item_idx = in_mem_coercible.findFirstSet().?; |
| 12468 | const first_imc_field_idx = field_indices[first_imc_item_idx]; |
| 12469 | const first_imc_field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[first_imc_field_idx]); |
| 12470 | const uncoerced = try coerce_block.addStructFieldVal(loaded_operand, first_imc_field_idx, first_imc_field_ty); |
| 12471 | const coerced = try coerce_block.addBitCast(capture_ty, uncoerced); |
| 12472 | _ = try coerce_block.addBr(capture_block_inst, coerced); |
| 12456 | 12473 | |
| 12457 | | var names: InferredErrorSet.NameMap = .{}; |
| 12458 | | try names.ensureUnusedCapacity(sema.arena, case_vals.len); |
| 12459 | | for (case_vals) |err| { |
| 12460 | | const err_val = sema.resolveValue(err).?; |
| 12461 | | names.putAssumeCapacityNoClobber(err_val.getErrorName(zcu).unwrap().?, {}); |
| 12462 | | } |
| 12463 | | const error_ty = try pt.errorSetFromUnsortedNames(names.keys()); |
| 12464 | | return sema.bitCast(case_block, error_ty, operand_val, operand_src, null); |
| 12465 | | } |
| 12474 | try cases_extra.appendSlice(gpa, @ptrCast(coerce_block.instructions.items)); |
| 12475 | break :len coerce_block.instructions.items.len; |
| 12476 | }; |
| 12466 | 12477 | |
| 12467 | | // In this case the capture value is just the passed-through value of the |
| 12468 | | // switch condition. It is comptime-known if there is only one item. |
| 12469 | | if (capture_by_ref) { |
| 12470 | | return operand_ptr; |
| 12471 | | } |
| 12472 | | switch (kind) { |
| 12473 | | .inline_ref, .special => unreachable, |
| 12474 | | .item_refs => |case_vals| { |
| 12475 | | // If there's only a single item, the capture is comptime-known! |
| 12476 | | if (case_vals.len == 1) return case_vals[0]; |
| 12478 | try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr).@"struct".field_names.len + |
| 12479 | cases_extra.items.len + |
| 12480 | @typeInfo(Air.Block).@"struct".field_names.len + |
| 12481 | 1); |
| 12482 | |
| 12483 | const switch_br_inst: u32 = @intCast(sema.air_instructions.len); |
| 12484 | try sema.air_instructions.append(gpa, .{ |
| 12485 | .tag = .switch_br, |
| 12486 | .data = .{ |
| 12487 | .pl_op = .{ |
| 12488 | .operand = undefined, // set by switch below |
| 12489 | .payload = sema.addExtraAssumeCapacity(Air.SwitchBr{ |
| 12490 | .cases_len = @intCast(prong_count), |
| 12491 | .else_body_len = @intCast(else_body_len), |
| 12492 | }), |
| 12493 | }, |
| 12494 | }, |
| 12495 | }); |
| 12496 | sema.air_extra.appendSliceAssumeCapacity(cases_extra.items); |
| 12497 | |
| 12498 | // Set up block body |
| 12499 | switch (operand) { |
| 12500 | .simple => |s| { |
| 12501 | const air_datas = sema.air_instructions.items(.data); |
| 12502 | air_datas[switch_br_inst].pl_op.operand = s.cond; |
| 12503 | air_datas[@intFromEnum(capture_block_inst)].ty_pl.payload = |
| 12504 | sema.addExtraAssumeCapacity(Air.Block{ .body_len = 1 }); |
| 12505 | sema.air_extra.appendAssumeCapacity(switch_br_inst); |
| 12506 | }, |
| 12507 | .loop => { |
| 12508 | // The block must first extract the tag from the loaded union. |
| 12509 | const tag_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len); |
| 12510 | try sema.air_instructions.append(sema.gpa, .{ |
| 12511 | .tag = .get_union_tag, |
| 12512 | .data = .{ .ty_op = .{ |
| 12513 | .ty = .fromIntern(union_obj.enum_tag_type), |
| 12514 | .operand = loaded_operand, |
| 12515 | } }, |
| 12516 | }); |
| 12517 | const air_datas = sema.air_instructions.items(.data); |
| 12518 | air_datas[switch_br_inst].pl_op.operand = tag_inst.toRef(); |
| 12519 | air_datas[@intFromEnum(capture_block_inst)].ty_pl.payload = |
| 12520 | sema.addExtraAssumeCapacity(Air.Block{ .body_len = 2 }); |
| 12521 | sema.air_extra.appendAssumeCapacity(@intFromEnum(tag_inst)); |
| 12522 | sema.air_extra.appendAssumeCapacity(switch_br_inst); |
| 12477 | 12523 | }, |
| 12478 | | .has_ranges => {}, |
| 12479 | 12524 | } |
| 12480 | | return operand_val; |
| 12525 | |
| 12526 | return capture_block_inst.toRef(); |
| 12481 | 12527 | } |
| 12482 | 12528 | |
| 12483 | 12529 | const ResolvedSwitchItem = struct { |