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