| ... | @@ -10211,15 +10211,17 @@ fn analyzeSwitchBlock( | ... | @@ -10211,15 +10211,17 @@ fn analyzeSwitchBlock( |
| 10211 | const operand_ty = sema.typeOf(val); | 10211 | const operand_ty = sema.typeOf(val); |
| 10212 | operand_ty.assertHasLayout(zcu); | 10212 | operand_ty.assertHasLayout(zcu); |
| 10213 | const maybe_operand_opv = try operand_ty.onePossibleValue(pt); | 10213 | const maybe_operand_opv = try operand_ty.onePossibleValue(pt); |
| 10214 | const init_cond: Air.Inst.Ref, const item_ty: Type = switch (operand_ty.zigTypeTag(zcu)) { | 10214 | const init_cond: Air.Inst.Ref, const item_ty: Type = init: { |
| 10215 | .@"union" => tag: { | 10215 | if (operand_ty.zigTypeTag(zcu) == .@"union" and |
| | 10216 | operand_ty.containerLayout(zcu) != .@"packed") |
| | 10217 | { |
| 10216 | const tag_val = try sema.unionToTag(block, val); | 10218 | const tag_val = try sema.unionToTag(block, val); |
| 10217 | break :tag .{ tag_val, sema.typeOf(tag_val) }; | 10219 | break :init .{ tag_val, sema.typeOf(tag_val) }; |
| 10218 | }, | 10220 | } |
| 10219 | else => .{ | 10221 | break :init .{ |
| 10220 | if (maybe_operand_opv) |operand_opv| .fromValue(operand_opv) else val, | 10222 | if (maybe_operand_opv) |operand_opv| .fromValue(operand_opv) else val, |
| 10221 | operand_ty, | 10223 | operand_ty, |
| 10222 | }, | 10224 | }; |
| 10223 | }; | 10225 | }; |
| 10224 | item_ty.assertHasLayout(zcu); | 10226 | item_ty.assertHasLayout(zcu); |
| 10225 | | 10227 | |
| ... | @@ -10250,7 +10252,8 @@ fn analyzeSwitchBlock( | ... | @@ -10250,7 +10252,8 @@ fn analyzeSwitchBlock( |
| 10250 | | 10252 | |
| 10251 | const raw_operand_ty = sema.typeOf(raw_operand); | 10253 | const raw_operand_ty = sema.typeOf(raw_operand); |
| 10252 | | 10254 | |
| 10253 | const union_originally = operand_ty.zigTypeTag(zcu) == .@"union"; | 10255 | const tagged_union_originally = operand_ty.zigTypeTag(zcu) == .@"union" and |
| | 10256 | operand_ty.containerLayout(zcu) != .@"packed"; |
| 10254 | const err_set = operand_ty.zigTypeTag(zcu) == .error_set; | 10257 | const err_set = operand_ty.zigTypeTag(zcu) == .error_set; |
| 10255 | | 10258 | |
| 10256 | if (item_ty.zigTypeTag(zcu) == .@"enum" and | 10259 | if (item_ty.zigTypeTag(zcu) == .@"enum" and |
| ... | @@ -10305,7 +10308,7 @@ fn analyzeSwitchBlock( | ... | @@ -10305,7 +10308,7 @@ fn analyzeSwitchBlock( |
| 10305 | else | 10308 | else |
| 10306 | .{ new_operand, .none }; | 10309 | .{ new_operand, .none }; |
| 10307 | | 10310 | |
| 10308 | const new_cond_ref = if (union_originally) | 10311 | const new_cond_ref = if (tagged_union_originally) |
| 10309 | try sema.unionToTag(child_block, new_val) | 10312 | try sema.unionToTag(child_block, new_val) |
| 10310 | else | 10313 | else |
| 10311 | new_val; | 10314 | new_val; |
| ... | @@ -10327,9 +10330,26 @@ fn analyzeSwitchBlock( | ... | @@ -10327,9 +10330,26 @@ fn analyzeSwitchBlock( |
| 10327 | unreachable; | 10330 | unreachable; |
| 10328 | } | 10331 | } |
| 10329 | | 10332 | |
| 10330 | if (try item_ty.onePossibleValue(pt)) |item_opv| { | 10333 | const switch_ref: Air.Inst.Ref, const item_has_opv = switch_ref: { |
| | 10334 | const item_opv = try item_ty.onePossibleValue(pt) orelse { |
| | 10335 | assert(maybe_operand_opv == null); // `operand_ty` can only be an OPV type if `item_ty` is one too! |
| | 10336 | const air_ref = try sema.finishSwitchBr( |
| | 10337 | block, |
| | 10338 | child_block, |
| | 10339 | operand, |
| | 10340 | raw_operand_ty, |
| | 10341 | operand_is_ref, |
| | 10342 | merges, |
| | 10343 | switch_inst, |
| | 10344 | zir_switch, |
| | 10345 | validated_switch, |
| | 10346 | ); |
| | 10347 | break :switch_ref .{ air_ref, false }; |
| | 10348 | }; |
| | 10349 | |
| 10331 | // We simplify conditions with OPV to either a `loop` or a `block` since | 10350 | // We simplify conditions with OPV to either a `loop` or a `block` since |
| 10332 | // we cannot switch on a value which doesn't exist at runtime. | 10351 | // we cannot switch on a value which doesn't exist at runtime. |
| | 10352 | |
| 10333 | assert(operand == .loop); // `simple` should have already been comptime-resolved above! | 10353 | assert(operand == .loop); // `simple` should have already been comptime-resolved above! |
| 10334 | | 10354 | |
| 10335 | var case_block = child_block.makeSubBlock(); | 10355 | var case_block = child_block.makeSubBlock(); |
| ... | @@ -10374,7 +10394,7 @@ fn analyzeSwitchBlock( | ... | @@ -10374,7 +10394,7 @@ fn analyzeSwitchBlock( |
| 10374 | unreachable; // malformed validated switch | 10394 | unreachable; // malformed validated switch |
| 10375 | }; | 10395 | }; |
| 10376 | | 10396 | |
| 10377 | const analyze_body = sema.wantSwitchProngBodyAnalysis(.fromValue(item_opv), operand_ty, union_originally, err_set, false); | 10397 | const analyze_body = sema.wantSwitchProngBodyAnalysis(.fromValue(item_opv), operand_ty, tagged_union_originally, err_set, false); |
| 10378 | if (!analyze_body) return .unreachable_value; | 10398 | if (!analyze_body) return .unreachable_value; |
| 10379 | | 10399 | |
| 10380 | if (!(err_set and | 10400 | if (!(err_set and |
| ... | @@ -10384,47 +10404,46 @@ fn analyzeSwitchBlock( | ... | @@ -10384,47 +10404,46 @@ fn analyzeSwitchBlock( |
| 10384 | const payload_inst: Zir.Inst.Index = if (capture != .none) inst: { | 10404 | const payload_inst: Zir.Inst.Index = if (capture != .none) inst: { |
| 10385 | const payload_inst = zir_switch.payload_capture_placeholder.unwrap() orelse switch_inst; | 10405 | const payload_inst = zir_switch.payload_capture_placeholder.unwrap() orelse switch_inst; |
| 10386 | const payload_ref: Air.Inst.Ref = payload_ref: { | 10406 | const payload_ref: Air.Inst.Ref = payload_ref: { |
| 10387 | const item_val: Value = switch (operand_ty.zigTypeTag(zcu)) { | 10407 | const item_val: Value = item_val: { |
| 10388 | .@"union" => item_val: { | 10408 | if (!tagged_union_originally) { |
| 10389 | if (maybe_operand_opv) |operand_opv| { | 10409 | break :item_val item_opv; |
| 10390 | break :item_val .fromInterned(zcu.intern_pool.indexToKey(operand_opv.toIntern()).un.val); | 10410 | } |
| 10391 | } | 10411 | if (maybe_operand_opv) |operand_opv| { |
| 10392 | assert(union_originally); // operand type must be union, otherwise it would be an OPV type here | 10412 | break :item_val .fromInterned(zcu.intern_pool.indexToKey(operand_opv.toIntern()).un.val); |
| 10393 | assert(zir_switch.any_maybe_runtime_capture); // there's a payload capture | 10413 | } |
| 10394 | const operand_val, const operand_ref = switch (operand) { | 10414 | assert(zir_switch.any_maybe_runtime_capture); // there's a payload capture |
| 10395 | .simple => unreachable, | 10415 | const operand_val, const operand_ref = switch (operand) { |
| 10396 | .loop => |l| load_operand: { | 10416 | .simple => unreachable, |
| 10397 | const loaded = try sema.analyzeLoad(block, src, l.operand_alloc, src); | 10417 | .loop => |l| load_operand: { |
| 10398 | if (l.operand_is_ref) { | 10418 | const loaded = try sema.analyzeLoad(block, src, l.operand_alloc, src); |
| 10399 | const by_val = try sema.analyzeLoad(block, src, loaded, src); | 10419 | if (l.operand_is_ref) { |
| 10400 | break :load_operand .{ by_val, loaded }; | 10420 | const by_val = try sema.analyzeLoad(block, src, loaded, src); |
| 10401 | } else { | 10421 | break :load_operand .{ by_val, loaded }; |
| 10402 | break :load_operand .{ loaded, .none }; | 10422 | } else { |
| 10403 | } | 10423 | break :load_operand .{ loaded, .none }; |
| 10404 | }, | 10424 | } |
| 10405 | }; | 10425 | }, |
| 10406 | const prong_kind: SwitchProngKind = kind: { | 10426 | }; |
| 10407 | if (is_inline) break :kind .{ .inline_ref = .fromValue(item_opv) }; | 10427 | const prong_kind: SwitchProngKind = kind: { |
| 10408 | if (is_special) break :kind .special; | 10428 | if (is_inline) break :kind .{ .inline_ref = .fromValue(item_opv) }; |
| 10409 | break :kind .{ .item_refs = &.{.fromValue(item_opv)} }; | 10429 | if (is_special) break :kind .special; |
| 10410 | }; | 10430 | break :kind .{ .item_refs = &.{.fromValue(item_opv)} }; |
| 10411 | break :payload_ref try sema.analyzeSwitchPayloadCapture( | 10431 | }; |
| 10412 | &case_block, | 10432 | break :payload_ref try sema.analyzeSwitchPayloadCapture( |
| 10413 | operand, | 10433 | &case_block, |
| 10414 | operand_val, | 10434 | operand, |
| 10415 | operand_ref, | 10435 | operand_val, |
| 10416 | operand_ty, | 10436 | operand_ref, |
| 10417 | operand_src, | 10437 | operand_ty, |
| 10418 | block.src(.{ .switch_capture = .{ | 10438 | operand_src, |
| 10419 | .switch_node_offset = src_node_offset, | 10439 | block.src(.{ .switch_capture = .{ |
| 10420 | .case_idx = index, | 10440 | .switch_node_offset = src_node_offset, |
| 10421 | } }), | 10441 | .case_idx = index, |
| 10422 | capture == .by_ref, | 10442 | } }), |
| 10423 | prong_kind, | 10443 | capture == .by_ref, |
| 10424 | validated_switch.else_err_ty, | 10444 | prong_kind, |
| 10425 | ); | 10445 | validated_switch.else_err_ty, |
| 10426 | }, | 10446 | ); |
| 10427 | else => item_opv, | | |
| 10428 | }; | 10447 | }; |
| 10429 | break :payload_ref switch (capture) { | 10448 | break :payload_ref switch (capture) { |
| 10430 | .by_val => .fromValue(item_val), | 10449 | .by_val => .fromValue(item_val), |
| ... | @@ -10462,40 +10481,100 @@ fn analyzeSwitchBlock( | ... | @@ -10462,40 +10481,100 @@ fn analyzeSwitchBlock( |
| 10462 | .loop | 10481 | .loop |
| 10463 | else | 10482 | else |
| 10464 | .block; | 10483 | .block; |
| 10465 | const air_loop_ref = try child_block.addInst(.{ | 10484 | const air_ref = try child_block.addInst(.{ |
| 10466 | .tag = air_tag, | 10485 | .tag = air_tag, |
| 10467 | .data = .{ .ty_pl = .{ | 10486 | .data = .{ .ty_pl = .{ |
| 10468 | .ty = .noreturn_type, | 10487 | .ty = .noreturn_type, |
| 10469 | .payload = payload_index, | 10488 | .payload = payload_index, |
| 10470 | } }, | 10489 | } }, |
| 10471 | }); | 10490 | }); |
| 10472 | try sema.fixupSwitchContinues( | 10491 | break :switch_ref .{ air_ref, true }; |
| 10473 | block, | 10492 | }; |
| 10474 | src, | 10493 | |
| 10475 | air_loop_ref, | 10494 | const air_tag = sema.air_instructions.items(.tag)[@intFromEnum(switch_ref.toIndex().?)]; |
| 10476 | operand, | 10495 | switch (air_tag) { |
| 10477 | operand_is_ref, | 10496 | .loop_switch_br, .switch_br => assert(!item_has_opv), |
| 10478 | item_ty, | 10497 | .loop, .block => assert(item_has_opv), |
| 10479 | .opv, | 10498 | else => unreachable, |
| 10480 | zir_switch.any_maybe_runtime_capture, | 10499 | } |
| 10481 | merges, | 10500 | switch (air_tag) { |
| 10482 | ); | 10501 | .loop_switch_br, .loop => assert(merges.extra_insts.items.len > 0), |
| 10483 | return null; | 10502 | .switch_br, .block => assert(merges.extra_insts.items.len == 0), |
| | 10503 | else => unreachable, |
| 10484 | } | 10504 | } |
| 10485 | | 10505 | |
| 10486 | assert(maybe_operand_opv == null); // `operand_ty` can only be an OPV type if `item_ty` is one too! | 10506 | // We're done with analyzing the switch statement! Now all we have to do is |
| | 10507 | // replace the placeholder `br` insts inserted by `zirSwitchContinue`s with |
| | 10508 | // their respective finalized inst pointing back at `switch_ref`. |
| | 10509 | |
| | 10510 | for (merges.extra_insts.items, merges.extra_src_locs.items) |placeholder_inst, dispatch_src| { |
| | 10511 | var replacement_block = block.makeSubBlock(); |
| | 10512 | defer replacement_block.instructions.deinit(gpa); |
| | 10513 | |
| | 10514 | assert(sema.air_instructions.items(.tag)[@intFromEnum(placeholder_inst)] == .br); |
| | 10515 | const new_operand_maybe_ref = sema.air_instructions.items(.data)[@intFromEnum(placeholder_inst)].br.operand; |
| | 10516 | |
| | 10517 | if (zir_switch.any_maybe_runtime_capture and !item_has_opv) { |
| | 10518 | _ = try replacement_block.addBinOp(.store, operand.loop.operand_alloc, new_operand_maybe_ref); |
| | 10519 | } |
| | 10520 | |
| | 10521 | const new_operand_val = if (operand_is_ref) |
| | 10522 | try sema.analyzeLoad(&replacement_block, dispatch_src, new_operand_maybe_ref, dispatch_src) |
| | 10523 | else |
| | 10524 | new_operand_maybe_ref; |
| | 10525 | |
| | 10526 | const new_cond = try sema.coerce(&replacement_block, item_ty, new_operand_val, dispatch_src); |
| | 10527 | |
| | 10528 | if (zcu.backendSupportsFeature(.is_named_enum_value) and block.wantSafety() and |
| | 10529 | item_ty.zigTypeTag(zcu) == .@"enum" and !item_ty.isNonexhaustiveEnum(zcu) and |
| | 10530 | !item_has_opv and !try sema.isComptimeKnown(new_cond)) |
| | 10531 | { |
| | 10532 | const ok = try replacement_block.addUnOp(.is_named_enum_value, new_cond); |
| | 10533 | try sema.addSafetyCheck(&replacement_block, src, ok, .corrupt_switch); |
| | 10534 | } |
| | 10535 | |
| | 10536 | if (item_has_opv) { |
| | 10537 | _ = try replacement_block.addInst(.{ |
| | 10538 | .tag = .repeat, |
| | 10539 | .data = .{ .repeat = .{ |
| | 10540 | .loop_inst = switch_ref.toIndex().?, |
| | 10541 | } }, |
| | 10542 | }); |
| | 10543 | } else { |
| | 10544 | _ = try replacement_block.addInst(.{ |
| | 10545 | .tag = .switch_dispatch, |
| | 10546 | .data = .{ .br = .{ |
| | 10547 | .block_inst = switch_ref.toIndex().?, |
| | 10548 | .operand = new_cond, |
| | 10549 | } }, |
| | 10550 | }); |
| | 10551 | } |
| | 10552 | |
| | 10553 | if (replacement_block.instructions.items.len == 1) { |
| | 10554 | // Optimization: we don't need a block! |
| | 10555 | sema.air_instructions.set( |
| | 10556 | @intFromEnum(placeholder_inst), |
| | 10557 | sema.air_instructions.get(@intFromEnum(replacement_block.instructions.items[0])), |
| | 10558 | ); |
| | 10559 | continue; |
| | 10560 | } |
| | 10561 | |
| | 10562 | // Replace placeholder with a block. |
| | 10563 | // No `br` is needed as the block is a switch dispatch so necessarily `noreturn`. |
| | 10564 | try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).@"struct".fields.len + |
| | 10565 | replacement_block.instructions.items.len); |
| | 10566 | sema.air_instructions.set(@intFromEnum(placeholder_inst), .{ |
| | 10567 | .tag = .block, |
| | 10568 | .data = .{ .ty_pl = .{ |
| | 10569 | .ty = .noreturn_type, |
| | 10570 | .payload = sema.addExtraAssumeCapacity(Air.Block{ |
| | 10571 | .body_len = @intCast(replacement_block.instructions.items.len), |
| | 10572 | }), |
| | 10573 | } }, |
| | 10574 | }); |
| | 10575 | sema.air_extra.appendSliceAssumeCapacity(@ptrCast(replacement_block.instructions.items)); |
| | 10576 | } |
| 10487 | | 10577 | |
| 10488 | try sema.finishSwitchBr( | | |
| 10489 | block, | | |
| 10490 | child_block, | | |
| 10491 | operand, | | |
| 10492 | raw_operand_ty, | | |
| 10493 | operand_is_ref, | | |
| 10494 | merges, | | |
| 10495 | switch_inst, | | |
| 10496 | zir_switch, | | |
| 10497 | validated_switch, | | |
| 10498 | ); | | |
| 10499 | return null; | 10578 | return null; |
| 10500 | } | 10579 | } |
| 10501 | | 10580 | |
| ... | @@ -10510,7 +10589,7 @@ fn finishSwitchBr( | ... | @@ -10510,7 +10589,7 @@ fn finishSwitchBr( |
| 10510 | switch_inst: Zir.Inst.Index, | 10589 | switch_inst: Zir.Inst.Index, |
| 10511 | zir_switch: *const Zir.UnwrappedSwitchBlock, | 10590 | zir_switch: *const Zir.UnwrappedSwitchBlock, |
| 10512 | validated_switch: *const ValidatedSwitchBlock, | 10591 | validated_switch: *const ValidatedSwitchBlock, |
| 10513 | ) CompileError!void { | 10592 | ) CompileError!Air.Inst.Ref { |
| 10514 | const pt = sema.pt; | 10593 | const pt = sema.pt; |
| 10515 | const zcu = pt.zcu; | 10594 | const zcu = pt.zcu; |
| 10516 | const ip = &zcu.intern_pool; | 10595 | const ip = &zcu.intern_pool; |
| ... | @@ -10544,13 +10623,15 @@ fn finishSwitchBr( | ... | @@ -10544,13 +10623,15 @@ fn finishSwitchBr( |
| 10544 | | 10623 | |
| 10545 | const else_is_named_only = has_else and has_under; | 10624 | const else_is_named_only = has_else and has_under; |
| 10546 | | 10625 | |
| 10547 | const item_ty = switch (operand_ty.zigTypeTag(zcu)) { | 10626 | const tagged_union_originally = operand_ty.zigTypeTag(zcu) == .@"union" and |
| 10548 | .@"union" => operand_ty.unionTagType(zcu).?, | 10627 | operand_ty.containerLayout(zcu) != .@"packed"; |
| 10549 | else => operand_ty, | | |
| 10550 | }; | | |
| 10551 | const union_originally = operand_ty.zigTypeTag(zcu) == .@"union"; | | |
| 10552 | const err_set = operand_ty.zigTypeTag(zcu) == .error_set; | 10628 | const err_set = operand_ty.zigTypeTag(zcu) == .error_set; |
| 10553 | | 10629 | |
| | 10630 | const item_ty = if (tagged_union_originally) |
| | 10631 | operand_ty.unionTagType(zcu).? |
| | 10632 | else |
| | 10633 | operand_ty; |
| | 10634 | |
| 10554 | const estimated_cases_len: u32 = scalar_cases_len + multi_cases_len + | 10635 | const estimated_cases_len: u32 = scalar_cases_len + multi_cases_len + |
| 10555 | @intFromBool(has_else or has_under); | 10636 | @intFromBool(has_else or has_under); |
| 10556 | | 10637 | |
| ... | @@ -10632,7 +10713,7 @@ fn finishSwitchBr( | ... | @@ -10632,7 +10713,7 @@ fn finishSwitchBr( |
| 10632 | if (item_ref == .none) is_under_prong = true; | 10713 | if (item_ref == .none) is_under_prong = true; |
| 10633 | if (item_info.bodyLen()) |body_len| extra_index += body_len; | 10714 | if (item_info.bodyLen()) |body_len| extra_index += body_len; |
| 10634 | | 10715 | |
| 10635 | const analyze_body = sema.wantSwitchProngBodyAnalysis(item_ref, operand_ty, union_originally, err_set, prong_info.is_comptime_unreach); | 10716 | const analyze_body = sema.wantSwitchProngBodyAnalysis(item_ref, operand_ty, tagged_union_originally, err_set, prong_info.is_comptime_unreach); |
| 10636 | if (analyze_body) any_analyze_body = true; | 10717 | if (analyze_body) any_analyze_body = true; |
| 10637 | | 10718 | |
| 10638 | if (prong_info.is_inline) { | 10719 | if (prong_info.is_inline) { |
| ... | @@ -10840,9 +10921,8 @@ fn finishSwitchBr( | ... | @@ -10840,9 +10921,8 @@ fn finishSwitchBr( |
| 10840 | const else_prong_src = block.src(.{ .node_offset_switch_else_prong = src_node_offset }); | 10921 | const else_prong_src = block.src(.{ .node_offset_switch_else_prong = src_node_offset }); |
| 10841 | const error_names, const min_int = check_enumerable: { | 10922 | const error_names, const min_int = check_enumerable: { |
| 10842 | switch (item_ty.zigTypeTag(zcu)) { | 10923 | switch (item_ty.zigTypeTag(zcu)) { |
| 10843 | .@"union" => unreachable, | | |
| 10844 | .@"enum" => if (else_is_named_only or | 10924 | .@"enum" => if (else_is_named_only or |
| 10845 | !item_ty.isNonexhaustiveEnum(zcu) or union_originally) | 10925 | !item_ty.isNonexhaustiveEnum(zcu) or tagged_union_originally) |
| 10846 | { | 10926 | { |
| 10847 | try branch_hints.ensureUnusedCapacity(gpa, @intCast(validated_switch.seen_enum_fields.len)); | 10927 | try branch_hints.ensureUnusedCapacity(gpa, @intCast(validated_switch.seen_enum_fields.len)); |
| 10848 | break :check_enumerable .{ undefined, undefined }; | 10928 | break :check_enumerable .{ undefined, undefined }; |
| ... | @@ -10856,6 +10936,11 @@ fn finishSwitchBr( | ... | @@ -10856,6 +10936,11 @@ fn finishSwitchBr( |
| 10856 | const min_int = try item_ty.minInt(pt, item_ty); | 10936 | const min_int = try item_ty.minInt(pt, item_ty); |
| 10857 | break :check_enumerable .{ undefined, min_int }; | 10937 | break :check_enumerable .{ undefined, min_int }; |
| 10858 | }, | 10938 | }, |
| | 10939 | .@"union", .@"struct" => { |
| | 10940 | const backing_int_ty = item_ty.bitpackBackingInt(zcu); |
| | 10941 | const min_backing_int = try backing_int_ty.minInt(pt, backing_int_ty); |
| | 10942 | break :check_enumerable .{ undefined, min_backing_int }; |
| | 10943 | }, |
| 10859 | .bool, .void => break :check_enumerable .{ undefined, undefined }, | 10944 | .bool, .void => break :check_enumerable .{ undefined, undefined }, |
| 10860 | else => {}, | 10945 | else => {}, |
| 10861 | } | 10946 | } |
| ... | @@ -10871,7 +10956,7 @@ fn finishSwitchBr( | ... | @@ -10871,7 +10956,7 @@ fn finishSwitchBr( |
| 10871 | | 10956 | |
| 10872 | const item_ref: Air.Inst.Ref = .fromValue(item_val); | 10957 | const item_ref: Air.Inst.Ref = .fromValue(item_val); |
| 10873 | | 10958 | |
| 10874 | const analyze_body = sema.wantSwitchProngBodyAnalysis(item_ref, operand_ty, union_originally, err_set, false); | 10959 | const analyze_body = sema.wantSwitchProngBodyAnalysis(item_ref, operand_ty, tagged_union_originally, err_set, false); |
| 10875 | | 10960 | |
| 10876 | if (emit_bb) try sema.emitBackwardBranch(block, else_prong_src); | 10961 | if (emit_bb) try sema.emitBackwardBranch(block, else_prong_src); |
| 10877 | emit_bb = true; | 10962 | emit_bb = true; |
| ... | @@ -10918,13 +11003,11 @@ fn finishSwitchBr( | ... | @@ -10918,13 +11003,11 @@ fn finishSwitchBr( |
| 10918 | if (zcu.backendSupportsFeature(.is_named_enum_value) and | 11003 | if (zcu.backendSupportsFeature(.is_named_enum_value) and |
| 10919 | (has_else or has_under) and block.wantSafety() and | 11004 | (has_else or has_under) and block.wantSafety() and |
| 10920 | item_ty.zigTypeTag(zcu) == .@"enum" and | 11005 | item_ty.zigTypeTag(zcu) == .@"enum" and |
| 10921 | (!operand_ty.isNonexhaustiveEnum(zcu) or union_originally)) | 11006 | (!operand_ty.isNonexhaustiveEnum(zcu) or tagged_union_originally)) |
| 10922 | { | 11007 | { |
| 10923 | try sema.zirDbgStmt(&case_block, cond_dbg_node_index); | 11008 | try sema.zirDbgStmt(&case_block, cond_dbg_node_index); |
| 10924 | const ok = try case_block.addUnOp(.is_named_enum_value, cond_ref); | 11009 | const ok = try case_block.addUnOp(.is_named_enum_value, cond_ref); |
| 10925 | if (else_is_named_only) {} else { | 11010 | try sema.addSafetyCheck(&case_block, src, ok, .corrupt_switch); |
| 10926 | try sema.addSafetyCheck(&case_block, src, ok, .corrupt_switch); | | |
| 10927 | } | | |
| 10928 | } | 11011 | } |
| 10929 | | 11012 | |
| 10930 | if (else_is_named_only and !else_case.is_inline) { | 11013 | if (else_is_named_only and !else_case.is_inline) { |
| ... | @@ -10991,13 +11074,15 @@ fn finishSwitchBr( | ... | @@ -10991,13 +11074,15 @@ fn finishSwitchBr( |
| 10991 | | 11074 | |
| 10992 | const analyze_catch_all_body = analyze_body: { | 11075 | const analyze_catch_all_body = analyze_body: { |
| 10993 | if (has_under) { | 11076 | if (has_under) { |
| 10994 | break :analyze_body true; // can't be a union or an error set, never inlined | 11077 | assert(!tagged_union_originally); |
| | 11078 | assert(!err_set); |
| | 11079 | break :analyze_body true; // can never be inline |
| 10995 | } else if (has_else) { | 11080 | } else if (has_else) { |
| 10996 | if (else_case.is_inline) break :analyze_body false; // already handled above | 11081 | if (else_case.is_inline) break :analyze_body false; // already handled above |
| 10997 | } else { | 11082 | } else { |
| 10998 | break :analyze_body false; // we still may want a safety check! | 11083 | break :analyze_body false; // we still may want a safety check! |
| 10999 | } | 11084 | } |
| 11000 | if (union_originally) { | 11085 | if (tagged_union_originally) { |
| 11001 | const union_obj = zcu.typeToUnion(operand_ty).?; | 11086 | const union_obj = zcu.typeToUnion(operand_ty).?; |
| 11002 | for (validated_switch.seen_enum_fields, 0..) |seen_field, field_i| { | 11087 | for (validated_switch.seen_enum_fields, 0..) |seen_field, field_i| { |
| 11003 | if (seen_field != null) continue; | 11088 | if (seen_field != null) continue; |
| ... | @@ -11072,126 +11157,14 @@ fn finishSwitchBr( | ... | @@ -11072,126 +11157,14 @@ fn finishSwitchBr( |
| 11072 | .loop_switch_br | 11157 | .loop_switch_br |
| 11073 | else | 11158 | else |
| 11074 | .switch_br; | 11159 | .switch_br; |
| 11075 | const air_switch_ref = try child_block.addInst(.{ | 11160 | const air_ref = try child_block.addInst(.{ |
| 11076 | .tag = air_tag, | 11161 | .tag = air_tag, |
| 11077 | .data = .{ .pl_op = .{ | 11162 | .data = .{ .pl_op = .{ |
| 11078 | .operand = cond_ref, | 11163 | .operand = cond_ref, |
| 11079 | .payload = payload_index, | 11164 | .payload = payload_index, |
| 11080 | } }, | 11165 | } }, |
| 11081 | }); | 11166 | }); |
| 11082 | try sema.fixupSwitchContinues( | 11167 | return air_ref; |
| 11083 | block, | | |
| 11084 | src, | | |
| 11085 | air_switch_ref, | | |
| 11086 | operand, | | |
| 11087 | operand_is_ref, | | |
| 11088 | item_ty, | | |
| 11089 | .normal, | | |
| 11090 | zir_switch.any_maybe_runtime_capture, | | |
| 11091 | merges, | | |
| 11092 | ); | | |
| 11093 | } | | |
| 11094 | | | |
| 11095 | /// This is the counterpart to `zirSwitchContinue`; replaces placeholder `br` insts | | |
| 11096 | /// with their respective finalized inst pointing back at `switch_ref`. | | |
| 11097 | fn fixupSwitchContinues( | | |
| 11098 | sema: *Sema, | | |
| 11099 | block: *Block, | | |
| 11100 | switch_src: LazySrcLoc, | | |
| 11101 | switch_ref: Air.Inst.Ref, | | |
| 11102 | operand: SwitchOperand, | | |
| 11103 | operand_is_ref: bool, | | |
| 11104 | item_ty: Type, | | |
| 11105 | mode: enum { normal, opv }, | | |
| 11106 | any_maybe_runtime_capture: bool, | | |
| 11107 | merges: *const Block.Merges, | | |
| 11108 | ) CompileError!void { | | |
| 11109 | const pt = sema.pt; | | |
| 11110 | const zcu = pt.zcu; | | |
| 11111 | const gpa = sema.gpa; | | |
| 11112 | | | |
| 11113 | const air_tag = sema.air_instructions.items(.tag)[@intFromEnum(switch_ref.toIndex().?)]; | | |
| 11114 | switch (air_tag) { | | |
| 11115 | .loop_switch_br, .switch_br => assert(mode == .normal), | | |
| 11116 | .loop, .block => assert(mode == .opv), | | |
| 11117 | else => unreachable, | | |
| 11118 | } | | |
| 11119 | switch (air_tag) { | | |
| 11120 | .loop_switch_br, .loop => assert(merges.extra_insts.items.len > 0), | | |
| 11121 | .switch_br, .block => assert(merges.extra_insts.items.len == 0), | | |
| 11122 | else => unreachable, | | |
| 11123 | } | | |
| 11124 | | | |
| 11125 | for (merges.extra_insts.items, merges.extra_src_locs.items) |placeholder_inst, dispatch_src| { | | |
| 11126 | var replacement_block = block.makeSubBlock(); | | |
| 11127 | defer replacement_block.instructions.deinit(gpa); | | |
| 11128 | | | |
| 11129 | assert(sema.air_instructions.items(.tag)[@intFromEnum(placeholder_inst)] == .br); | | |
| 11130 | const new_operand_maybe_ref = sema.air_instructions.items(.data)[@intFromEnum(placeholder_inst)].br.operand; | | |
| 11131 | | | |
| 11132 | if (any_maybe_runtime_capture and mode != .opv) { | | |
| 11133 | _ = try replacement_block.addBinOp(.store, operand.loop.operand_alloc, new_operand_maybe_ref); | | |
| 11134 | } | | |
| 11135 | | | |
| 11136 | const new_operand_val = if (operand_is_ref) | | |
| 11137 | try sema.analyzeLoad(&replacement_block, dispatch_src, new_operand_maybe_ref, dispatch_src) | | |
| 11138 | else | | |
| 11139 | new_operand_maybe_ref; | | |
| 11140 | | | |
| 11141 | const new_cond = try sema.coerce(&replacement_block, item_ty, new_operand_val, dispatch_src); | | |
| 11142 | | | |
| 11143 | if (zcu.backendSupportsFeature(.is_named_enum_value) and block.wantSafety() and | | |
| 11144 | item_ty.zigTypeTag(zcu) == .@"enum" and !item_ty.isNonexhaustiveEnum(zcu) and | | |
| 11145 | mode == .normal and !try sema.isComptimeKnown(new_cond)) | | |
| 11146 | { | | |
| 11147 | const ok = try replacement_block.addUnOp(.is_named_enum_value, new_cond); | | |
| 11148 | try sema.addSafetyCheck(&replacement_block, switch_src, ok, .corrupt_switch); | | |
| 11149 | } | | |
| 11150 | | | |
| 11151 | switch (mode) { | | |
| 11152 | .normal => { | | |
| 11153 | _ = try replacement_block.addInst(.{ | | |
| 11154 | .tag = .switch_dispatch, | | |
| 11155 | .data = .{ .br = .{ | | |
| 11156 | .block_inst = switch_ref.toIndex().?, | | |
| 11157 | .operand = new_cond, | | |
| 11158 | } }, | | |
| 11159 | }); | | |
| 11160 | }, | | |
| 11161 | .opv => { | | |
| 11162 | _ = try replacement_block.addInst(.{ | | |
| 11163 | .tag = .repeat, | | |
| 11164 | .data = .{ .repeat = .{ | | |
| 11165 | .loop_inst = switch_ref.toIndex().?, | | |
| 11166 | } }, | | |
| 11167 | }); | | |
| 11168 | }, | | |
| 11169 | } | | |
| 11170 | | | |
| 11171 | if (replacement_block.instructions.items.len == 1) { | | |
| 11172 | // Optimization: we don't need a block! | | |
| 11173 | sema.air_instructions.set( | | |
| 11174 | @intFromEnum(placeholder_inst), | | |
| 11175 | sema.air_instructions.get(@intFromEnum(replacement_block.instructions.items[0])), | | |
| 11176 | ); | | |
| 11177 | continue; | | |
| 11178 | } | | |
| 11179 | | | |
| 11180 | // Replace placeholder with a block. | | |
| 11181 | // No `br` is needed as the block is a switch dispatch so necessarily `noreturn`. | | |
| 11182 | try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).@"struct".fields.len + | | |
| 11183 | replacement_block.instructions.items.len); | | |
| 11184 | sema.air_instructions.set(@intFromEnum(placeholder_inst), .{ | | |
| 11185 | .tag = .block, | | |
| 11186 | .data = .{ .ty_pl = .{ | | |
| 11187 | .ty = .noreturn_type, | | |
| 11188 | .payload = sema.addExtraAssumeCapacity(Air.Block{ | | |
| 11189 | .body_len = @intCast(replacement_block.instructions.items.len), | | |
| 11190 | }), | | |
| 11191 | } }, | | |
| 11192 | }); | | |
| 11193 | sema.air_extra.appendSliceAssumeCapacity(@ptrCast(replacement_block.instructions.items)); | | |
| 11194 | } | | |
| 11195 | } | 11168 | } |
| 11196 | | 11169 | |
| 11197 | const ValidatedSwitchBlock = struct { | 11170 | const ValidatedSwitchBlock = struct { |
| ... | @@ -11242,7 +11215,6 @@ const ValidatedSwitchBlock = struct { | ... | @@ -11242,7 +11215,6 @@ const ValidatedSwitchBlock = struct { |
| 11242 | const zcu = pt.zcu; | 11215 | const zcu = pt.zcu; |
| 11243 | const ip = &zcu.intern_pool; | 11216 | const ip = &zcu.intern_pool; |
| 11244 | switch (item_ty.zigTypeTag(zcu)) { | 11217 | switch (item_ty.zigTypeTag(zcu)) { |
| 11245 | .@"union" => unreachable, | | |
| 11246 | .@"enum" => { | 11218 | .@"enum" => { |
| 11247 | for (it.seen_enum_fields[it.next_idx..], it.next_idx..) |seen_field, field_i| { | 11219 | for (it.seen_enum_fields[it.next_idx..], it.next_idx..) |seen_field, field_i| { |
| 11248 | if (seen_field != null) continue; | 11220 | if (seen_field != null) continue; |
| ... | @@ -11262,26 +11234,35 @@ const ValidatedSwitchBlock = struct { | ... | @@ -11262,26 +11234,35 @@ const ValidatedSwitchBlock = struct { |
| 11262 | } | 11234 | } |
| 11263 | return null; | 11235 | return null; |
| 11264 | }, | 11236 | }, |
| 11265 | .int => { | 11237 | .int, .@"union", .@"struct" => |type_tag| { |
| 11266 | var cur = it.next_val orelse return null; | 11238 | var cur_val = it.next_val orelse return null; |
| | 11239 | const int_ty = switch (type_tag) { |
| | 11240 | .int => item_ty, |
| | 11241 | .@"union", .@"struct" => item_ty.bitpackBackingInt(zcu), |
| | 11242 | else => unreachable, |
| | 11243 | }; |
| 11267 | while (it.next_idx < it.seen_ranges.len and | 11244 | while (it.next_idx < it.seen_ranges.len and |
| 11268 | cur.eql(it.seen_ranges[it.next_idx].first, item_ty, zcu)) | 11245 | cur_val.eql(it.seen_ranges[it.next_idx].first, int_ty, zcu)) |
| 11269 | { | 11246 | { |
| 11270 | defer it.next_idx += 1; | 11247 | defer it.next_idx += 1; |
| 11271 | const incr = try arith.incrementDefinedInt( | 11248 | const incr = try arith.incrementDefinedInt( |
| 11272 | sema, | 11249 | sema, |
| 11273 | item_ty, | 11250 | int_ty, |
| 11274 | it.seen_ranges[it.next_idx].last, | 11251 | it.seen_ranges[it.next_idx].last, |
| 11275 | ); | 11252 | ); |
| 11276 | if (incr.overflow) { | 11253 | if (incr.overflow) { |
| 11277 | it.next_val = null; | 11254 | it.next_val = null; |
| 11278 | return null; | 11255 | return null; |
| 11279 | } | 11256 | } |
| 11280 | cur = incr.val; | 11257 | cur_val = incr.val; |
| 11281 | } | 11258 | } |
| 11282 | const incr = try arith.incrementDefinedInt(sema, item_ty, cur); | 11259 | const incr = try arith.incrementDefinedInt(sema, int_ty, cur_val); |
| 11283 | it.next_val = if (incr.overflow) null else incr.val; | 11260 | it.next_val = if (incr.overflow) null else incr.val; |
| 11284 | return cur; | 11261 | return switch (type_tag) { |
| | 11262 | .int => cur_val, |
| | 11263 | .@"union", .@"struct" => try pt.bitpackValue(item_ty, cur_val), |
| | 11264 | else => unreachable, |
| | 11265 | }; |
| 11285 | }, | 11266 | }, |
| 11286 | .bool => { | 11267 | .bool => { |
| 11287 | if (!it.seen_true) { | 11268 | if (!it.seen_true) { |
| ... | @@ -11366,17 +11347,43 @@ fn validateSwitchBlock( | ... | @@ -11366,17 +11347,43 @@ fn validateSwitchBlock( |
| 11366 | => break :item_ty operand_ty, | 11347 | => break :item_ty operand_ty, |
| 11367 | | 11348 | |
| 11368 | .@"union" => { | 11349 | .@"union" => { |
| 11369 | const enum_ty = operand_ty.unionTagType(zcu) orelse { | 11350 | operand_ty.assertHasLayout(zcu); |
| 11370 | return sema.failWithOwnedErrorMsg(block, msg: { | 11351 | const union_obj = ip.loadUnionType(operand_ty.toIntern()); |
| 11371 | const msg = try sema.errMsg(operand_src, "switch on union with no attached enum", .{}); | 11352 | switch (union_obj.tag_usage) { |
| 11372 | errdefer msg.destroy(sema.gpa); | 11353 | .tagged => { |
| 11373 | if (operand_ty.srcLocOrNull(zcu)) |union_src| { | 11354 | break :item_ty .fromInterned(union_obj.enum_tag_type); |
| 11374 | try sema.errNote(union_src, msg, "consider 'union(enum)' here", .{}); | 11355 | }, |
| | 11356 | .none => { |
| | 11357 | if (union_obj.layout == .@"packed") { |
| | 11358 | break :item_ty operand_ty; |
| 11375 | } | 11359 | } |
| 11376 | break :msg msg; | 11360 | }, |
| 11377 | }); | 11361 | .safety => {}, |
| 11378 | }; | 11362 | } |
| 11379 | break :item_ty enum_ty; | 11363 | return sema.failWithOwnedErrorMsg(block, msg: { |
| | 11364 | const msg = try sema.errMsg(operand_src, "switch on union with no attached enum", .{}); |
| | 11365 | errdefer msg.destroy(sema.gpa); |
| | 11366 | if (operand_ty.srcLocOrNull(zcu)) |union_src| { |
| | 11367 | try sema.errNote(union_src, msg, "consider 'union(enum)' here", .{}); |
| | 11368 | } |
| | 11369 | break :msg msg; |
| | 11370 | }); |
| | 11371 | }, |
| | 11372 | |
| | 11373 | .@"struct" => { |
| | 11374 | operand_ty.assertHasLayout(zcu); |
| | 11375 | const layout = operand_ty.containerLayout(zcu); |
| | 11376 | if (layout == .@"packed") { |
| | 11377 | break :item_ty operand_ty; |
| | 11378 | } |
| | 11379 | return sema.failWithOwnedErrorMsg(block, msg: { |
| | 11380 | const msg = try sema.errMsg(operand_src, "switch on struct with {t} layout", .{layout}); |
| | 11381 | errdefer msg.destroy(sema.gpa); |
| | 11382 | if (operand_ty.srcLocOrNull(zcu)) |struct_src| { |
| | 11383 | try sema.errNote(struct_src, msg, "consider 'packed struct' here", .{}); |
| | 11384 | } |
| | 11385 | break :msg msg; |
| | 11386 | }); |
| 11380 | }, | 11387 | }, |
| 11381 | | 11388 | |
| 11382 | .pointer => { | 11389 | .pointer => { |
| ... | @@ -11424,7 +11431,6 @@ fn validateSwitchBlock( | ... | @@ -11424,7 +11431,6 @@ fn validateSwitchBlock( |
| 11424 | const else_case = zir_switch.else_case orelse undefined; | 11431 | const else_case = zir_switch.else_case orelse undefined; |
| 11425 | | 11432 | |
| 11426 | switch (item_ty.zigTypeTag(zcu)) { | 11433 | switch (item_ty.zigTypeTag(zcu)) { |
| 11427 | .@"union" => unreachable, | | |
| 11428 | .@"enum" => { | 11434 | .@"enum" => { |
| 11429 | seen_enum_fields = try arena.alloc(?LazySrcLoc, item_ty.enumFieldCount(zcu)); | 11435 | seen_enum_fields = try arena.alloc(?LazySrcLoc, item_ty.enumFieldCount(zcu)); |
| 11430 | @memset(seen_enum_fields, null); | 11436 | @memset(seen_enum_fields, null); |
| ... | @@ -11435,7 +11441,7 @@ fn validateSwitchBlock( | ... | @@ -11435,7 +11441,7 @@ fn validateSwitchBlock( |
| 11435 | .error_set => { | 11441 | .error_set => { |
| 11436 | try seen_errors.ensureUnusedCapacity(arena, zir_switch.totalItemsLen()); | 11442 | try seen_errors.ensureUnusedCapacity(arena, zir_switch.totalItemsLen()); |
| 11437 | }, | 11443 | }, |
| 11438 | .int, .comptime_int => { | 11444 | .int, .comptime_int, .@"union", .@"struct" => { |
| 11439 | try range_set.ensureUnusedCapacity(arena, zir_switch.totalItemsLen()); | 11445 | try range_set.ensureUnusedCapacity(arena, zir_switch.totalItemsLen()); |
| 11440 | }, | 11446 | }, |
| 11441 | .enum_literal, .@"fn", .pointer, .type => { | 11447 | .enum_literal, .@"fn", .pointer, .type => { |
| ... | @@ -11503,7 +11509,6 @@ fn validateSwitchBlock( | ... | @@ -11503,7 +11509,6 @@ fn validateSwitchBlock( |
| 11503 | } | 11509 | } |
| 11504 | | 11510 | |
| 11505 | switch (item_ty.zigTypeTag(zcu)) { | 11511 | switch (item_ty.zigTypeTag(zcu)) { |
| 11506 | .@"union" => unreachable, | | |
| 11507 | .int, .comptime_int => {}, | 11512 | .int, .comptime_int => {}, |
| 11508 | else => if (zir_switch.anyRanges()) { | 11513 | else => if (zir_switch.anyRanges()) { |
| 11509 | const range_src = block.src(.{ .node_offset_switch_range = src_node_offset }); | 11514 | const range_src = block.src(.{ .node_offset_switch_range = src_node_offset }); |
| ... | @@ -11528,7 +11533,6 @@ fn validateSwitchBlock( | ... | @@ -11528,7 +11533,6 @@ fn validateSwitchBlock( |
| 11528 | | 11533 | |
| 11529 | // Validate for missing special prongs. | 11534 | // Validate for missing special prongs. |
| 11530 | switch (item_ty.zigTypeTag(zcu)) { | 11535 | switch (item_ty.zigTypeTag(zcu)) { |
| 11531 | .@"union" => unreachable, | | |
| 11532 | .@"enum" => { | 11536 | .@"enum" => { |
| 11533 | const all_tags_handled = for (seen_enum_fields) |seen_src| { | 11537 | const all_tags_handled = for (seen_enum_fields) |seen_src| { |
| 11534 | if (seen_src == null) break false; | 11538 | if (seen_src == null) break false; |
| ... | @@ -11661,22 +11665,26 @@ fn validateSwitchBlock( | ... | @@ -11661,22 +11665,26 @@ fn validateSwitchBlock( |
| 11661 | }, | 11665 | }, |
| 11662 | }; | 11666 | }; |
| 11663 | }, | 11667 | }, |
| 11664 | .int, .comptime_int => |type_tag| { | 11668 | .int, .comptime_int, .@"union", .@"struct" => |type_tag| { |
| 11665 | check_range: { | 11669 | check_range: { |
| 11666 | if (type_tag == .int) { | 11670 | const int_ty = switch (type_tag) { |
| 11667 | const min_int = try item_ty.minInt(pt, item_ty); | 11671 | .comptime_int => break :check_range, // comptime_int has 'infinite' range |
| 11668 | const max_int = try item_ty.maxInt(pt, item_ty); | 11672 | .int => item_ty, |
| 11669 | if (try range_set.spans(arena, min_int, max_int, item_ty, zcu)) { | 11673 | .@"union", .@"struct" => item_ty.bitpackBackingInt(zcu), |
| 11670 | if (has_else) { | 11674 | else => unreachable, |
| 11671 | return sema.fail( | 11675 | }; |
| 11672 | block, | 11676 | const min_int = try int_ty.minInt(pt, int_ty); |
| 11673 | else_prong_src, | 11677 | const max_int = try int_ty.maxInt(pt, int_ty); |
| 11674 | "unreachable else prong; all cases already handled", | 11678 | if (try range_set.spans(arena, min_int, max_int, int_ty, zcu)) { |
| 11675 | .{}, | 11679 | if (has_else) { |
| 11676 | ); | 11680 | return sema.fail( |
| 11677 | } | 11681 | block, |
| 11678 | break :check_range; | 11682 | else_prong_src, |
| | 11683 | "unreachable else prong; all cases already handled", |
| | 11684 | .{}, |
| | 11685 | ); |
| 11679 | } | 11686 | } |
| | 11687 | break :check_range; |
| 11680 | } | 11688 | } |
| 11681 | if (!has_else) { | 11689 | if (!has_else) { |
| 11682 | return sema.fail( | 11690 | return sema.fail( |
| ... | @@ -11759,12 +11767,15 @@ fn resolveSwitchBlock( | ... | @@ -11759,12 +11767,15 @@ fn resolveSwitchBlock( |
| 11759 | const switch_node_offset = zir_switch.switch_src_node_offset; | 11767 | const switch_node_offset = zir_switch.switch_src_node_offset; |
| 11760 | | 11768 | |
| 11761 | const operand_ty = sema.typeOf(operand.simple.by_val); | 11769 | const operand_ty = sema.typeOf(operand.simple.by_val); |
| 11762 | const item_ty = switch (operand_ty.zigTypeTag(zcu)) { | 11770 | |
| 11763 | .@"union" => operand_ty.unionTagType(zcu).?, | 11771 | const tagged_union_originally = operand_ty.zigTypeTag(zcu) == .@"union" and |
| 11764 | else => operand_ty, | 11772 | operand_ty.containerLayout(zcu) != .@"packed"; |
| 11765 | }; | 11773 | const err_set = operand_ty.zigTypeTag(zcu) == .error_set; |
| 11766 | const union_originally = operand_ty.zigTypeTag(zcu) == .@"union"; | 11774 | |
| 11767 | const err_set = item_ty.zigTypeTag(zcu) == .error_set; | 11775 | const item_ty = if (tagged_union_originally) |
| | 11776 | operand_ty.unionTagType(zcu).? |
| | 11777 | else |
| | 11778 | operand_ty; |
| 11768 | | 11779 | |
| 11769 | const cond_ref = operand.simple.cond; | 11780 | const cond_ref = operand.simple.cond; |
| 11770 | | 11781 | |
| ... | @@ -11807,7 +11818,7 @@ fn resolveSwitchBlock( | ... | @@ -11807,7 +11818,7 @@ fn resolveSwitchBlock( |
| 11807 | const item_val = sema.resolveValue(item_ref).?; | 11818 | const item_val = sema.resolveValue(item_ref).?; |
| 11808 | if (cond_val.eql(item_val, item_ty, zcu)) { | 11819 | if (cond_val.eql(item_val, item_ty, zcu)) { |
| 11809 | if (err_set) try sema.maybeErrorUnwrapComptime(child_block, prong_body, cond_ref); | 11820 | if (err_set) try sema.maybeErrorUnwrapComptime(child_block, prong_body, cond_ref); |
| 11810 | if (union_originally and operand_ty.unionFieldType(item_val, zcu).?.isNoReturn(zcu)) { | 11821 | if (tagged_union_originally and operand_ty.unionFieldType(item_val, zcu).?.isNoReturn(zcu)) { |
| 11811 | // This prong should be unreachable! | 11822 | // This prong should be unreachable! |
| 11812 | return .unreachable_value; | 11823 | return .unreachable_value; |
| 11813 | } | 11824 | } |
| ... | @@ -11908,7 +11919,7 @@ fn resolveSwitchBlock( | ... | @@ -11908,7 +11919,7 @@ fn resolveSwitchBlock( |
| 11908 | else | 11919 | else |
| 11909 | .{ else_case.index, else_case.body, else_case.capture, else_case.has_tag_capture, else_case.is_inline }; | 11920 | .{ else_case.index, else_case.body, else_case.capture, else_case.has_tag_capture, else_case.is_inline }; |
| 11910 | if (err_set) try sema.maybeErrorUnwrapComptime(child_block, body, cond_ref); | 11921 | if (err_set) try sema.maybeErrorUnwrapComptime(child_block, body, cond_ref); |
| 11911 | if (union_originally) { | 11922 | if (tagged_union_originally) { |
| 11912 | for (validated_switch.seen_enum_fields, 0..) |maybe_seen, field_i| { | 11923 | for (validated_switch.seen_enum_fields, 0..) |maybe_seen, field_i| { |
| 11913 | if (maybe_seen != null) continue; | 11924 | if (maybe_seen != null) continue; |
| 11914 | if (!operand_ty.unionFieldTypeByIndex(field_i, zcu).isNoReturn(zcu)) break; | 11925 | if (!operand_ty.unionFieldTypeByIndex(field_i, zcu).isNoReturn(zcu)) break; |
| ... | @@ -12049,12 +12060,12 @@ fn wantSwitchProngBodyAnalysis( | ... | @@ -12049,12 +12060,12 @@ fn wantSwitchProngBodyAnalysis( |
| 12049 | sema: *Sema, | 12060 | sema: *Sema, |
| 12050 | item_ref: Air.Inst.Ref, | 12061 | item_ref: Air.Inst.Ref, |
| 12051 | operand_ty: Type, | 12062 | operand_ty: Type, |
| 12052 | union_originally: bool, | 12063 | tagged_union_originally: bool, |
| 12053 | err_set: bool, | 12064 | err_set: bool, |
| 12054 | prong_is_comptime_unreach: bool, | 12065 | prong_is_comptime_unreach: bool, |
| 12055 | ) bool { | 12066 | ) bool { |
| 12056 | const zcu = sema.pt.zcu; | 12067 | const zcu = sema.pt.zcu; |
| 12057 | if (union_originally) { | 12068 | if (tagged_union_originally) { |
| 12058 | const item_val = sema.resolveValue(item_ref).?; | 12069 | const item_val = sema.resolveValue(item_ref).?; |
| 12059 | const field_ty = operand_ty.unionFieldType(item_val, zcu).?; | 12070 | const field_ty = operand_ty.unionFieldType(item_val, zcu).?; |
| 12060 | if (field_ty.isNoReturn(zcu)) return false; | 12071 | if (field_ty.isNoReturn(zcu)) return false; |
| ... | @@ -12106,8 +12117,10 @@ fn analyzeSwitchProng( | ... | @@ -12106,8 +12117,10 @@ fn analyzeSwitchProng( |
| 12106 | // No need to load the operand for this prong! | 12117 | // No need to load the operand for this prong! |
| 12107 | break :need_load false; | 12118 | break :need_load false; |
| 12108 | } | 12119 | } |
| 12109 | if (capture != .none and operand_ty.zigTypeTag(zcu) == .@"union") { | 12120 | if (capture != .none and operand_ty.zigTypeTag(zcu) == .@"union" and |
| 12110 | // Non-OPV union payload captures are always runtime-known. | 12121 | operand_ty.containerLayout(zcu) != .@"packed") |
| | 12122 | { |
| | 12123 | // Non-OPV tagged union payload captures are always runtime-known. |
| 12111 | break :need_load true; | 12124 | break :need_load true; |
| 12112 | } | 12125 | } |
| 12113 | if (kind == .inline_ref) { | 12126 | if (kind == .inline_ref) { |
| ... | @@ -12202,6 +12215,9 @@ fn analyzeSwitchTagCapture( | ... | @@ -12202,6 +12215,9 @@ fn analyzeSwitchTagCapture( |
| 12202 | operand_ty.fmt(pt), | 12215 | operand_ty.fmt(pt), |
| 12203 | }); | 12216 | }); |
| 12204 | } | 12217 | } |
| | 12218 | if (operand_ty.containerLayout(zcu) == .@"packed") { |
| | 12219 | return sema.fail(case_block, tag_capture_src, "cannot capture tag of packed union", .{}); |
| | 12220 | } |
| 12205 | switch (kind) { | 12221 | switch (kind) { |
| 12206 | .has_ranges => unreachable, | 12222 | .has_ranges => unreachable, |
| 12207 | .inline_ref => |ref| return ref, | 12223 | .inline_ref => |ref| return ref, |
| ... | @@ -12215,9 +12231,9 @@ fn analyzeSwitchPayloadCapture( | ... | @@ -12215,9 +12231,9 @@ fn analyzeSwitchPayloadCapture( |
| 12215 | sema: *Sema, | 12231 | sema: *Sema, |
| 12216 | case_block: *Block, | 12232 | case_block: *Block, |
| 12217 | operand: SwitchOperand, | 12233 | operand: SwitchOperand, |
| 12218 | /// Always has to be not-`none` if this is a union payload capture. | 12234 | /// Always has to be not-`none` if this is a tagged union payload capture. |
| 12219 | /// For non-union captures, this may be `none` if this is an inline capture | 12235 | /// For non-tagged-union captures, this may be `none` if this is an inline |
| 12220 | /// or if `kind.item_refs.len == 1` and capture is by val. | 12236 | /// capture or if `kind.item_refs.len == 1` and capture is by val. |
| 12221 | operand_val: Air.Inst.Ref, | 12237 | operand_val: Air.Inst.Ref, |
| 12222 | /// May be `none` if `capture_by_ref` is `false` or if `operand_val` is also `none`. | 12238 | /// May be `none` if `capture_by_ref` is `false` or if `operand_val` is also `none`. |
| 12223 | operand_ptr: Air.Inst.Ref, | 12239 | operand_ptr: Air.Inst.Ref, |
| ... | @@ -12234,9 +12250,22 @@ fn analyzeSwitchPayloadCapture( | ... | @@ -12234,9 +12250,22 @@ fn analyzeSwitchPayloadCapture( |
| 12234 | | 12250 | |
| 12235 | const switch_node_offset = operand_src.offset.node_offset_switch_operand; | 12251 | const switch_node_offset = operand_src.offset.node_offset_switch_operand; |
| 12236 | | 12252 | |
| | 12253 | const tagged_union_originally = operand_ty.zigTypeTag(zcu) == .@"union" and |
| | 12254 | operand_ty.containerLayout(zcu) != .@"packed"; |
| | 12255 | const err_set = operand_ty.zigTypeTag(zcu) == .error_set; |
| | 12256 | |
| | 12257 | if (err_set and capture_by_ref) { |
| | 12258 | return sema.fail( |
| | 12259 | case_block, |
| | 12260 | capture_src, |
| | 12261 | "error set cannot be captured by reference", |
| | 12262 | .{}, |
| | 12263 | ); |
| | 12264 | } |
| | 12265 | |
| 12237 | if (kind == .inline_ref) { | 12266 | if (kind == .inline_ref) { |
| 12238 | const item_val = sema.resolveValue(kind.inline_ref).?; | 12267 | const item_val = sema.resolveValue(kind.inline_ref).?; |
| 12239 | if (operand_ty.zigTypeTag(zcu) == .@"union") { | 12268 | if (tagged_union_originally) { |
| 12240 | const field_index: u32 = @intCast(operand_ty.unionTagFieldIndex(item_val, zcu).?); | 12269 | const field_index: u32 = @intCast(operand_ty.unionTagFieldIndex(item_val, zcu).?); |
| 12241 | const union_obj = zcu.typeToUnion(operand_ty).?; | 12270 | const union_obj = zcu.typeToUnion(operand_ty).?; |
| 12242 | const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]); | 12271 | const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]); |
| ... | @@ -12267,52 +12296,90 @@ fn analyzeSwitchPayloadCapture( | ... | @@ -12267,52 +12296,90 @@ fn analyzeSwitchPayloadCapture( |
| 12267 | } | 12296 | } |
| 12268 | | 12297 | |
| 12269 | if (kind == .special) { | 12298 | if (kind == .special) { |
| 12270 | if (capture_by_ref) return operand_ptr; | 12299 | if (err_set) { |
| 12271 | return switch (operand_ty.zigTypeTag(zcu)) { | 12300 | if (else_err_ty) |err_ty| { |
| 12272 | .error_set => e: { | 12301 | return sema.bitCast(case_block, err_ty, operand_val, operand_src, null); |
| 12273 | if (else_err_ty) |err_ty| { | 12302 | } else { |
| 12274 | break :e sema.bitCast(case_block, err_ty, operand_val, operand_src, null); | 12303 | try sema.analyzeUnreachable(case_block, operand_src, false); |
| 12275 | } else { | 12304 | return .unreachable_value; |
| 12276 | try sema.analyzeUnreachable(case_block, operand_src, false); | 12305 | } |
| 12277 | break :e .unreachable_value; | 12306 | } |
| 12278 | } | 12307 | if (capture_by_ref) { |
| 12279 | }, | 12308 | return operand_ptr; |
| 12280 | else => operand_val, | 12309 | } |
| 12281 | }; | 12310 | return operand_val; |
| 12282 | } | 12311 | } |
| 12283 | | 12312 | |
| 12284 | switch (operand_ty.zigTypeTag(zcu)) { | 12313 | if (tagged_union_originally) { |
| 12285 | .@"union" => { | 12314 | const case_vals = kind.item_refs; |
| 12286 | const case_vals = kind.item_refs; | | |
| 12287 | | 12315 | |
| 12288 | const union_obj = zcu.typeToUnion(operand_ty).?; | 12316 | const union_obj = zcu.typeToUnion(operand_ty).?; |
| 12289 | const first_item_val = sema.resolveValue(case_vals[0]).?; | 12317 | const first_item_val = sema.resolveValue(case_vals[0]).?; |
| 12290 | | 12318 | |
| 12291 | const first_field_index: u32 = zcu.unionTagFieldIndex(union_obj, first_item_val).?; | 12319 | const first_field_index: u32 = zcu.unionTagFieldIndex(union_obj, first_item_val).?; |
| 12292 | const first_field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[first_field_index]); | 12320 | const first_field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[first_field_index]); |
| 12293 | | 12321 | |
| 12294 | const field_indices = try sema.arena.alloc(u32, case_vals.len); | 12322 | const field_indices = try sema.arena.alloc(u32, case_vals.len); |
| 12295 | for (case_vals, field_indices) |item, *field_idx| { | 12323 | for (case_vals, field_indices) |item, *field_idx| { |
| 12296 | const item_val = sema.resolveValue(item).?; | 12324 | const item_val = sema.resolveValue(item).?; |
| 12297 | field_idx.* = zcu.unionTagFieldIndex(union_obj, item_val).?; | 12325 | field_idx.* = zcu.unionTagFieldIndex(union_obj, item_val).?; |
| 12298 | } | 12326 | } |
| | 12327 | |
| | 12328 | // Fast path: if all the operands are the same type already, we don't need to hit |
| | 12329 | // PTR! This will also allow us to emit simpler code. |
| | 12330 | const same_types = for (field_indices[1..]) |field_idx| { |
| | 12331 | const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]); |
| | 12332 | if (!field_ty.eql(first_field_ty, zcu)) break false; |
| | 12333 | } else true; |
| 12299 | | 12334 | |
| 12300 | // Fast path: if all the operands are the same type already, we don't need to hit | 12335 | const capture_ty: Type = capture_ty: { |
| 12301 | // PTR! This will also allow us to emit simpler code. | 12336 | if (same_types) break :capture_ty first_field_ty; |
| 12302 | const same_types = for (field_indices[1..]) |field_idx| { | 12337 | // We need values to run PTR on, so make a bunch of undef constants. |
| | 12338 | const dummy_captures = try sema.arena.alloc(Air.Inst.Ref, case_vals.len); |
| | 12339 | for (dummy_captures, field_indices) |*dummy, field_idx| { |
| 12303 | const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]); | 12340 | const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]); |
| 12304 | if (!field_ty.eql(first_field_ty, zcu)) break false; | 12341 | dummy.* = try pt.undefRef(field_ty); |
| 12305 | } else true; | 12342 | } |
| 12306 | | 12343 | |
| 12307 | const capture_ty: Type = capture_ty: { | 12344 | const case_srcs = try sema.arena.alloc(?LazySrcLoc, case_vals.len); |
| 12308 | if (same_types) break :capture_ty first_field_ty; | 12345 | for (case_srcs, 0..) |*case_src, item_i| { |
| | 12346 | case_src.* = .{ |
| | 12347 | .base_node_inst = capture_src.base_node_inst, |
| | 12348 | .offset = .{ .switch_case_item = .{ |
| | 12349 | .switch_node_offset = switch_node_offset, |
| | 12350 | .case_idx = capture_src.offset.switch_capture.case_idx, |
| | 12351 | .item_idx = .{ .kind = .single, .value = @intCast(item_i) }, |
| | 12352 | } }, |
| | 12353 | }; |
| | 12354 | } |
| | 12355 | |
| | 12356 | break :capture_ty sema.resolvePeerTypes( |
| | 12357 | case_block, |
| | 12358 | capture_src, |
| | 12359 | dummy_captures, |
| | 12360 | .{ .override = case_srcs }, |
| | 12361 | ) catch |err| switch (err) { |
| | 12362 | error.AnalysisFail => { |
| | 12363 | const msg = sema.err orelse return error.AnalysisFail; |
| | 12364 | try sema.reparentOwnedErrorMsg(capture_src, msg, "capture group with incompatible types", .{}); |
| | 12365 | return error.AnalysisFail; |
| | 12366 | }, |
| | 12367 | else => |e| return e, |
| | 12368 | }; |
| | 12369 | }; |
| | 12370 | |
| | 12371 | // By-reference captures have some further restrictions which make them easier to emit |
| | 12372 | if (capture_by_ref) { |
| | 12373 | const operand_ptr_ty = sema.typeOf(operand_ptr); |
| | 12374 | const capture_ptr_ty = resolve: { |
| | 12375 | // By-ref captures of hetereogeneous types are only allowed if all field |
| | 12376 | // pointer types are peer resolvable to each other. |
| 12309 | // We need values to run PTR on, so make a bunch of undef constants. | 12377 | // We need values to run PTR on, so make a bunch of undef constants. |
| 12310 | const dummy_captures = try sema.arena.alloc(Air.Inst.Ref, case_vals.len); | 12378 | const dummy_captures = try sema.arena.alloc(Air.Inst.Ref, case_vals.len); |
| 12311 | for (dummy_captures, field_indices) |*dummy, field_idx| { | 12379 | for (field_indices, dummy_captures) |field_index, *dummy| { |
| 12312 | const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]); | 12380 | const field_ptr_ty = try operand_ptr_ty.fieldPtrType(field_index, pt); |
| 12313 | dummy.* = try pt.undefRef(field_ty); | 12381 | dummy.* = try pt.undefRef(field_ptr_ty); |
| 12314 | } | 12382 | } |
| 12315 | | | |
| 12316 | const case_srcs = try sema.arena.alloc(?LazySrcLoc, case_vals.len); | 12383 | const case_srcs = try sema.arena.alloc(?LazySrcLoc, case_vals.len); |
| 12317 | for (case_srcs, 0..) |*case_src, item_i| { | 12384 | for (case_srcs, 0..) |*case_src, item_i| { |
| 12318 | case_src.* = .{ | 12385 | case_src.* = .{ |
| ... | @@ -12325,7 +12392,7 @@ fn analyzeSwitchPayloadCapture( | ... | @@ -12325,7 +12392,7 @@ fn analyzeSwitchPayloadCapture( |
| 12325 | }; | 12392 | }; |
| 12326 | } | 12393 | } |
| 12327 | | 12394 | |
| 12328 | break :capture_ty sema.resolvePeerTypes( | 12395 | break :resolve sema.resolvePeerTypes( |
| 12329 | case_block, | 12396 | case_block, |
| 12330 | capture_src, | 12397 | capture_src, |
| 12331 | dummy_captures, | 12398 | dummy_captures, |
| ... | @@ -12333,6 +12400,7 @@ fn analyzeSwitchPayloadCapture( | ... | @@ -12333,6 +12400,7 @@ fn analyzeSwitchPayloadCapture( |
| 12333 | ) catch |err| switch (err) { | 12400 | ) catch |err| switch (err) { |
| 12334 | error.AnalysisFail => { | 12401 | error.AnalysisFail => { |
| 12335 | const msg = sema.err orelse return error.AnalysisFail; | 12402 | const msg = sema.err orelse return error.AnalysisFail; |
| | 12403 | try sema.errNote(capture_src, msg, "this coercion is only possible when capturing by value", .{}); |
| 12336 | try sema.reparentOwnedErrorMsg(capture_src, msg, "capture group with incompatible types", .{}); | 12404 | try sema.reparentOwnedErrorMsg(capture_src, msg, "capture group with incompatible types", .{}); |
| 12337 | return error.AnalysisFail; | 12405 | return error.AnalysisFail; |
| 12338 | }, | 12406 | }, |
| ... | @@ -12340,272 +12408,222 @@ fn analyzeSwitchPayloadCapture( | ... | @@ -12340,272 +12408,222 @@ fn analyzeSwitchPayloadCapture( |
| 12340 | }; | 12408 | }; |
| 12341 | }; | 12409 | }; |
| 12342 | | 12410 | |
| 12343 | // By-reference captures have some further restrictions which make them easier to emit | 12411 | if (try sema.resolveDefinedValue(case_block, operand_src, operand_ptr)) |op_ptr_val| { |
| 12344 | if (capture_by_ref) { | 12412 | if (op_ptr_val.isUndef(zcu)) return pt.undefRef(capture_ptr_ty); |
| 12345 | const operand_ptr_ty = sema.typeOf(operand_ptr); | 12413 | const field_ptr_val = try op_ptr_val.ptrField(first_field_index, pt); |
| 12346 | const capture_ptr_ty = resolve: { | 12414 | return .fromValue(try pt.getCoerced(field_ptr_val, capture_ptr_ty)); |
| 12347 | // By-ref captures of hetereogeneous types are only allowed if all field | | |
| 12348 | // pointer types are peer resolvable to each other. | | |
| 12349 | // We need values to run PTR on, so make a bunch of undef constants. | | |
| 12350 | const dummy_captures = try sema.arena.alloc(Air.Inst.Ref, case_vals.len); | | |
| 12351 | for (field_indices, dummy_captures) |field_index, *dummy| { | | |
| 12352 | const field_ptr_ty = try operand_ptr_ty.fieldPtrType(field_index, pt); | | |
| 12353 | dummy.* = try pt.undefRef(field_ptr_ty); | | |
| 12354 | } | | |
| 12355 | const case_srcs = try sema.arena.alloc(?LazySrcLoc, case_vals.len); | | |
| 12356 | for (case_srcs, 0..) |*case_src, item_i| { | | |
| 12357 | case_src.* = .{ | | |
| 12358 | .base_node_inst = capture_src.base_node_inst, | | |
| 12359 | .offset = .{ .switch_case_item = .{ | | |
| 12360 | .switch_node_offset = switch_node_offset, | | |
| 12361 | .case_idx = capture_src.offset.switch_capture.case_idx, | | |
| 12362 | .item_idx = .{ .kind = .single, .value = @intCast(item_i) }, | | |
| 12363 | } }, | | |
| 12364 | }; | | |
| 12365 | } | | |
| 12366 | | | |
| 12367 | break :resolve sema.resolvePeerTypes( | | |
| 12368 | case_block, | | |
| 12369 | capture_src, | | |
| 12370 | dummy_captures, | | |
| 12371 | .{ .override = case_srcs }, | | |
| 12372 | ) catch |err| switch (err) { | | |
| 12373 | error.AnalysisFail => { | | |
| 12374 | const msg = sema.err orelse return error.AnalysisFail; | | |
| 12375 | try sema.errNote(capture_src, msg, "this coercion is only possible when capturing by value", .{}); | | |
| 12376 | try sema.reparentOwnedErrorMsg(capture_src, msg, "capture group with incompatible types", .{}); | | |
| 12377 | return error.AnalysisFail; | | |
| 12378 | }, | | |
| 12379 | else => |e| return e, | | |
| 12380 | }; | | |
| 12381 | }; | | |
| 12382 | | | |
| 12383 | if (try sema.resolveDefinedValue(case_block, operand_src, operand_ptr)) |op_ptr_val| { | | |
| 12384 | if (op_ptr_val.isUndef(zcu)) return pt.undefRef(capture_ptr_ty); | | |
| 12385 | const field_ptr_val = try op_ptr_val.ptrField(first_field_index, pt); | | |
| 12386 | return .fromValue(try pt.getCoerced(field_ptr_val, capture_ptr_ty)); | | |
| 12387 | } | | |
| 12388 | | | |
| 12389 | try sema.requireRuntimeBlock(case_block, operand_src, null); | | |
| 12390 | return case_block.addStructFieldPtr(operand_ptr, first_field_index, capture_ptr_ty); | | |
| 12391 | } | 12415 | } |
| 12392 | | 12416 | |
| 12393 | if (try capture_ty.onePossibleValue(pt)) |opv| return .fromValue(opv); | 12417 | try sema.requireRuntimeBlock(case_block, operand_src, null); |
| | 12418 | return case_block.addStructFieldPtr(operand_ptr, first_field_index, capture_ptr_ty); |
| | 12419 | } |
| 12394 | | 12420 | |
| 12395 | if (try sema.resolveDefinedValue(case_block, operand_src, operand_val)) |operand_val_val| { | 12421 | if (try capture_ty.onePossibleValue(pt)) |opv| return .fromValue(opv); |
| 12396 | if (operand_val_val.isUndef(zcu)) return pt.undefRef(capture_ty); | | |
| 12397 | const union_val = ip.indexToKey(operand_val_val.toIntern()).un; | | |
| 12398 | if (Value.fromInterned(union_val.tag).isUndef(zcu)) return pt.undefRef(capture_ty); | | |
| 12399 | const uncoerced: Air.Inst.Ref = .fromIntern(union_val.val); | | |
| 12400 | return sema.coerce(case_block, capture_ty, uncoerced, operand_src); | | |
| 12401 | } | | |
| 12402 | | 12422 | |
| 12403 | try sema.requireRuntimeBlock(case_block, operand_src, null); | 12423 | if (try sema.resolveDefinedValue(case_block, operand_src, operand_val)) |operand_val_val| { |
| | 12424 | if (operand_val_val.isUndef(zcu)) return pt.undefRef(capture_ty); |
| | 12425 | const union_val = ip.indexToKey(operand_val_val.toIntern()).un; |
| | 12426 | if (Value.fromInterned(union_val.tag).isUndef(zcu)) return pt.undefRef(capture_ty); |
| | 12427 | const uncoerced: Air.Inst.Ref = .fromIntern(union_val.val); |
| | 12428 | return sema.coerce(case_block, capture_ty, uncoerced, operand_src); |
| | 12429 | } |
| 12404 | | 12430 | |
| 12405 | if (same_types) { | 12431 | try sema.requireRuntimeBlock(case_block, operand_src, null); |
| 12406 | return case_block.addStructFieldVal(operand_val, first_field_index, capture_ty); | | |
| 12407 | } | | |
| 12408 | | 12432 | |
| 12409 | // We may have to emit a switch block which coerces the operand to the capture type. | 12433 | if (same_types) { |
| 12410 | // If we can, try to avoid that using in-memory coercions. | 12434 | return case_block.addStructFieldVal(operand_val, first_field_index, capture_ty); |
| 12411 | const first_non_imc = in_mem: { | 12435 | } |
| 12412 | for (field_indices, 0..) |field_idx, i| { | 12436 | |
| 12413 | const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]); | 12437 | // We may have to emit a switch block which coerces the operand to the capture type. |
| 12414 | if (.ok != try sema.coerceInMemoryAllowed(case_block, capture_ty, field_ty, false, zcu.getTarget(), .unneeded, .unneeded, null)) { | 12438 | // If we can, try to avoid that using in-memory coercions. |
| 12415 | break :in_mem i; | 12439 | const first_non_imc = in_mem: { |
| 12416 | } | 12440 | for (field_indices, 0..) |field_idx, i| { |
| | 12441 | const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]); |
| | 12442 | if (.ok != try sema.coerceInMemoryAllowed(case_block, capture_ty, field_ty, false, zcu.getTarget(), .unneeded, .unneeded, null)) { |
| | 12443 | break :in_mem i; |
| 12417 | } | 12444 | } |
| 12418 | // All fields are in-memory coercible to the resolved type! | 12445 | } |
| 12419 | // Just take the first field and bitcast the result. | 12446 | // All fields are in-memory coercible to the resolved type! |
| 12420 | const uncoerced = try case_block.addStructFieldVal(operand_val, first_field_index, first_field_ty); | 12447 | // Just take the first field and bitcast the result. |
| 12421 | return case_block.addBitCast(capture_ty, uncoerced); | 12448 | const uncoerced = try case_block.addStructFieldVal(operand_val, first_field_index, first_field_ty); |
| 12422 | }; | 12449 | return case_block.addBitCast(capture_ty, uncoerced); |
| | 12450 | }; |
| 12423 | | 12451 | |
| 12424 | // By-val capture with heterogeneous types which are not all in-memory coercible to | 12452 | // By-val capture with heterogeneous types which are not all in-memory coercible to |
| 12425 | // the resolved capture type. We finally have to fall back to the ugly method. | 12453 | // the resolved capture type. We finally have to fall back to the ugly method. |
| 12426 | | 12454 | |
| 12427 | // However, let's first track which operands are in-memory coercible. There may well | 12455 | // However, let's first track which operands are in-memory coercible. There may well |
| 12428 | // be several, and we can squash all of these cases into the same switch prong using | 12456 | // be several, and we can squash all of these cases into the same switch prong using |
| 12429 | // a simple bitcast. We'll make this the 'else' prong. | 12457 | // a simple bitcast. We'll make this the 'else' prong. |
| 12430 | | 12458 | |
| 12431 | var in_mem_coercible: std.DynamicBitSet = try .initFull(sema.arena, field_indices.len); | 12459 | var in_mem_coercible: std.DynamicBitSet = try .initFull(sema.arena, field_indices.len); |
| 12432 | in_mem_coercible.unset(first_non_imc); | 12460 | in_mem_coercible.unset(first_non_imc); |
| 12433 | { | 12461 | { |
| 12434 | const next = first_non_imc + 1; | 12462 | const next = first_non_imc + 1; |
| 12435 | for (field_indices[next..], next..) |field_idx, i| { | 12463 | for (field_indices[next..], next..) |field_idx, i| { |
| 12436 | const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]); | 12464 | const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]); |
| 12437 | if (.ok != try sema.coerceInMemoryAllowed(case_block, capture_ty, field_ty, false, zcu.getTarget(), .unneeded, .unneeded, null)) { | 12465 | if (.ok != try sema.coerceInMemoryAllowed(case_block, capture_ty, field_ty, false, zcu.getTarget(), .unneeded, .unneeded, null)) { |
| 12438 | in_mem_coercible.unset(i); | 12466 | in_mem_coercible.unset(i); |
| 12439 | } | | |
| 12440 | } | 12467 | } |
| 12441 | } | 12468 | } |
| | 12469 | } |
| 12442 | | 12470 | |
| 12443 | const capture_block_inst = try case_block.addInstAsIndex(.{ | 12471 | const capture_block_inst = try case_block.addInstAsIndex(.{ |
| 12444 | .tag = .block, | 12472 | .tag = .block, |
| 12445 | .data = .{ | 12473 | .data = .{ |
| 12446 | .ty_pl = .{ | 12474 | .ty_pl = .{ |
| 12447 | .ty = .fromType(capture_ty), | 12475 | .ty = .fromType(capture_ty), |
| 12448 | .payload = undefined, // updated below | 12476 | .payload = undefined, // updated below |
| 12449 | }, | | |
| 12450 | }, | 12477 | }, |
| 12451 | }); | 12478 | }, |
| | 12479 | }); |
| 12452 | | 12480 | |
| 12453 | const prong_count = field_indices.len - in_mem_coercible.count(); | 12481 | const prong_count = field_indices.len - in_mem_coercible.count(); |
| 12454 | | 12482 | |
| 12455 | const estimated_extra = prong_count * 6 + (prong_count / 10); // 2 for Case, 1 item, probably 3 insts; plus hints | 12483 | const estimated_extra = prong_count * 6 + (prong_count / 10); // 2 for Case, 1 item, probably 3 insts; plus hints |
| 12456 | var cases_extra = try std.array_list.Managed(u32).initCapacity(sema.gpa, estimated_extra); | 12484 | var cases_extra = try std.array_list.Managed(u32).initCapacity(sema.gpa, estimated_extra); |
| 12457 | defer cases_extra.deinit(); | 12485 | defer cases_extra.deinit(); |
| 12458 | | 12486 | |
| 12459 | { | 12487 | { |
| 12460 | // All branch hints are `.none`, so just add zero elems. | 12488 | // All branch hints are `.none`, so just add zero elems. |
| 12461 | comptime assert(@intFromEnum(std.builtin.BranchHint.none) == 0); | 12489 | comptime assert(@intFromEnum(std.builtin.BranchHint.none) == 0); |
| 12462 | const need_elems = std.math.divCeil(usize, prong_count + 1, 10) catch unreachable; | 12490 | const need_elems = std.math.divCeil(usize, prong_count + 1, 10) catch unreachable; |
| 12463 | try cases_extra.appendNTimes(0, need_elems); | 12491 | try cases_extra.appendNTimes(0, need_elems); |
| 12464 | } | 12492 | } |
| 12465 | | 12493 | |
| 12466 | { | 12494 | { |
| 12467 | // Non-bitcast cases | 12495 | // Non-bitcast cases |
| 12468 | var it = in_mem_coercible.iterator(.{ .kind = .unset }); | 12496 | var it = in_mem_coercible.iterator(.{ .kind = .unset }); |
| 12469 | while (it.next()) |idx| { | 12497 | while (it.next()) |idx| { |
| 12470 | var coerce_block = case_block.makeSubBlock(); | 12498 | var coerce_block = case_block.makeSubBlock(); |
| 12471 | defer coerce_block.instructions.deinit(sema.gpa); | 12499 | defer coerce_block.instructions.deinit(sema.gpa); |
| 12472 | | 12500 | |
| 12473 | const case_src: LazySrcLoc = .{ | 12501 | const case_src: LazySrcLoc = .{ |
| 12474 | .base_node_inst = capture_src.base_node_inst, | 12502 | .base_node_inst = capture_src.base_node_inst, |
| 12475 | .offset = .{ .switch_case_item = .{ | 12503 | .offset = .{ .switch_case_item = .{ |
| 12476 | .switch_node_offset = switch_node_offset, | 12504 | .switch_node_offset = switch_node_offset, |
| 12477 | .case_idx = capture_src.offset.switch_capture.case_idx, | 12505 | .case_idx = capture_src.offset.switch_capture.case_idx, |
| 12478 | .item_idx = .{ .kind = .single, .value = @intCast(idx) }, | 12506 | .item_idx = .{ .kind = .single, .value = @intCast(idx) }, |
| 12479 | } }, | 12507 | } }, |
| 12480 | }; | 12508 | }; |
| 12481 | | 12509 | |
| 12482 | const field_idx = field_indices[idx]; | 12510 | const field_idx = field_indices[idx]; |
| 12483 | const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]); | 12511 | const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]); |
| 12484 | const uncoerced = try coerce_block.addStructFieldVal(operand_val, field_idx, field_ty); | 12512 | const uncoerced = try coerce_block.addStructFieldVal(operand_val, field_idx, field_ty); |
| 12485 | const coerced = try sema.coerce(&coerce_block, capture_ty, uncoerced, case_src); | 12513 | const coerced = try sema.coerce(&coerce_block, capture_ty, uncoerced, case_src); |
| 12486 | _ = try coerce_block.addBr(capture_block_inst, coerced); | 12514 | _ = try coerce_block.addBr(capture_block_inst, coerced); |
| 12487 | | 12515 | |
| 12488 | try cases_extra.ensureUnusedCapacity(@typeInfo(Air.SwitchBr.Case).@"struct".fields.len + | 12516 | try cases_extra.ensureUnusedCapacity(@typeInfo(Air.SwitchBr.Case).@"struct".fields.len + |
| 12489 | 1 + // `item`, no ranges | 12517 | 1 + // `item`, no ranges |
| 12490 | coerce_block.instructions.items.len); | 12518 | coerce_block.instructions.items.len); |
| 12491 | cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{ | 12519 | cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{ |
| 12492 | .items_len = 1, | 12520 | .items_len = 1, |
| 12493 | .ranges_len = 0, | 12521 | .ranges_len = 0, |
| 12494 | .body_len = @intCast(coerce_block.instructions.items.len), | 12522 | .body_len = @intCast(coerce_block.instructions.items.len), |
| 12495 | })); | 12523 | })); |
| 12496 | cases_extra.appendAssumeCapacity(@intFromEnum(case_vals[idx])); // item | 12524 | cases_extra.appendAssumeCapacity(@intFromEnum(case_vals[idx])); // item |
| 12497 | cases_extra.appendSliceAssumeCapacity(@ptrCast(coerce_block.instructions.items)); // body | 12525 | cases_extra.appendSliceAssumeCapacity(@ptrCast(coerce_block.instructions.items)); // body |
| 12498 | } | | |
| 12499 | } | 12526 | } |
| 12500 | const else_body_len = len: { | 12527 | } |
| 12501 | // 'else' prong uses a bitcast | 12528 | const else_body_len = len: { |
| 12502 | var coerce_block = case_block.makeSubBlock(); | 12529 | // 'else' prong uses a bitcast |
| 12503 | defer coerce_block.instructions.deinit(sema.gpa); | 12530 | var coerce_block = case_block.makeSubBlock(); |
| | 12531 | defer coerce_block.instructions.deinit(sema.gpa); |
| 12504 | | 12532 | |
| 12505 | const first_imc_item_idx = in_mem_coercible.findFirstSet().?; | 12533 | const first_imc_item_idx = in_mem_coercible.findFirstSet().?; |
| 12506 | const first_imc_field_idx = field_indices[first_imc_item_idx]; | 12534 | const first_imc_field_idx = field_indices[first_imc_item_idx]; |
| 12507 | const first_imc_field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[first_imc_field_idx]); | 12535 | const first_imc_field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[first_imc_field_idx]); |
| 12508 | const uncoerced = try coerce_block.addStructFieldVal(operand_val, first_imc_field_idx, first_imc_field_ty); | 12536 | const uncoerced = try coerce_block.addStructFieldVal(operand_val, first_imc_field_idx, first_imc_field_ty); |
| 12509 | const coerced = try coerce_block.addBitCast(capture_ty, uncoerced); | 12537 | const coerced = try coerce_block.addBitCast(capture_ty, uncoerced); |
| 12510 | _ = try coerce_block.addBr(capture_block_inst, coerced); | 12538 | _ = try coerce_block.addBr(capture_block_inst, coerced); |
| 12511 | | 12539 | |
| 12512 | try cases_extra.appendSlice(@ptrCast(coerce_block.instructions.items)); | 12540 | try cases_extra.appendSlice(@ptrCast(coerce_block.instructions.items)); |
| 12513 | break :len coerce_block.instructions.items.len; | 12541 | break :len coerce_block.instructions.items.len; |
| 12514 | }; | 12542 | }; |
| 12515 | | 12543 | |
| 12516 | try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.SwitchBr).@"struct".fields.len + | 12544 | try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.SwitchBr).@"struct".fields.len + |
| 12517 | cases_extra.items.len + | 12545 | cases_extra.items.len + |
| 12518 | @typeInfo(Air.Block).@"struct".fields.len + | 12546 | @typeInfo(Air.Block).@"struct".fields.len + |
| 12519 | 1); | 12547 | 1); |
| 12520 | | 12548 | |
| 12521 | const switch_br_inst: u32 = @intCast(sema.air_instructions.len); | 12549 | const switch_br_inst: u32 = @intCast(sema.air_instructions.len); |
| 12522 | try sema.air_instructions.append(sema.gpa, .{ | 12550 | try sema.air_instructions.append(sema.gpa, .{ |
| 12523 | .tag = .switch_br, | 12551 | .tag = .switch_br, |
| 12524 | .data = .{ | 12552 | .data = .{ |
| 12525 | .pl_op = .{ | 12553 | .pl_op = .{ |
| 12526 | .operand = undefined, // set by switch below | 12554 | .operand = undefined, // set by switch below |
| 12527 | .payload = sema.addExtraAssumeCapacity(Air.SwitchBr{ | 12555 | .payload = sema.addExtraAssumeCapacity(Air.SwitchBr{ |
| 12528 | .cases_len = @intCast(prong_count), | 12556 | .cases_len = @intCast(prong_count), |
| 12529 | .else_body_len = @intCast(else_body_len), | 12557 | .else_body_len = @intCast(else_body_len), |
| 12530 | }), | 12558 | }), |
| 12531 | }, | | |
| 12532 | }, | | |
| 12533 | }); | | |
| 12534 | sema.air_extra.appendSliceAssumeCapacity(cases_extra.items); | | |
| 12535 | | | |
| 12536 | // Set up block body | | |
| 12537 | switch (operand) { | | |
| 12538 | .simple => |s| { | | |
| 12539 | const air_datas = sema.air_instructions.items(.data); | | |
| 12540 | air_datas[switch_br_inst].pl_op.operand = s.cond; | | |
| 12541 | air_datas[@intFromEnum(capture_block_inst)].ty_pl.payload = | | |
| 12542 | sema.addExtraAssumeCapacity(Air.Block{ .body_len = 1 }); | | |
| 12543 | sema.air_extra.appendAssumeCapacity(switch_br_inst); | | |
| 12544 | }, | | |
| 12545 | .loop => { | | |
| 12546 | // The block must first extract the tag from the loaded union. | | |
| 12547 | const tag_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len); | | |
| 12548 | try sema.air_instructions.append(sema.gpa, .{ | | |
| 12549 | .tag = .get_union_tag, | | |
| 12550 | .data = .{ .ty_op = .{ | | |
| 12551 | .ty = .fromIntern(union_obj.enum_tag_type), | | |
| 12552 | .operand = operand_val, | | |
| 12553 | } }, | | |
| 12554 | }); | | |
| 12555 | const air_datas = sema.air_instructions.items(.data); | | |
| 12556 | air_datas[switch_br_inst].pl_op.operand = tag_inst.toRef(); | | |
| 12557 | air_datas[@intFromEnum(capture_block_inst)].ty_pl.payload = | | |
| 12558 | sema.addExtraAssumeCapacity(Air.Block{ .body_len = 2 }); | | |
| 12559 | sema.air_extra.appendAssumeCapacity(@intFromEnum(tag_inst)); | | |
| 12560 | sema.air_extra.appendAssumeCapacity(switch_br_inst); | | |
| 12561 | }, | 12559 | }, |
| 12562 | } | 12560 | }, |
| | 12561 | }); |
| | 12562 | sema.air_extra.appendSliceAssumeCapacity(cases_extra.items); |
| 12563 | | 12563 | |
| 12564 | return capture_block_inst.toRef(); | 12564 | // Set up block body |
| 12565 | }, | 12565 | switch (operand) { |
| 12566 | .error_set => { | 12566 | .simple => |s| { |
| 12567 | if (capture_by_ref) { | 12567 | const air_datas = sema.air_instructions.items(.data); |
| 12568 | return sema.fail( | 12568 | air_datas[switch_br_inst].pl_op.operand = s.cond; |
| 12569 | case_block, | 12569 | air_datas[@intFromEnum(capture_block_inst)].ty_pl.payload = |
| 12570 | capture_src, | 12570 | sema.addExtraAssumeCapacity(Air.Block{ .body_len = 1 }); |
| 12571 | "error set cannot be captured by reference", | 12571 | sema.air_extra.appendAssumeCapacity(switch_br_inst); |
| 12572 | .{}, | 12572 | }, |
| 12573 | ); | 12573 | .loop => { |
| 12574 | } | 12574 | // The block must first extract the tag from the loaded union. |
| | 12575 | const tag_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len); |
| | 12576 | try sema.air_instructions.append(sema.gpa, .{ |
| | 12577 | .tag = .get_union_tag, |
| | 12578 | .data = .{ .ty_op = .{ |
| | 12579 | .ty = .fromIntern(union_obj.enum_tag_type), |
| | 12580 | .operand = operand_val, |
| | 12581 | } }, |
| | 12582 | }); |
| | 12583 | const air_datas = sema.air_instructions.items(.data); |
| | 12584 | air_datas[switch_br_inst].pl_op.operand = tag_inst.toRef(); |
| | 12585 | air_datas[@intFromEnum(capture_block_inst)].ty_pl.payload = |
| | 12586 | sema.addExtraAssumeCapacity(Air.Block{ .body_len = 2 }); |
| | 12587 | sema.air_extra.appendAssumeCapacity(@intFromEnum(tag_inst)); |
| | 12588 | sema.air_extra.appendAssumeCapacity(switch_br_inst); |
| | 12589 | }, |
| | 12590 | } |
| 12575 | | 12591 | |
| 12576 | const case_vals = kind.item_refs; | 12592 | return capture_block_inst.toRef(); |
| 12577 | if (case_vals.len == 1) { | 12593 | } |
| 12578 | const item_val = sema.resolveValue(case_vals[0]).?; | | |
| 12579 | const item_ty = try pt.singleErrorSetType(item_val.getErrorName(zcu).unwrap().?); | | |
| 12580 | return sema.bitCast(case_block, item_ty, .fromValue(item_val), operand_src, null); | | |
| 12581 | } | | |
| 12582 | | 12594 | |
| 12583 | var names: InferredErrorSet.NameMap = .{}; | 12595 | if (err_set) { |
| 12584 | try names.ensureUnusedCapacity(sema.arena, case_vals.len); | 12596 | const case_vals = kind.item_refs; |
| 12585 | for (case_vals) |err| { | 12597 | if (case_vals.len == 1) { |
| 12586 | const err_val = sema.resolveValue(err).?; | 12598 | const item_val = sema.resolveValue(case_vals[0]).?; |
| 12587 | names.putAssumeCapacityNoClobber(err_val.getErrorName(zcu).unwrap().?, {}); | 12599 | const item_ty = try pt.singleErrorSetType(item_val.getErrorName(zcu).unwrap().?); |
| 12588 | } | 12600 | return sema.bitCast(case_block, item_ty, .fromValue(item_val), operand_src, null); |
| 12589 | const error_ty = try pt.errorSetFromUnsortedNames(names.keys()); | 12601 | } |
| 12590 | return sema.bitCast(case_block, error_ty, operand_val, operand_src, null); | 12602 | |
| 12591 | }, | 12603 | var names: InferredErrorSet.NameMap = .{}; |
| 12592 | else => { | 12604 | try names.ensureUnusedCapacity(sema.arena, case_vals.len); |
| 12593 | // In this case the capture value is just the passed-through value of the | 12605 | for (case_vals) |err| { |
| 12594 | // switch condition. It is comptime-known if there is only one item. | 12606 | const err_val = sema.resolveValue(err).?; |
| 12595 | if (capture_by_ref) { | 12607 | names.putAssumeCapacityNoClobber(err_val.getErrorName(zcu).unwrap().?, {}); |
| 12596 | return operand_ptr; | 12608 | } |
| 12597 | } | 12609 | const error_ty = try pt.errorSetFromUnsortedNames(names.keys()); |
| 12598 | switch (kind) { | 12610 | return sema.bitCast(case_block, error_ty, operand_val, operand_src, null); |
| 12599 | .inline_ref, .special => unreachable, | 12611 | } |
| 12600 | .item_refs => |case_vals| { | 12612 | |
| 12601 | // If there's only a single item, the capture is comptime-known! | 12613 | // In this case the capture value is just the passed-through value of the |
| 12602 | if (case_vals.len == 1) return case_vals[0]; | 12614 | // switch condition. It is comptime-known if there is only one item. |
| 12603 | }, | 12615 | if (capture_by_ref) { |
| 12604 | .has_ranges => {}, | 12616 | return operand_ptr; |
| 12605 | } | 12617 | } |
| 12606 | return operand_val; | 12618 | switch (kind) { |
| | 12619 | .inline_ref, .special => unreachable, |
| | 12620 | .item_refs => |case_vals| { |
| | 12621 | // If there's only a single item, the capture is comptime-known! |
| | 12622 | if (case_vals.len == 1) return case_vals[0]; |
| 12607 | }, | 12623 | }, |
| | 12624 | .has_ranges => {}, |
| 12608 | } | 12625 | } |
| | 12626 | return operand_val; |
| 12609 | } | 12627 | } |
| 12610 | | 12628 | |
| 12611 | const ResolvedSwitchItem = struct { | 12629 | const ResolvedSwitchItem = struct { |
| ... | @@ -12714,7 +12732,6 @@ fn validateSwitchItemOrRange( | ... | @@ -12714,7 +12732,6 @@ fn validateSwitchItemOrRange( |
| 12714 | const zcu = pt.zcu; | 12732 | const zcu = pt.zcu; |
| 12715 | const ip = &zcu.intern_pool; | 12733 | const ip = &zcu.intern_pool; |
| 12716 | const maybe_prev_src: ?LazySrcLoc = maybe_prev_src: switch (item_ty.zigTypeTag(zcu)) { | 12734 | const maybe_prev_src: ?LazySrcLoc = maybe_prev_src: switch (item_ty.zigTypeTag(zcu)) { |
| 12717 | .@"union" => unreachable, | | |
| 12718 | .@"enum" => { | 12735 | .@"enum" => { |
| 12719 | const int = ip.indexToKey(item_val.toIntern()).enum_tag.int; | 12736 | const int = ip.indexToKey(item_val.toIntern()).enum_tag.int; |
| 12720 | if (ip.loadEnumType(item_ty.toIntern()).tagValueIndex(ip, int)) |field_index| { | 12737 | if (ip.loadEnumType(item_ty.toIntern()).tagValueIndex(ip, int)) |field_index| { |
| ... | @@ -12755,6 +12772,14 @@ fn validateSwitchItemOrRange( | ... | @@ -12755,6 +12772,14 @@ fn validateSwitchItemOrRange( |
| 12755 | }, item_ty, zcu); | 12772 | }, item_ty, zcu); |
| 12756 | } | 12773 | } |
| 12757 | }, | 12774 | }, |
| | 12775 | .@"union", .@"struct" => { |
| | 12776 | const backing_int_val = ip.indexToKey(item_val.toIntern()).bitpack.backing_int_val; |
| | 12777 | break :maybe_prev_src range_set.addAssumeCapacity(.{ |
| | 12778 | .first = .fromInterned(backing_int_val), |
| | 12779 | .last = .fromInterned(backing_int_val), |
| | 12780 | .src = item_src, |
| | 12781 | }, item_ty.bitpackBackingInt(zcu), zcu); |
| | 12782 | }, |
| 12758 | .enum_literal, .@"fn", .pointer, .type => { | 12783 | .enum_literal, .@"fn", .pointer, .type => { |
| 12759 | break :maybe_prev_src if (seen_sparse_values.fetchPutAssumeCapacity(item_val.toIntern(), item_src)) |prev| | 12784 | break :maybe_prev_src if (seen_sparse_values.fetchPutAssumeCapacity(item_val.toIntern(), item_src)) |prev| |
| 12760 | prev.value | 12785 | prev.value |