| ... | @@ -10216,7 +10216,7 @@ fn analyzeSwitchBlock( | ... | @@ -10216,7 +10216,7 @@ fn analyzeSwitchBlock( |
| 10216 | | 10216 | |
| 10217 | const case_vals = validated_switch.case_vals; | 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 | var case_val_idx: usize = 0; | 10220 | var case_val_idx: usize = 0; |
| 10221 | var case_it = zir_switch.iterateCases(); | 10221 | var case_it = zir_switch.iterateCases(); |
| 10222 | var extra_index = zir_switch.end; | 10222 | var extra_index = zir_switch.end; |
| ... | @@ -10239,12 +10239,12 @@ fn analyzeSwitchBlock( | ... | @@ -10239,12 +10239,12 @@ fn analyzeSwitchBlock( |
| 10239 | } | 10239 | } |
| 10240 | continue; | 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 | if (has_else) { | 10244 | if (has_else) { |
| 10245 | // This *has* to be checked after iterating all regular cases because | 10245 | // This *has* to be checked after iterating all regular cases because |
| 10246 | // we allow simple noreturn else prongs when switching on error sets! | 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 | unreachable; // malformed validated switch | 10249 | unreachable; // malformed validated switch |
| 10250 | }; | 10250 | }; |
| ... | @@ -10255,58 +10255,33 @@ fn analyzeSwitchBlock( | ... | @@ -10255,58 +10255,33 @@ fn analyzeSwitchBlock( |
| 10255 | if (!(err_set and | 10255 | if (!(err_set and |
| 10256 | try sema.maybeErrorUnwrap(&case_block, body, cond_ref, operand_src, true))) | 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. | 10258 | const payload_inst = if (capture != .none) inst: { |
| 10259 | const payload_inst: Zir.Inst.Index = if (capture != .none) inst: { | | |
| 10260 | const payload_inst = zir_switch.payload_capture_placeholder.unwrap() orelse switch_inst; | 10259 | const payload_inst = zir_switch.payload_capture_placeholder.unwrap() orelse switch_inst; |
| 10261 | const payload_ref: Air.Inst.Ref = payload_ref: { | 10260 | const payload_ref: Air.Inst.Ref = payload_ref: { |
| 10262 | const item_val: Value = item_val: { | 10261 | const captured_opv: Value = captured_opv: { |
| 10263 | if (!tagged_union_originally) { | 10262 | if (!tagged_union_originally) { |
| 10264 | break :item_val item_opv; | 10263 | break :captured_opv item_opv; |
| 10265 | } | 10264 | } |
| 10266 | if (maybe_operand_opv) |operand_opv| { | 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 | assert(zir_switch.any_maybe_runtime_capture); // there's a payload capture | 10268 | assert(zir_switch.any_maybe_runtime_capture); // there's a payload capture |
| 10270 | const operand_val, const operand_ref = switch (operand) { | 10269 | const loaded_operand = try sema.analyzeSwitchOperandLoad(&case_block, operand, operand_src, capture == .by_ref); |
| 10271 | .simple => unreachable, | 10270 | break :payload_ref try sema.resolveSwitchPayloadCaptureTaggedUnion( |
| 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( | | |
| 10288 | &case_block, | 10271 | &case_block, |
| 10289 | operand, | 10272 | loaded_operand, |
| 10290 | operand_val, | | |
| 10291 | operand_ref, | | |
| 10292 | operand_ty, | | |
| 10293 | operand_src, | 10273 | operand_src, |
| 10294 | block.src(.{ .switch_capture = .{ | 10274 | operand_ty, |
| 10295 | .switch_node_offset = src_node_offset, | 10275 | item_opv, |
| 10296 | .case_idx = index, | | |
| 10297 | } }), | | |
| 10298 | capture == .by_ref, | 10276 | capture == .by_ref, |
| 10299 | prong_kind, | | |
| 10300 | validated_switch.else_err_ty, | | |
| 10301 | ); | 10277 | ); |
| 10302 | }; | 10278 | }; |
| 10303 | break :payload_ref switch (capture) { | 10279 | break :payload_ref switch (capture) { |
| 10304 | .by_val => .fromValue(item_val), | 10280 | .by_val => .fromValue(captured_opv), |
| 10305 | .by_ref => try sema.uavRef(item_val), | 10281 | .by_ref => try sema.uavRef(captured_opv), |
| 10306 | .none => unreachable, | 10282 | .none => unreachable, |
| 10307 | }; | 10283 | }; |
| 10308 | }; | 10284 | }; |
| 10309 | assert(!sema.typeOf(payload_ref).isNoReturn(sema.pt.zcu)); | | |
| 10310 | sema.inst_map.putAssumeCapacity(payload_inst, payload_ref); | 10285 | sema.inst_map.putAssumeCapacity(payload_inst, payload_ref); |
| 10311 | break :inst payload_inst; | 10286 | break :inst payload_inst; |
| 10312 | } else undefined; | 10287 | } else undefined; |
| ... | @@ -10599,7 +10574,7 @@ fn finishSwitchBr( | ... | @@ -10599,7 +10574,7 @@ fn finishSwitchBr( |
| 10599 | } }), | 10574 | } }), |
| 10600 | prong_info.capture, | 10575 | prong_info.capture, |
| 10601 | prong_info.has_tag_capture, | 10576 | prong_info.has_tag_capture, |
| 10602 | .{ .inline_ref = item_ref }, | 10577 | .{ .@"inline" = item_ref }, |
| 10603 | validated_switch.else_err_ty, | 10578 | validated_switch.else_err_ty, |
| 10604 | switch_inst, | 10579 | switch_inst, |
| 10605 | zir_switch, | 10580 | zir_switch, |
| ... | @@ -10689,7 +10664,7 @@ fn finishSwitchBr( | ... | @@ -10689,7 +10664,7 @@ fn finishSwitchBr( |
| 10689 | } }), | 10664 | } }), |
| 10690 | prong_info.capture, | 10665 | prong_info.capture, |
| 10691 | prong_info.has_tag_capture, | 10666 | prong_info.has_tag_capture, |
| 10692 | .{ .inline_ref = item_ref }, | 10667 | .{ .@"inline" = item_ref }, |
| 10693 | validated_switch.else_err_ty, | 10668 | validated_switch.else_err_ty, |
| 10694 | switch_inst, | 10669 | switch_inst, |
| 10695 | zir_switch, | 10670 | zir_switch, |
| ... | @@ -10829,7 +10804,7 @@ fn finishSwitchBr( | ... | @@ -10829,7 +10804,7 @@ fn finishSwitchBr( |
| 10829 | } }), | 10804 | } }), |
| 10830 | else_case.capture, | 10805 | else_case.capture, |
| 10831 | else_case.has_tag_capture, | 10806 | else_case.has_tag_capture, |
| 10832 | .{ .inline_ref = item_ref }, | 10807 | .{ .@"inline" = item_ref }, |
| 10833 | validated_switch.else_err_ty, | 10808 | validated_switch.else_err_ty, |
| 10834 | switch_inst, | 10809 | switch_inst, |
| 10835 | zir_switch, | 10810 | zir_switch, |
| ... | @@ -11269,8 +11244,7 @@ fn validateSwitchBlock( | ... | @@ -11269,8 +11244,7 @@ fn validateSwitchBlock( |
| 11269 | const has_else = zir_switch.else_case != null; | 11244 | const has_else = zir_switch.else_case != null; |
| 11270 | const has_under = zir_switch.has_under; | 11245 | const has_under = zir_switch.has_under; |
| 11271 | | 11246 | |
| 11272 | var case_vals: std.ArrayList(Air.Inst.Ref) = .empty; | 11247 | var case_vals: std.ArrayList(Air.Inst.Ref) = try .initCapacity(arena, zir_switch.item_infos.len); |
| 11273 | try case_vals.ensureUnusedCapacity(arena, zir_switch.item_infos.len); | | |
| 11274 | | 11248 | |
| 11275 | // Duplicate checking variables later also used for `inline else`. | 11249 | // Duplicate checking variables later also used for `inline else`. |
| 11276 | var seen_enum_fields: []?LazySrcLoc = &.{}; | 11250 | var seen_enum_fields: []?LazySrcLoc = &.{}; |
| ... | @@ -11676,10 +11650,10 @@ fn resolveSwitchBlock( | ... | @@ -11676,10 +11650,10 @@ fn resolveSwitchBlock( |
| 11676 | // This prong should be unreachable! | 11650 | // This prong should be unreachable! |
| 11677 | return .unreachable_value; | 11651 | return .unreachable_value; |
| 11678 | } | 11652 | } |
| 11679 | const prong_kind: SwitchProngKind = kind: { | 11653 | const prong_items: SwitchProngItems = prong_items: { |
| 11680 | if (prong_info.is_inline) break :kind .{ .inline_ref = cond_ref }; | 11654 | if (prong_info.is_inline) break :prong_items .{ .@"inline" = cond_ref }; |
| 11681 | if (range_refs.len > 0) break :kind .has_ranges; | 11655 | if (range_refs.len > 0) break :prong_items .has_ranges; |
| 11682 | break :kind .{ .item_refs = item_refs }; | 11656 | break :prong_items .{ .item_refs = item_refs }; |
| 11683 | }; | 11657 | }; |
| 11684 | return sema.resolveSwitchProng( | 11658 | return sema.resolveSwitchProng( |
| 11685 | block, | 11659 | block, |
| ... | @@ -11693,7 +11667,7 @@ fn resolveSwitchBlock( | ... | @@ -11693,7 +11667,7 @@ fn resolveSwitchBlock( |
| 11693 | } }), | 11667 | } }), |
| 11694 | prong_info.capture, | 11668 | prong_info.capture, |
| 11695 | prong_info.has_tag_capture, | 11669 | prong_info.has_tag_capture, |
| 11696 | prong_kind, | 11670 | prong_items, |
| 11697 | validated_switch.else_err_ty, | 11671 | validated_switch.else_err_ty, |
| 11698 | merges, | 11672 | merges, |
| 11699 | switch_inst, | 11673 | switch_inst, |
| ... | @@ -11707,8 +11681,8 @@ fn resolveSwitchBlock( | ... | @@ -11707,8 +11681,8 @@ fn resolveSwitchBlock( |
| 11707 | if ((try sema.compareAll(cond_val, .gte, first_val, item_ty)) and | 11681 | if ((try sema.compareAll(cond_val, .gte, first_val, item_ty)) and |
| 11708 | (try sema.compareAll(cond_val, .lte, last_val, item_ty))) | 11682 | (try sema.compareAll(cond_val, .lte, last_val, item_ty))) |
| 11709 | { | 11683 | { |
| 11710 | const prong_kind: SwitchProngKind = if (prong_info.is_inline) | 11684 | const prong_items: SwitchProngItems = if (prong_info.is_inline) |
| 11711 | .{ .inline_ref = cond_ref } | 11685 | .{ .@"inline" = cond_ref } |
| 11712 | else | 11686 | else |
| 11713 | .has_ranges; | 11687 | .has_ranges; |
| 11714 | return sema.resolveSwitchProng( | 11688 | return sema.resolveSwitchProng( |
| ... | @@ -11723,7 +11697,7 @@ fn resolveSwitchBlock( | ... | @@ -11723,7 +11697,7 @@ fn resolveSwitchBlock( |
| 11723 | } }), | 11697 | } }), |
| 11724 | prong_info.capture, | 11698 | prong_info.capture, |
| 11725 | prong_info.has_tag_capture, | 11699 | prong_info.has_tag_capture, |
| 11726 | prong_kind, | 11700 | prong_items, |
| 11727 | validated_switch.else_err_ty, | 11701 | validated_switch.else_err_ty, |
| 11728 | merges, | 11702 | merges, |
| 11729 | switch_inst, | 11703 | switch_inst, |
| ... | @@ -11742,8 +11716,8 @@ fn resolveSwitchBlock( | ... | @@ -11742,8 +11716,8 @@ fn resolveSwitchBlock( |
| 11742 | | 11716 | |
| 11743 | if (else_is_named_only and item_ty.enumTagFieldIndex(cond_val, zcu) != null) { | 11717 | if (else_is_named_only and item_ty.enumTagFieldIndex(cond_val, zcu) != null) { |
| 11744 | assert(item_ty.isNonexhaustiveEnum(zcu)); | 11718 | assert(item_ty.isNonexhaustiveEnum(zcu)); |
| 11745 | const prong_kind: SwitchProngKind = if (else_case.is_inline) | 11719 | const prong_items: SwitchProngItems = if (else_case.is_inline) |
| 11746 | .{ .inline_ref = cond_ref } | 11720 | .{ .@"inline" = cond_ref } |
| 11747 | else | 11721 | else |
| 11748 | .special; | 11722 | .special; |
| 11749 | return sema.resolveSwitchProng( | 11723 | return sema.resolveSwitchProng( |
| ... | @@ -11758,7 +11732,7 @@ fn resolveSwitchBlock( | ... | @@ -11758,7 +11732,7 @@ fn resolveSwitchBlock( |
| 11758 | } }), | 11732 | } }), |
| 11759 | else_case.capture, | 11733 | else_case.capture, |
| 11760 | else_case.has_tag_capture, | 11734 | else_case.has_tag_capture, |
| 11761 | prong_kind, | 11735 | prong_items, |
| 11762 | validated_switch.else_err_ty, | 11736 | validated_switch.else_err_ty, |
| 11763 | merges, | 11737 | merges, |
| 11764 | switch_inst, | 11738 | switch_inst, |
| ... | @@ -11782,8 +11756,8 @@ fn resolveSwitchBlock( | ... | @@ -11782,8 +11756,8 @@ fn resolveSwitchBlock( |
| 11782 | return .unreachable_value; | 11756 | return .unreachable_value; |
| 11783 | } | 11757 | } |
| 11784 | } | 11758 | } |
| 11785 | const prong_kind: SwitchProngKind = if (is_inline) | 11759 | const prong_items: SwitchProngItems = if (is_inline) |
| 11786 | .{ .inline_ref = cond_ref } | 11760 | .{ .@"inline" = cond_ref } |
| 11787 | else | 11761 | else |
| 11788 | .special; | 11762 | .special; |
| 11789 | return sema.resolveSwitchProng( | 11763 | return sema.resolveSwitchProng( |
| ... | @@ -11798,7 +11772,7 @@ fn resolveSwitchBlock( | ... | @@ -11798,7 +11772,7 @@ fn resolveSwitchBlock( |
| 11798 | } }), | 11772 | } }), |
| 11799 | capture, | 11773 | capture, |
| 11800 | has_tag_capture, | 11774 | has_tag_capture, |
| 11801 | prong_kind, | 11775 | prong_items, |
| 11802 | validated_switch.else_err_ty, | 11776 | validated_switch.else_err_ty, |
| 11803 | merges, | 11777 | merges, |
| 11804 | switch_inst, | 11778 | switch_inst, |
| ... | @@ -11834,14 +11808,59 @@ const SwitchOperand = union(enum) { | ... | @@ -11834,14 +11808,59 @@ const SwitchOperand = union(enum) { |
| 11834 | }, | 11808 | }, |
| 11835 | }; | 11809 | }; |
| 11836 | | 11810 | |
| 11837 | const SwitchProngKind = union(enum) { | 11811 | fn analyzeSwitchOperandLoad( |
| 11838 | /// Prefer populating this field over the others, if possible. | 11812 | sema: *Sema, |
| 11839 | inline_ref: Air.Inst.Ref, | 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 | item_refs: []const Air.Inst.Ref, | 11844 | item_refs: []const Air.Inst.Ref, |
| 11841 | has_ranges, | 11845 | has_ranges, |
| 11842 | special, | 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 | /// Resolve a switch prong which is determined at comptime to have no peers. | 11864 | /// Resolve a switch prong which is determined at comptime to have no peers. |
| 11846 | /// Sets up captures as needed. Uses `analyzeBodyRuntimeBreak`. | 11865 | /// Sets up captures as needed. Uses `analyzeBodyRuntimeBreak`. |
| 11847 | fn resolveSwitchProng( | 11866 | fn resolveSwitchProng( |
| ... | @@ -11855,7 +11874,7 @@ fn resolveSwitchProng( | ... | @@ -11855,7 +11874,7 @@ fn resolveSwitchProng( |
| 11855 | capture_src: LazySrcLoc, | 11874 | capture_src: LazySrcLoc, |
| 11856 | capture: Zir.Inst.SwitchBlock.ProngInfo.Capture, | 11875 | capture: Zir.Inst.SwitchBlock.ProngInfo.Capture, |
| 11857 | has_tag_capture: bool, | 11876 | has_tag_capture: bool, |
| 11858 | kind: SwitchProngKind, | 11877 | prong_items: SwitchProngItems, |
| 11859 | else_err_ty: ?Type, | 11878 | else_err_ty: ?Type, |
| 11860 | merges: *Block.Merges, | 11879 | merges: *Block.Merges, |
| 11861 | switch_inst: Zir.Inst.Index, | 11880 | switch_inst: Zir.Inst.Index, |
| ... | @@ -11870,36 +11889,28 @@ fn resolveSwitchProng( | ... | @@ -11870,36 +11889,28 @@ fn resolveSwitchProng( |
| 11870 | const parent_hint = sema.branch_hint; | 11889 | const parent_hint = sema.branch_hint; |
| 11871 | defer sema.branch_hint = parent_hint orelse if (sema.branch_hint == .cold) .cold else null; | 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 | const payload_inst = zir_switch.payload_capture_placeholder.unwrap() orelse switch_inst; | 11905 | const payload_inst = zir_switch.payload_capture_placeholder.unwrap() orelse switch_inst; |
| 11875 | const payload_ref = try sema.analyzeSwitchPayloadCapture( | 11906 | sema.inst_map.putAssumeCapacity(payload_inst, analyzed_captures.payload_ref); |
| 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); | | |
| 11889 | break :inst payload_inst; | 11907 | break :inst payload_inst; |
| 11890 | } else undefined; | 11908 | } else undefined; |
| 11891 | defer if (capture != .none) assert(sema.inst_map.remove(payload_inst)); | 11909 | defer if (capture != .none) assert(sema.inst_map.remove(payload_inst)); |
| 11892 | | 11910 | |
| 11893 | const tag_inst: Zir.Inst.Index = if (has_tag_capture) inst: { | 11911 | const tag_inst: Zir.Inst.Index = if (has_tag_capture) inst: { |
| 11894 | const tag_inst = zir_switch.tag_capture_placeholder.unwrap() orelse switch_inst; | 11912 | const tag_inst = zir_switch.tag_capture_placeholder.unwrap() orelse switch_inst; |
| 11895 | const tag_ref = try sema.analyzeSwitchTagCapture( | 11913 | sema.inst_map.putAssumeCapacity(tag_inst, analyzed_captures.tag_ref); |
| 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); | | |
| 11903 | break :inst tag_inst; | 11914 | break :inst tag_inst; |
| 11904 | } else undefined; | 11915 | } else undefined; |
| 11905 | defer if (has_tag_capture) assert(sema.inst_map.remove(tag_inst)); | 11916 | defer if (has_tag_capture) assert(sema.inst_map.remove(tag_inst)); |
| ... | @@ -11945,7 +11956,7 @@ fn analyzeSwitchProng( | ... | @@ -11945,7 +11956,7 @@ fn analyzeSwitchProng( |
| 11945 | capture_src: LazySrcLoc, | 11956 | capture_src: LazySrcLoc, |
| 11946 | capture: Zir.Inst.SwitchBlock.ProngInfo.Capture, | 11957 | capture: Zir.Inst.SwitchBlock.ProngInfo.Capture, |
| 11947 | has_tag_capture: bool, | 11958 | has_tag_capture: bool, |
| 11948 | kind: SwitchProngKind, | 11959 | prong_items: SwitchProngItems, |
| 11949 | else_err_ty: ?Type, | 11960 | else_err_ty: ?Type, |
| 11950 | switch_inst: Zir.Inst.Index, | 11961 | switch_inst: Zir.Inst.Index, |
| 11951 | zir_switch: *const Zir.UnwrappedSwitchBlock, | 11962 | zir_switch: *const Zir.UnwrappedSwitchBlock, |
| ... | @@ -11966,77 +11977,28 @@ fn analyzeSwitchProng( | ... | @@ -11966,77 +11977,28 @@ fn analyzeSwitchProng( |
| 11966 | } | 11977 | } |
| 11967 | } | 11978 | } |
| 11968 | | 11979 | |
| 11969 | const need_load: bool = need_load: { | 11980 | const analyzed_captures = try sema.analyzeSwitchCaptures( |
| 11970 | if (capture == .none and !has_tag_capture) { | 11981 | case_block, |
| 11971 | // No need to load the operand for this prong! | 11982 | operand, |
| 11972 | break :need_load false; | 11983 | operand_src, |
| 11973 | } | 11984 | operand_ty, |
| 11974 | if (capture != .none and operand_ty.zigTypeTag(zcu) == .@"union" and | 11985 | capture_src, |
| 11975 | operand_ty.containerLayout(zcu) != .@"packed") | 11986 | capture, |
| 11976 | { | 11987 | has_tag_capture, |
| 11977 | // Non-OPV tagged union payload captures are always runtime-known. | 11988 | prong_items, |
| 11978 | break :need_load true; | 11989 | else_err_ty, |
| 11979 | } | 11990 | ); |
| 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 | }; | | |
| 12009 | | 11991 | |
| 12010 | const payload_inst: Zir.Inst.Index = if (capture != .none) inst: { | 11992 | const payload_inst = if (capture != .none) inst: { |
| 12011 | const payload_inst = zir_switch.payload_capture_placeholder.unwrap() orelse switch_inst; | 11993 | const payload_inst = zir_switch.payload_capture_placeholder.unwrap() orelse switch_inst; |
| 12012 | const payload_ref = try sema.analyzeSwitchPayloadCapture( | 11994 | sema.inst_map.putAssumeCapacity(payload_inst, analyzed_captures.payload_ref); |
| 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); | | |
| 12026 | break :inst payload_inst; | 11995 | break :inst payload_inst; |
| 12027 | } else undefined; | 11996 | } else undefined; |
| 12028 | defer if (capture != .none) assert(sema.inst_map.remove(payload_inst)); | 11997 | defer if (capture != .none) assert(sema.inst_map.remove(payload_inst)); |
| 12029 | | 11998 | |
| 12030 | const tag_inst: Zir.Inst.Index = if (has_tag_capture) inst: { | 11999 | const tag_inst: Zir.Inst.Index = if (has_tag_capture) inst: { |
| 12031 | const tag_inst = zir_switch.tag_capture_placeholder.unwrap() orelse switch_inst; | 12000 | const tag_inst = zir_switch.tag_capture_placeholder.unwrap() orelse switch_inst; |
| 12032 | const tag_ref = try sema.analyzeSwitchTagCapture( | 12001 | sema.inst_map.putAssumeCapacity(tag_inst, analyzed_captures.tag_ref); |
| 12033 | case_block, | | |
| 12034 | operand_val, | | |
| 12035 | operand_ty, | | |
| 12036 | capture_src, | | |
| 12037 | kind, | | |
| 12038 | ); | | |
| 12039 | sema.inst_map.putAssumeCapacity(tag_inst, tag_ref); | | |
| 12040 | break :inst tag_inst; | 12002 | break :inst tag_inst; |
| 12041 | } else undefined; | 12003 | } else undefined; |
| 12042 | defer if (has_tag_capture) assert(sema.inst_map.remove(tag_inst)); | 12004 | defer if (has_tag_capture) assert(sema.inst_map.remove(tag_inst)); |
| ... | @@ -12047,157 +12009,314 @@ fn analyzeSwitchProng( | ... | @@ -12047,157 +12009,314 @@ fn analyzeSwitchProng( |
| 12047 | return sema.analyzeBodyRuntimeBreak(case_block, prong_body); | 12009 | return sema.analyzeBodyRuntimeBreak(case_block, prong_body); |
| 12048 | } | 12010 | } |
| 12049 | | 12011 | |
| 12050 | fn analyzeSwitchTagCapture( | 12012 | fn analyzeSwitchCaptures( |
| 12051 | sema: *Sema, | 12013 | sema: *Sema, |
| 12052 | case_block: *Block, | 12014 | case_block: *Block, |
| 12053 | /// May be `none` if this is an inline capture or if `kind.item_refs.len == 1`. | 12015 | operand: SwitchOperand, |
| 12054 | operand_val: Air.Inst.Ref, | 12016 | operand_src: LazySrcLoc, |
| 12055 | operand_ty: Type, | 12017 | operand_ty: Type, |
| 12056 | capture_src: LazySrcLoc, | 12018 | capture_src: LazySrcLoc, |
| 12057 | kind: SwitchProngKind, | 12019 | capture: Zir.Inst.SwitchBlock.ProngInfo.Capture, |
| 12058 | ) CompileError!Air.Inst.Ref { | 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 | const pt = sema.pt; | 12027 | const pt = sema.pt; |
| 12060 | const zcu = pt.zcu; | 12028 | const zcu = pt.zcu; |
| 12061 | | 12029 | |
| 12062 | const tag_capture_src: LazySrcLoc = .{ | 12030 | if (operand_ty.zigTypeTag(zcu) == .@"union" and |
| 12063 | .base_node_inst = capture_src.base_node_inst, | 12031 | operand_ty.containerLayout(zcu) != .@"packed") |
| 12064 | .offset = .{ .switch_tag_capture = capture_src.offset.switch_capture }, | 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") { | 12155 | if (has_tag_capture) { |
| 12068 | return sema.fail(case_block, tag_capture_src, "cannot capture tag of non-union type '{f}'", .{ | 12156 | const tag_capture_src: LazySrcLoc = .{ |
| 12069 | operand_ty.fmt(pt), | 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") { | 12181 | |
| 12073 | return sema.fail(case_block, tag_capture_src, "cannot capture tag of packed union", .{}); | 12182 | return .{ .payload_ref = payload_ref, .tag_ref = .none }; |
| 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); | | |
| 12082 | } | 12183 | } |
| 12083 | | 12184 | |
| 12084 | fn analyzeSwitchPayloadCapture( | 12185 | fn resolveSwitchPayloadCaptureTaggedUnion( |
| 12085 | sema: *Sema, | 12186 | sema: *Sema, |
| 12086 | case_block: *Block, | 12187 | case_block: *Block, |
| 12087 | operand: SwitchOperand, | 12188 | loaded_operand: Air.Inst.Ref, |
| 12088 | /// Always has to be not-`none` if this is a tagged union payload capture. | 12189 | operand_src: LazySrcLoc, |
| 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, | | |
| 12094 | operand_ty: Type, | 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 | operand_src: LazySrcLoc, | 12230 | operand_src: LazySrcLoc, |
| 12096 | capture_src: LazySrcLoc, | 12231 | operand_ty: Type, |
| 12097 | capture_by_ref: bool, | 12232 | capture_by_ref: bool, |
| 12098 | kind: SwitchProngKind, | 12233 | capture_src: LazySrcLoc, |
| 12099 | else_err_ty: ?Type, | 12234 | prong_items: SwitchProngItems, |
| 12100 | ) CompileError!Air.Inst.Ref { | 12235 | ) CompileError!Air.Inst.Ref { |
| 12101 | const pt = sema.pt; | 12236 | const pt = sema.pt; |
| 12102 | const zcu = pt.zcu; | 12237 | const zcu = pt.zcu; |
| 12103 | const ip = &zcu.intern_pool; | 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 | const switch_node_offset = operand_src.offset.node_offset_switch_operand; | 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 | 12250 | const union_obj = zcu.typeToUnion(operand_ty).?; |
| 12108 | operand_ty.containerLayout(zcu) != .@"packed"; | | |
| 12109 | const err_set = operand_ty.zigTypeTag(zcu) == .error_set; | | |
| 12110 | | 12251 | |
| 12111 | if (err_set and capture_by_ref) { | 12252 | const first_item_val = sema.resolveValue(item_refs[0]).?; |
| 12112 | return sema.fail( | 12253 | const first_field_index: u32 = zcu.unionTagFieldIndex(union_obj, first_item_val).?; |
| 12113 | case_block, | 12254 | const first_field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[first_field_index]); |
| 12114 | capture_src, | | |
| 12115 | "error set cannot be captured by reference", | | |
| 12116 | .{}, | | |
| 12117 | ); | | |
| 12118 | } | | |
| 12119 | | 12255 | |
| 12120 | if (kind == .inline_ref) { | 12256 | const field_indices = try sema.arena.alloc(u32, item_refs.len); |
| 12121 | const item_val = sema.resolveValue(kind.inline_ref).?; | 12257 | for (item_refs, field_indices) |item_ref, *field_idx| { |
| 12122 | if (tagged_union_originally) { | 12258 | const item_val = sema.resolveValue(item_ref).?; |
| 12123 | const field_index: u32 = @intCast(operand_ty.unionTagFieldIndex(item_val, zcu).?); | 12259 | field_idx.* = zcu.unionTagFieldIndex(union_obj, item_val).?; |
| 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; | | |
| 12165 | } | 12260 | } |
| 12166 | | 12261 | |
| 12167 | if (tagged_union_originally) { | 12262 | // Fast path: if all the operands are the same type already, we don't need to hit |
| 12168 | const case_vals = kind.item_refs; | 12263 | // PTR! This will also allow us to emit simpler code. |
| 12169 | | 12264 | const same_types = for (field_indices[1..]) |field_idx| { |
| 12170 | const union_obj = zcu.typeToUnion(operand_ty).?; | 12265 | const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]); |
| 12171 | const first_item_val = sema.resolveValue(case_vals[0]).?; | 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).?; | 12269 | const capture_ty: Type = capture_ty: { |
| 12174 | const first_field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[first_field_index]); | 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); | 12278 | const item_srcs = try sema.arena.alloc(?LazySrcLoc, item_refs.len); |
| 12177 | for (case_vals, field_indices) |item, *field_idx| { | 12279 | for (item_srcs, 0..) |*item_src, item_i| { |
| 12178 | const item_val = sema.resolveValue(item).?; | 12280 | item_src.* = .{ |
| 12179 | field_idx.* = zcu.unionTagFieldIndex(union_obj, item_val).?; | 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 | 12290 | break :capture_ty sema.resolvePeerTypes( |
| 12183 | // PTR! This will also allow us to emit simpler code. | 12291 | case_block, |
| 12184 | const same_types = for (field_indices[1..]) |field_idx| { | 12292 | capture_src, |
| 12185 | const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]); | 12293 | dummy_captures, |
| 12186 | if (!field_ty.eql(first_field_ty, zcu)) break false; | 12294 | .{ .override = item_srcs }, |
| 12187 | } else true; | 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: { | 12305 | // By-reference captures have some further restrictions which make them easier to emit |
| 12190 | if (same_types) break :capture_ty first_field_ty; | 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 | // We need values to run PTR on, so make a bunch of undef constants. | 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); | 12312 | const dummy_captures = try sema.arena.alloc(Air.Inst.Ref, item_refs.len); |
| 12193 | for (dummy_captures, field_indices) |*dummy, field_idx| { | 12313 | for (field_indices, dummy_captures) |field_index, *dummy| { |
| 12194 | const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]); | 12314 | const field_ptr_ty = try operand_ptr_ty.fieldPtrType(field_index, pt); |
| 12195 | dummy.* = try pt.undefRef(field_ty); | 12315 | dummy.* = try pt.undefRef(field_ptr_ty); |
| 12196 | } | 12316 | } |
| 12197 | | 12317 | const item_srcs = try sema.arena.alloc(?LazySrcLoc, item_refs.len); |
| 12198 | const case_srcs = try sema.arena.alloc(?LazySrcLoc, case_vals.len); | 12318 | for (item_srcs, 0..) |*item_src, item_i| { |
| 12199 | for (case_srcs, 0..) |*case_src, item_i| { | 12319 | item_src.* = .{ |
| 12200 | case_src.* = .{ | | |
| 12201 | .base_node_inst = capture_src.base_node_inst, | 12320 | .base_node_inst = capture_src.base_node_inst, |
| 12202 | .offset = .{ .switch_case_item = .{ | 12321 | .offset = .{ .switch_case_item = .{ |
| 12203 | .switch_node_offset = switch_node_offset, | 12322 | .switch_node_offset = switch_node_offset, |
| ... | @@ -12207,14 +12326,15 @@ fn analyzeSwitchPayloadCapture( | ... | @@ -12207,14 +12326,15 @@ fn analyzeSwitchPayloadCapture( |
| 12207 | }; | 12326 | }; |
| 12208 | } | 12327 | } |
| 12209 | | 12328 | |
| 12210 | break :capture_ty sema.resolvePeerTypes( | 12329 | break :resolve sema.resolvePeerTypes( |
| 12211 | case_block, | 12330 | case_block, |
| 12212 | capture_src, | 12331 | capture_src, |
| 12213 | dummy_captures, | 12332 | dummy_captures, |
| 12214 | .{ .override = case_srcs }, | 12333 | .{ .override = item_srcs }, |
| 12215 | ) catch |err| switch (err) { | 12334 | ) catch |err| switch (err) { |
| 12216 | error.AnalysisFail => { | 12335 | error.AnalysisFail => { |
| 12217 | const msg = sema.err orelse return error.AnalysisFail; | 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 | try sema.reparentOwnedErrorMsg(capture_src, msg, "capture group with incompatible types", .{}); | 12338 | try sema.reparentOwnedErrorMsg(capture_src, msg, "capture group with incompatible types", .{}); |
| 12219 | return error.AnalysisFail; | 12339 | return error.AnalysisFail; |
| 12220 | }, | 12340 | }, |
| ... | @@ -12222,262 +12342,188 @@ fn analyzeSwitchPayloadCapture( | ... | @@ -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 | 12345 | if (try sema.resolveDefinedValue(case_block, operand_src, loaded_operand)) |op_ptr_val| { |
| 12226 | if (capture_by_ref) { | 12346 | if (op_ptr_val.isUndef(zcu)) return pt.undefRef(capture_ptr_ty); |
| 12227 | const operand_ptr_ty = sema.typeOf(operand_ptr); | 12347 | const field_ptr_val = try op_ptr_val.ptrField(first_field_index, pt); |
| 12228 | const capture_ptr_ty = resolve: { | 12348 | return .fromValue(try pt.getCoerced(field_ptr_val, capture_ptr_ty)); |
| 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); | | |
| 12283 | } | 12349 | } |
| 12284 | | 12350 | |
| 12285 | try sema.requireRuntimeBlock(case_block, operand_src, null); | 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) { | 12355 | if (try capture_ty.onePossibleValue(pt)) |opv| return .fromValue(opv); |
| 12288 | return case_block.addStructFieldVal(operand_val, first_field_index, capture_ty); | | |
| 12289 | } | | |
| 12290 | | 12356 | |
| 12291 | // We may have to emit a switch block which coerces the operand to the capture type. | 12357 | if (try sema.resolveDefinedValue(case_block, operand_src, loaded_operand)) |operand_val| { |
| 12292 | // If we can, try to avoid that using in-memory coercions. | 12358 | if (operand_val.isUndef(zcu)) return pt.undefRef(capture_ty); |
| 12293 | const first_non_imc = in_mem: { | 12359 | const union_val = ip.indexToKey(operand_val.toIntern()).un; |
| 12294 | for (field_indices, 0..) |field_idx, i| { | 12360 | if (Value.fromInterned(union_val.tag).isUndef(zcu)) return pt.undefRef(capture_ty); |
| 12295 | const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]); | 12361 | const uncoerced: Air.Inst.Ref = .fromIntern(union_val.val); |
| 12296 | if (.ok != try sema.coerceInMemoryAllowed(case_block, capture_ty, field_ty, false, zcu.getTarget(), .unneeded, .unneeded, null)) { | 12362 | return sema.coerce(case_block, capture_ty, uncoerced, operand_src); |
| 12297 | break :in_mem i; | 12363 | } |
| 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 | }; | | |
| 12305 | | 12364 | |
| 12306 | // By-val capture with heterogeneous types which are not all in-memory coercible to | 12365 | try sema.requireRuntimeBlock(case_block, operand_src, null); |
| 12307 | // the resolved capture type. We finally have to fall back to the ugly method. | | |
| 12308 | | 12366 | |
| 12309 | // However, let's first track which operands are in-memory coercible. There may well | 12367 | if (same_types) { |
| 12310 | // be several, and we can squash all of these cases into the same switch prong using | 12368 | return case_block.addStructFieldVal(loaded_operand, first_field_index, capture_ty); |
| 12311 | // a simple bitcast. We'll make this the 'else' prong. | 12369 | } |
| 12312 | | 12370 | |
| 12313 | var in_mem_coercible: std.bit_set.Dynamic = try .initFull(sema.arena, field_indices.len); | 12371 | // We may have to emit a switch block which coerces the operand to the capture type. |
| 12314 | in_mem_coercible.unset(first_non_imc); | 12372 | // If we can, try to avoid that using in-memory coercions. |
| 12315 | { | 12373 | const first_non_imc = in_mem: { |
| 12316 | const next = first_non_imc + 1; | 12374 | for (field_indices, 0..) |field_idx, i| { |
| 12317 | for (field_indices[next..], next..) |field_idx, i| { | 12375 | const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]); |
| 12318 | 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)) { |
| 12319 | if (.ok != try sema.coerceInMemoryAllowed(case_block, capture_ty, field_ty, false, zcu.getTarget(), .unneeded, .unneeded, null)) { | 12377 | break :in_mem i; |
| 12320 | in_mem_coercible.unset(i); | | |
| 12321 | } | | |
| 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(.{ | 12386 | // By-val capture with heterogeneous types which are not all in-memory coercible to |
| 12326 | .tag = .block, | 12387 | // the resolved capture type. We finally have to fall back to the ugly method. |
| 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(); | | |
| 12336 | | 12388 | |
| 12337 | const estimated_extra = prong_count * 6 + (prong_count / 10); // 2 for Case, 1 item, probably 3 insts; plus hints | 12389 | // However, let's first track which operands are in-memory coercible. There may well |
| 12338 | var cases_extra = try std.array_list.Managed(u32).initCapacity(sema.gpa, estimated_extra); | 12390 | // be several, and we can squash all of these cases into the same switch prong using |
| 12339 | defer cases_extra.deinit(); | 12391 | // a simple bitcast. We'll make this the 'else' prong. |
| 12340 | | 12392 | |
| 12341 | { | 12393 | var in_mem_coercible: std.bit_set.Dynamic = try .initFull(sema.arena, field_indices.len); |
| 12342 | // All branch hints are `.none`, so just add zero elems. | 12394 | in_mem_coercible.unset(first_non_imc); |
| 12343 | comptime assert(@intFromEnum(std.lang.BranchHint.none) == 0); | 12395 | { |
| 12344 | const need_elems = std.math.divCeil(usize, prong_count + 1, 10) catch unreachable; | 12396 | const next = first_non_imc + 1; |
| 12345 | try cases_extra.appendNTimes(0, need_elems); | 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 | { | 12405 | const capture_block_inst = try case_block.addInstAsIndex(.{ |
| 12349 | // Non-bitcast cases | 12406 | .tag = .block, |
| 12350 | var it = in_mem_coercible.iterator(.{ .kind = .unset }); | 12407 | .data = .{ |
| 12351 | while (it.next()) |idx| { | 12408 | .ty_pl = .{ |
| 12352 | var coerce_block = case_block.makeSubBlock(); | 12409 | .ty = .fromType(capture_ty), |
| 12353 | defer coerce_block.instructions.deinit(sema.gpa); | 12410 | .payload = undefined, // updated below |
| | 12411 | }, |
| | 12412 | }, |
| | 12413 | }); |
| 12354 | | 12414 | |
| 12355 | const case_src: LazySrcLoc = .{ | 12415 | const prong_count = field_indices.len - in_mem_coercible.count(); |
| 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 | }; | | |
| 12363 | | 12416 | |
| 12364 | const field_idx = field_indices[idx]; | 12417 | const estimated_extra = prong_count * 6 + (prong_count / 10); // 2 for Case, 1 item, probably 3 insts; plus hints |
| 12365 | const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]); | 12418 | var cases_extra = try std.ArrayList(u32).initCapacity(gpa, estimated_extra); |
| 12366 | const uncoerced = try coerce_block.addStructFieldVal(operand_val, field_idx, field_ty); | 12419 | defer cases_extra.deinit(gpa); |
| 12367 | const coerced = try sema.coerce(&coerce_block, capture_ty, uncoerced, case_src); | | |
| 12368 | _ = try coerce_block.addBr(capture_block_inst, coerced); | | |
| 12369 | | 12420 | |
| 12370 | try cases_extra.ensureUnusedCapacity(@typeInfo(Air.SwitchBr.Case).@"struct".field_names.len + | 12421 | { |
| 12371 | 1 + // `item`, no ranges | 12422 | // All branch hints are `.none`, so just add zero elems. |
| 12372 | coerce_block.instructions.items.len); | 12423 | comptime assert(@intFromEnum(std.lang.BranchHint.none) == 0); |
| 12373 | cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{ | 12424 | const need_elems = std.math.divCeil(usize, prong_count + 1, 10) catch unreachable; |
| 12374 | .items_len = 1, | 12425 | try cases_extra.appendNTimes(gpa, 0, need_elems); |
| 12375 | .ranges_len = 0, | 12426 | } |
| 12376 | .body_len = @intCast(coerce_block.instructions.items.len), | 12427 | |
| 12377 | })); | 12428 | { |
| 12378 | cases_extra.appendAssumeCapacity(@intFromEnum(case_vals[idx])); // item | 12429 | // Non-bitcast cases |
| 12379 | cases_extra.appendSliceAssumeCapacity(@ptrCast(coerce_block.instructions.items)); // body | 12430 | var it = in_mem_coercible.iterator(.{ .kind = .unset }); |
| 12380 | } | 12431 | while (it.next()) |idx| { |
| 12381 | } | | |
| 12382 | const else_body_len = len: { | | |
| 12383 | // 'else' prong uses a bitcast | | |
| 12384 | var coerce_block = case_block.makeSubBlock(); | 12432 | var coerce_block = case_block.makeSubBlock(); |
| 12385 | defer coerce_block.instructions.deinit(sema.gpa); | 12433 | defer coerce_block.instructions.deinit(sema.gpa); |
| 12386 | | 12434 | |
| 12387 | const first_imc_item_idx = in_mem_coercible.findFirstSet().?; | 12435 | const case_src: LazySrcLoc = .{ |
| 12388 | const first_imc_field_idx = field_indices[first_imc_item_idx]; | 12436 | .base_node_inst = capture_src.base_node_inst, |
| 12389 | const first_imc_field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[first_imc_field_idx]); | 12437 | .offset = .{ .switch_case_item = .{ |
| 12390 | const uncoerced = try coerce_block.addStructFieldVal(operand_val, first_imc_field_idx, first_imc_field_ty); | 12438 | .switch_node_offset = switch_node_offset, |
| 12391 | const coerced = try coerce_block.addBitCast(capture_ty, uncoerced); | 12439 | .case_idx = capture_src.offset.switch_capture.case_idx, |
| 12392 | _ = try coerce_block.addBr(capture_block_inst, coerced); | 12440 | .item_idx = .{ .kind = .single, .value = @intCast(idx) }, |
| 12393 | | 12441 | } }, |
| 12394 | try cases_extra.appendSlice(@ptrCast(coerce_block.instructions.items)); | 12442 | }; |
| 12395 | break :len coerce_block.instructions.items.len; | | |
| 12396 | }; | | |
| 12397 | | 12443 | |
| 12398 | try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.SwitchBr).@"struct".field_names.len + | 12444 | const field_idx = field_indices[idx]; |
| 12399 | cases_extra.items.len + | 12445 | const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]); |
| 12400 | @typeInfo(Air.Block).@"struct".field_names.len + | 12446 | const uncoerced = try coerce_block.addStructFieldVal(loaded_operand, field_idx, field_ty); |
| 12401 | 1); | 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); | 12450 | try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".field_names.len + |
| 12404 | try sema.air_instructions.append(sema.gpa, .{ | 12451 | 1 + // `item`, no ranges |
| 12405 | .tag = .switch_br, | 12452 | coerce_block.instructions.items.len); |
| 12406 | .data = .{ | 12453 | cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{ |
| 12407 | .pl_op = .{ | 12454 | .items_len = 1, |
| 12408 | .operand = undefined, // set by switch below | 12455 | .ranges_len = 0, |
| 12409 | .payload = sema.addExtraAssumeCapacity(Air.SwitchBr{ | 12456 | .body_len = @intCast(coerce_block.instructions.items.len), |
| 12410 | .cases_len = @intCast(prong_count), | 12457 | })); |
| 12411 | .else_body_len = @intCast(else_body_len), | 12458 | cases_extra.appendAssumeCapacity(@intFromEnum(item_refs[idx])); // item |
| 12412 | }), | 12459 | cases_extra.appendSliceAssumeCapacity(@ptrCast(coerce_block.instructions.items)); // body |
| 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 | }, | | |
| 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) { | 12467 | const first_imc_item_idx = in_mem_coercible.findFirstSet().?; |
| 12450 | const case_vals = kind.item_refs; | 12468 | const first_imc_field_idx = field_indices[first_imc_item_idx]; |
| 12451 | if (case_vals.len == 1) { | 12469 | const first_imc_field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[first_imc_field_idx]); |
| 12452 | const item_val = sema.resolveValue(case_vals[0]).?; | 12470 | const uncoerced = try coerce_block.addStructFieldVal(loaded_operand, first_imc_field_idx, first_imc_field_ty); |
| 12453 | const item_ty = try pt.singleErrorSetType(item_val.getErrorName(zcu).unwrap().?); | 12471 | const coerced = try coerce_block.addBitCast(capture_ty, uncoerced); |
| 12454 | return sema.bitCast(case_block, item_ty, .fromValue(item_val), operand_src, null); | 12472 | _ = try coerce_block.addBr(capture_block_inst, coerced); |
| 12455 | } | | |
| 12456 | | 12473 | |
| 12457 | var names: InferredErrorSet.NameMap = .{}; | 12474 | try cases_extra.appendSlice(gpa, @ptrCast(coerce_block.instructions.items)); |
| 12458 | try names.ensureUnusedCapacity(sema.arena, case_vals.len); | 12475 | break :len coerce_block.instructions.items.len; |
| 12459 | for (case_vals) |err| { | 12476 | }; |
| 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 | } | | |
| 12466 | | 12477 | |
| 12467 | // In this case the capture value is just the passed-through value of the | 12478 | try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr).@"struct".field_names.len + |
| 12468 | // switch condition. It is comptime-known if there is only one item. | 12479 | cases_extra.items.len + |
| 12469 | if (capture_by_ref) { | 12480 | @typeInfo(Air.Block).@"struct".field_names.len + |
| 12470 | return operand_ptr; | 12481 | 1); |
| 12471 | } | 12482 | |
| 12472 | switch (kind) { | 12483 | const switch_br_inst: u32 = @intCast(sema.air_instructions.len); |
| 12473 | .inline_ref, .special => unreachable, | 12484 | try sema.air_instructions.append(gpa, .{ |
| 12474 | .item_refs => |case_vals| { | 12485 | .tag = .switch_br, |
| 12475 | // If there's only a single item, the capture is comptime-known! | 12486 | .data = .{ |
| 12476 | if (case_vals.len == 1) return case_vals[0]; | 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 | const ResolvedSwitchItem = struct { | 12529 | const ResolvedSwitchItem = struct { |