| ... | ... | @@ -10211,15 +10211,17 @@ fn analyzeSwitchBlock( |
| 10211 | 10211 | const operand_ty = sema.typeOf(val); |
| 10212 | 10212 | operand_ty.assertHasLayout(zcu); |
| 10213 | 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)) { |
| 10215 | | .@"union" => tag: { |
| 10214 | const init_cond: Air.Inst.Ref, const item_ty: Type = init: { |
| 10215 | if (operand_ty.zigTypeTag(zcu) == .@"union" and |
| 10216 | operand_ty.containerLayout(zcu) != .@"packed") |
| 10217 | { |
| 10216 | 10218 | const tag_val = try sema.unionToTag(block, val); |
| 10217 | | break :tag .{ tag_val, sema.typeOf(tag_val) }; |
| 10218 | | }, |
| 10219 | | else => .{ |
| 10219 | break :init .{ tag_val, sema.typeOf(tag_val) }; |
| 10220 | } |
| 10221 | break :init .{ |
| 10220 | 10222 | if (maybe_operand_opv) |operand_opv| .fromValue(operand_opv) else val, |
| 10221 | 10223 | operand_ty, |
| 10222 | | }, |
| 10224 | }; |
| 10223 | 10225 | }; |
| 10224 | 10226 | item_ty.assertHasLayout(zcu); |
| 10225 | 10227 | |
| ... | ... | @@ -10250,7 +10252,8 @@ fn analyzeSwitchBlock( |
| 10250 | 10252 | |
| 10251 | 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 | 10257 | const err_set = operand_ty.zigTypeTag(zcu) == .error_set; |
| 10255 | 10258 | |
| 10256 | 10259 | if (item_ty.zigTypeTag(zcu) == .@"enum" and |
| ... | ... | @@ -10305,7 +10308,7 @@ fn analyzeSwitchBlock( |
| 10305 | 10308 | else |
| 10306 | 10309 | .{ new_operand, .none }; |
| 10307 | 10310 | |
| 10308 | | const new_cond_ref = if (union_originally) |
| 10311 | const new_cond_ref = if (tagged_union_originally) |
| 10309 | 10312 | try sema.unionToTag(child_block, new_val) |
| 10310 | 10313 | else |
| 10311 | 10314 | new_val; |
| ... | ... | @@ -10327,9 +10330,26 @@ fn analyzeSwitchBlock( |
| 10327 | 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 | 10350 | // We simplify conditions with OPV to either a `loop` or a `block` since |
| 10332 | 10351 | // we cannot switch on a value which doesn't exist at runtime. |
| 10352 | |
| 10333 | 10353 | assert(operand == .loop); // `simple` should have already been comptime-resolved above! |
| 10334 | 10354 | |
| 10335 | 10355 | var case_block = child_block.makeSubBlock(); |
| ... | ... | @@ -10374,7 +10394,7 @@ fn analyzeSwitchBlock( |
| 10374 | 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 | 10398 | if (!analyze_body) return .unreachable_value; |
| 10379 | 10399 | |
| 10380 | 10400 | if (!(err_set and |
| ... | ... | @@ -10384,47 +10404,46 @@ fn analyzeSwitchBlock( |
| 10384 | 10404 | const payload_inst: Zir.Inst.Index = if (capture != .none) inst: { |
| 10385 | 10405 | const payload_inst = zir_switch.payload_capture_placeholder.unwrap() orelse switch_inst; |
| 10386 | 10406 | const payload_ref: Air.Inst.Ref = payload_ref: { |
| 10387 | | const item_val: Value = switch (operand_ty.zigTypeTag(zcu)) { |
| 10388 | | .@"union" => item_val: { |
| 10389 | | if (maybe_operand_opv) |operand_opv| { |
| 10390 | | break :item_val .fromInterned(zcu.intern_pool.indexToKey(operand_opv.toIntern()).un.val); |
| 10391 | | } |
| 10392 | | assert(union_originally); // operand type must be union, otherwise it would be an OPV type here |
| 10393 | | assert(zir_switch.any_maybe_runtime_capture); // there's a payload capture |
| 10394 | | const operand_val, const operand_ref = switch (operand) { |
| 10395 | | .simple => unreachable, |
| 10396 | | .loop => |l| load_operand: { |
| 10397 | | const loaded = try sema.analyzeLoad(block, src, l.operand_alloc, src); |
| 10398 | | if (l.operand_is_ref) { |
| 10399 | | const by_val = try sema.analyzeLoad(block, src, loaded, src); |
| 10400 | | break :load_operand .{ by_val, loaded }; |
| 10401 | | } else { |
| 10402 | | break :load_operand .{ loaded, .none }; |
| 10403 | | } |
| 10404 | | }, |
| 10405 | | }; |
| 10406 | | const prong_kind: SwitchProngKind = kind: { |
| 10407 | | if (is_inline) break :kind .{ .inline_ref = .fromValue(item_opv) }; |
| 10408 | | if (is_special) break :kind .special; |
| 10409 | | break :kind .{ .item_refs = &.{.fromValue(item_opv)} }; |
| 10410 | | }; |
| 10411 | | break :payload_ref try sema.analyzeSwitchPayloadCapture( |
| 10412 | | &case_block, |
| 10413 | | operand, |
| 10414 | | operand_val, |
| 10415 | | operand_ref, |
| 10416 | | operand_ty, |
| 10417 | | operand_src, |
| 10418 | | block.src(.{ .switch_capture = .{ |
| 10419 | | .switch_node_offset = src_node_offset, |
| 10420 | | .case_idx = index, |
| 10421 | | } }), |
| 10422 | | capture == .by_ref, |
| 10423 | | prong_kind, |
| 10424 | | validated_switch.else_err_ty, |
| 10425 | | ); |
| 10426 | | }, |
| 10427 | | else => item_opv, |
| 10407 | const item_val: Value = item_val: { |
| 10408 | if (!tagged_union_originally) { |
| 10409 | break :item_val item_opv; |
| 10410 | } |
| 10411 | if (maybe_operand_opv) |operand_opv| { |
| 10412 | break :item_val .fromInterned(zcu.intern_pool.indexToKey(operand_opv.toIntern()).un.val); |
| 10413 | } |
| 10414 | assert(zir_switch.any_maybe_runtime_capture); // there's a payload capture |
| 10415 | const operand_val, const operand_ref = switch (operand) { |
| 10416 | .simple => unreachable, |
| 10417 | .loop => |l| load_operand: { |
| 10418 | const loaded = try sema.analyzeLoad(block, src, l.operand_alloc, src); |
| 10419 | if (l.operand_is_ref) { |
| 10420 | const by_val = try sema.analyzeLoad(block, src, loaded, src); |
| 10421 | break :load_operand .{ by_val, loaded }; |
| 10422 | } else { |
| 10423 | break :load_operand .{ loaded, .none }; |
| 10424 | } |
| 10425 | }, |
| 10426 | }; |
| 10427 | const prong_kind: SwitchProngKind = kind: { |
| 10428 | if (is_inline) break :kind .{ .inline_ref = .fromValue(item_opv) }; |
| 10429 | if (is_special) break :kind .special; |
| 10430 | break :kind .{ .item_refs = &.{.fromValue(item_opv)} }; |
| 10431 | }; |
| 10432 | break :payload_ref try sema.analyzeSwitchPayloadCapture( |
| 10433 | &case_block, |
| 10434 | operand, |
| 10435 | operand_val, |
| 10436 | operand_ref, |
| 10437 | operand_ty, |
| 10438 | operand_src, |
| 10439 | block.src(.{ .switch_capture = .{ |
| 10440 | .switch_node_offset = src_node_offset, |
| 10441 | .case_idx = index, |
| 10442 | } }), |
| 10443 | capture == .by_ref, |
| 10444 | prong_kind, |
| 10445 | validated_switch.else_err_ty, |
| 10446 | ); |
| 10428 | 10447 | }; |
| 10429 | 10448 | break :payload_ref switch (capture) { |
| 10430 | 10449 | .by_val => .fromValue(item_val), |
| ... | ... | @@ -10462,40 +10481,100 @@ fn analyzeSwitchBlock( |
| 10462 | 10481 | .loop |
| 10463 | 10482 | else |
| 10464 | 10483 | .block; |
| 10465 | | const air_loop_ref = try child_block.addInst(.{ |
| 10484 | const air_ref = try child_block.addInst(.{ |
| 10466 | 10485 | .tag = air_tag, |
| 10467 | 10486 | .data = .{ .ty_pl = .{ |
| 10468 | 10487 | .ty = .noreturn_type, |
| 10469 | 10488 | .payload = payload_index, |
| 10470 | 10489 | } }, |
| 10471 | 10490 | }); |
| 10472 | | try sema.fixupSwitchContinues( |
| 10473 | | block, |
| 10474 | | src, |
| 10475 | | air_loop_ref, |
| 10476 | | operand, |
| 10477 | | operand_is_ref, |
| 10478 | | item_ty, |
| 10479 | | .opv, |
| 10480 | | zir_switch.any_maybe_runtime_capture, |
| 10481 | | merges, |
| 10482 | | ); |
| 10483 | | return null; |
| 10491 | break :switch_ref .{ air_ref, true }; |
| 10492 | }; |
| 10493 | |
| 10494 | const air_tag = sema.air_instructions.items(.tag)[@intFromEnum(switch_ref.toIndex().?)]; |
| 10495 | switch (air_tag) { |
| 10496 | .loop_switch_br, .switch_br => assert(!item_has_opv), |
| 10497 | .loop, .block => assert(item_has_opv), |
| 10498 | else => unreachable, |
| 10499 | } |
| 10500 | switch (air_tag) { |
| 10501 | .loop_switch_br, .loop => assert(merges.extra_insts.items.len > 0), |
| 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 | 10578 | return null; |
| 10500 | 10579 | } |
| 10501 | 10580 | |
| ... | ... | @@ -10510,7 +10589,7 @@ fn finishSwitchBr( |
| 10510 | 10589 | switch_inst: Zir.Inst.Index, |
| 10511 | 10590 | zir_switch: *const Zir.UnwrappedSwitchBlock, |
| 10512 | 10591 | validated_switch: *const ValidatedSwitchBlock, |
| 10513 | | ) CompileError!void { |
| 10592 | ) CompileError!Air.Inst.Ref { |
| 10514 | 10593 | const pt = sema.pt; |
| 10515 | 10594 | const zcu = pt.zcu; |
| 10516 | 10595 | const ip = &zcu.intern_pool; |
| ... | ... | @@ -10544,13 +10623,15 @@ fn finishSwitchBr( |
| 10544 | 10623 | |
| 10545 | 10624 | const else_is_named_only = has_else and has_under; |
| 10546 | 10625 | |
| 10547 | | const item_ty = switch (operand_ty.zigTypeTag(zcu)) { |
| 10548 | | .@"union" => operand_ty.unionTagType(zcu).?, |
| 10549 | | else => operand_ty, |
| 10550 | | }; |
| 10551 | | const union_originally = operand_ty.zigTypeTag(zcu) == .@"union"; |
| 10626 | const tagged_union_originally = operand_ty.zigTypeTag(zcu) == .@"union" and |
| 10627 | operand_ty.containerLayout(zcu) != .@"packed"; |
| 10552 | 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 | 10635 | const estimated_cases_len: u32 = scalar_cases_len + multi_cases_len + |
| 10555 | 10636 | @intFromBool(has_else or has_under); |
| 10556 | 10637 | |
| ... | ... | @@ -10632,7 +10713,7 @@ fn finishSwitchBr( |
| 10632 | 10713 | if (item_ref == .none) is_under_prong = true; |
| 10633 | 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 | 10717 | if (analyze_body) any_analyze_body = true; |
| 10637 | 10718 | |
| 10638 | 10719 | if (prong_info.is_inline) { |
| ... | ... | @@ -10840,9 +10921,8 @@ fn finishSwitchBr( |
| 10840 | 10921 | const else_prong_src = block.src(.{ .node_offset_switch_else_prong = src_node_offset }); |
| 10841 | 10922 | const error_names, const min_int = check_enumerable: { |
| 10842 | 10923 | switch (item_ty.zigTypeTag(zcu)) { |
| 10843 | | .@"union" => unreachable, |
| 10844 | 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 | 10927 | try branch_hints.ensureUnusedCapacity(gpa, @intCast(validated_switch.seen_enum_fields.len)); |
| 10848 | 10928 | break :check_enumerable .{ undefined, undefined }; |
| ... | ... | @@ -10856,6 +10936,11 @@ fn finishSwitchBr( |
| 10856 | 10936 | const min_int = try item_ty.minInt(pt, item_ty); |
| 10857 | 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 | 10944 | .bool, .void => break :check_enumerable .{ undefined, undefined }, |
| 10860 | 10945 | else => {}, |
| 10861 | 10946 | } |
| ... | ... | @@ -10871,7 +10956,7 @@ fn finishSwitchBr( |
| 10871 | 10956 | |
| 10872 | 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 | 10961 | if (emit_bb) try sema.emitBackwardBranch(block, else_prong_src); |
| 10877 | 10962 | emit_bb = true; |
| ... | ... | @@ -10918,13 +11003,11 @@ fn finishSwitchBr( |
| 10918 | 11003 | if (zcu.backendSupportsFeature(.is_named_enum_value) and |
| 10919 | 11004 | (has_else or has_under) and block.wantSafety() and |
| 10920 | 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 | 11008 | try sema.zirDbgStmt(&case_block, cond_dbg_node_index); |
| 10924 | 11009 | const ok = try case_block.addUnOp(.is_named_enum_value, cond_ref); |
| 10925 | | if (else_is_named_only) {} else { |
| 10926 | | try sema.addSafetyCheck(&case_block, src, ok, .corrupt_switch); |
| 10927 | | } |
| 11010 | try sema.addSafetyCheck(&case_block, src, ok, .corrupt_switch); |
| 10928 | 11011 | } |
| 10929 | 11012 | |
| 10930 | 11013 | if (else_is_named_only and !else_case.is_inline) { |
| ... | ... | @@ -10991,13 +11074,15 @@ fn finishSwitchBr( |
| 10991 | 11074 | |
| 10992 | 11075 | const analyze_catch_all_body = analyze_body: { |
| 10993 | 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 | 11080 | } else if (has_else) { |
| 10996 | 11081 | if (else_case.is_inline) break :analyze_body false; // already handled above |
| 10997 | 11082 | } else { |
| 10998 | 11083 | break :analyze_body false; // we still may want a safety check! |
| 10999 | 11084 | } |
| 11000 | | if (union_originally) { |
| 11085 | if (tagged_union_originally) { |
| 11001 | 11086 | const union_obj = zcu.typeToUnion(operand_ty).?; |
| 11002 | 11087 | for (validated_switch.seen_enum_fields, 0..) |seen_field, field_i| { |
| 11003 | 11088 | if (seen_field != null) continue; |
| ... | ... | @@ -11072,126 +11157,14 @@ fn finishSwitchBr( |
| 11072 | 11157 | .loop_switch_br |
| 11073 | 11158 | else |
| 11074 | 11159 | .switch_br; |
| 11075 | | const air_switch_ref = try child_block.addInst(.{ |
| 11160 | const air_ref = try child_block.addInst(.{ |
| 11076 | 11161 | .tag = air_tag, |
| 11077 | 11162 | .data = .{ .pl_op = .{ |
| 11078 | 11163 | .operand = cond_ref, |
| 11079 | 11164 | .payload = payload_index, |
| 11080 | 11165 | } }, |
| 11081 | 11166 | }); |
| 11082 | | try sema.fixupSwitchContinues( |
| 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 | | } |
| 11167 | return air_ref; |
| 11195 | 11168 | } |
| 11196 | 11169 | |
| 11197 | 11170 | const ValidatedSwitchBlock = struct { |
| ... | ... | @@ -11242,7 +11215,6 @@ const ValidatedSwitchBlock = struct { |
| 11242 | 11215 | const zcu = pt.zcu; |
| 11243 | 11216 | const ip = &zcu.intern_pool; |
| 11244 | 11217 | switch (item_ty.zigTypeTag(zcu)) { |
| 11245 | | .@"union" => unreachable, |
| 11246 | 11218 | .@"enum" => { |
| 11247 | 11219 | for (it.seen_enum_fields[it.next_idx..], it.next_idx..) |seen_field, field_i| { |
| 11248 | 11220 | if (seen_field != null) continue; |
| ... | ... | @@ -11262,26 +11234,35 @@ const ValidatedSwitchBlock = struct { |
| 11262 | 11234 | } |
| 11263 | 11235 | return null; |
| 11264 | 11236 | }, |
| 11265 | | .int => { |
| 11266 | | var cur = it.next_val orelse return null; |
| 11237 | .int, .@"union", .@"struct" => |type_tag| { |
| 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 | 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 | 11247 | defer it.next_idx += 1; |
| 11271 | 11248 | const incr = try arith.incrementDefinedInt( |
| 11272 | 11249 | sema, |
| 11273 | | item_ty, |
| 11250 | int_ty, |
| 11274 | 11251 | it.seen_ranges[it.next_idx].last, |
| 11275 | 11252 | ); |
| 11276 | 11253 | if (incr.overflow) { |
| 11277 | 11254 | it.next_val = null; |
| 11278 | 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 | 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 | 11267 | .bool => { |
| 11287 | 11268 | if (!it.seen_true) { |
| ... | ... | @@ -11366,17 +11347,43 @@ fn validateSwitchBlock( |
| 11366 | 11347 | => break :item_ty operand_ty, |
| 11367 | 11348 | |
| 11368 | 11349 | .@"union" => { |
| 11369 | | const enum_ty = operand_ty.unionTagType(zcu) orelse { |
| 11370 | | return sema.failWithOwnedErrorMsg(block, msg: { |
| 11371 | | const msg = try sema.errMsg(operand_src, "switch on union with no attached enum", .{}); |
| 11372 | | errdefer msg.destroy(sema.gpa); |
| 11373 | | if (operand_ty.srcLocOrNull(zcu)) |union_src| { |
| 11374 | | try sema.errNote(union_src, msg, "consider 'union(enum)' here", .{}); |
| 11350 | operand_ty.assertHasLayout(zcu); |
| 11351 | const union_obj = ip.loadUnionType(operand_ty.toIntern()); |
| 11352 | switch (union_obj.tag_usage) { |
| 11353 | .tagged => { |
| 11354 | break :item_ty .fromInterned(union_obj.enum_tag_type); |
| 11355 | }, |
| 11356 | .none => { |
| 11357 | if (union_obj.layout == .@"packed") { |
| 11358 | break :item_ty operand_ty; |
| 11375 | 11359 | } |
| 11376 | | break :msg msg; |
| 11377 | | }); |
| 11378 | | }; |
| 11379 | | break :item_ty enum_ty; |
| 11360 | }, |
| 11361 | .safety => {}, |
| 11362 | } |
| 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 | 11389 | .pointer => { |
| ... | ... | @@ -11424,7 +11431,6 @@ fn validateSwitchBlock( |
| 11424 | 11431 | const else_case = zir_switch.else_case orelse undefined; |
| 11425 | 11432 | |
| 11426 | 11433 | switch (item_ty.zigTypeTag(zcu)) { |
| 11427 | | .@"union" => unreachable, |
| 11428 | 11434 | .@"enum" => { |
| 11429 | 11435 | seen_enum_fields = try arena.alloc(?LazySrcLoc, item_ty.enumFieldCount(zcu)); |
| 11430 | 11436 | @memset(seen_enum_fields, null); |
| ... | ... | @@ -11435,7 +11441,7 @@ fn validateSwitchBlock( |
| 11435 | 11441 | .error_set => { |
| 11436 | 11442 | try seen_errors.ensureUnusedCapacity(arena, zir_switch.totalItemsLen()); |
| 11437 | 11443 | }, |
| 11438 | | .int, .comptime_int => { |
| 11444 | .int, .comptime_int, .@"union", .@"struct" => { |
| 11439 | 11445 | try range_set.ensureUnusedCapacity(arena, zir_switch.totalItemsLen()); |
| 11440 | 11446 | }, |
| 11441 | 11447 | .enum_literal, .@"fn", .pointer, .type => { |
| ... | ... | @@ -11503,7 +11509,6 @@ fn validateSwitchBlock( |
| 11503 | 11509 | } |
| 11504 | 11510 | |
| 11505 | 11511 | switch (item_ty.zigTypeTag(zcu)) { |
| 11506 | | .@"union" => unreachable, |
| 11507 | 11512 | .int, .comptime_int => {}, |
| 11508 | 11513 | else => if (zir_switch.anyRanges()) { |
| 11509 | 11514 | const range_src = block.src(.{ .node_offset_switch_range = src_node_offset }); |
| ... | ... | @@ -11528,7 +11533,6 @@ fn validateSwitchBlock( |
| 11528 | 11533 | |
| 11529 | 11534 | // Validate for missing special prongs. |
| 11530 | 11535 | switch (item_ty.zigTypeTag(zcu)) { |
| 11531 | | .@"union" => unreachable, |
| 11532 | 11536 | .@"enum" => { |
| 11533 | 11537 | const all_tags_handled = for (seen_enum_fields) |seen_src| { |
| 11534 | 11538 | if (seen_src == null) break false; |
| ... | ... | @@ -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 | 11669 | check_range: { |
| 11666 | | if (type_tag == .int) { |
| 11667 | | const min_int = try item_ty.minInt(pt, item_ty); |
| 11668 | | const max_int = try item_ty.maxInt(pt, item_ty); |
| 11669 | | if (try range_set.spans(arena, min_int, max_int, item_ty, zcu)) { |
| 11670 | | if (has_else) { |
| 11671 | | return sema.fail( |
| 11672 | | block, |
| 11673 | | else_prong_src, |
| 11674 | | "unreachable else prong; all cases already handled", |
| 11675 | | .{}, |
| 11676 | | ); |
| 11677 | | } |
| 11678 | | break :check_range; |
| 11670 | const int_ty = switch (type_tag) { |
| 11671 | .comptime_int => break :check_range, // comptime_int has 'infinite' range |
| 11672 | .int => item_ty, |
| 11673 | .@"union", .@"struct" => item_ty.bitpackBackingInt(zcu), |
| 11674 | else => unreachable, |
| 11675 | }; |
| 11676 | const min_int = try int_ty.minInt(pt, int_ty); |
| 11677 | const max_int = try int_ty.maxInt(pt, int_ty); |
| 11678 | if (try range_set.spans(arena, min_int, max_int, int_ty, zcu)) { |
| 11679 | if (has_else) { |
| 11680 | return sema.fail( |
| 11681 | block, |
| 11682 | else_prong_src, |
| 11683 | "unreachable else prong; all cases already handled", |
| 11684 | .{}, |
| 11685 | ); |
| 11679 | 11686 | } |
| 11687 | break :check_range; |
| 11680 | 11688 | } |
| 11681 | 11689 | if (!has_else) { |
| 11682 | 11690 | return sema.fail( |
| ... | ... | @@ -11759,12 +11767,15 @@ fn resolveSwitchBlock( |
| 11759 | 11767 | const switch_node_offset = zir_switch.switch_src_node_offset; |
| 11760 | 11768 | |
| 11761 | 11769 | const operand_ty = sema.typeOf(operand.simple.by_val); |
| 11762 | | const item_ty = switch (operand_ty.zigTypeTag(zcu)) { |
| 11763 | | .@"union" => operand_ty.unionTagType(zcu).?, |
| 11764 | | else => operand_ty, |
| 11765 | | }; |
| 11766 | | const union_originally = operand_ty.zigTypeTag(zcu) == .@"union"; |
| 11767 | | const err_set = item_ty.zigTypeTag(zcu) == .error_set; |
| 11770 | |
| 11771 | const tagged_union_originally = operand_ty.zigTypeTag(zcu) == .@"union" and |
| 11772 | operand_ty.containerLayout(zcu) != .@"packed"; |
| 11773 | const err_set = operand_ty.zigTypeTag(zcu) == .error_set; |
| 11774 | |
| 11775 | const item_ty = if (tagged_union_originally) |
| 11776 | operand_ty.unionTagType(zcu).? |
| 11777 | else |
| 11778 | operand_ty; |
| 11768 | 11779 | |
| 11769 | 11780 | const cond_ref = operand.simple.cond; |
| 11770 | 11781 | |
| ... | ... | @@ -11807,7 +11818,7 @@ fn resolveSwitchBlock( |
| 11807 | 11818 | const item_val = sema.resolveValue(item_ref).?; |
| 11808 | 11819 | if (cond_val.eql(item_val, item_ty, zcu)) { |
| 11809 | 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 | 11822 | // This prong should be unreachable! |
| 11812 | 11823 | return .unreachable_value; |
| 11813 | 11824 | } |
| ... | ... | @@ -11908,7 +11919,7 @@ fn resolveSwitchBlock( |
| 11908 | 11919 | else |
| 11909 | 11920 | .{ else_case.index, else_case.body, else_case.capture, else_case.has_tag_capture, else_case.is_inline }; |
| 11910 | 11921 | if (err_set) try sema.maybeErrorUnwrapComptime(child_block, body, cond_ref); |
| 11911 | | if (union_originally) { |
| 11922 | if (tagged_union_originally) { |
| 11912 | 11923 | for (validated_switch.seen_enum_fields, 0..) |maybe_seen, field_i| { |
| 11913 | 11924 | if (maybe_seen != null) continue; |
| 11914 | 11925 | if (!operand_ty.unionFieldTypeByIndex(field_i, zcu).isNoReturn(zcu)) break; |
| ... | ... | @@ -12049,12 +12060,12 @@ fn wantSwitchProngBodyAnalysis( |
| 12049 | 12060 | sema: *Sema, |
| 12050 | 12061 | item_ref: Air.Inst.Ref, |
| 12051 | 12062 | operand_ty: Type, |
| 12052 | | union_originally: bool, |
| 12063 | tagged_union_originally: bool, |
| 12053 | 12064 | err_set: bool, |
| 12054 | 12065 | prong_is_comptime_unreach: bool, |
| 12055 | 12066 | ) bool { |
| 12056 | 12067 | const zcu = sema.pt.zcu; |
| 12057 | | if (union_originally) { |
| 12068 | if (tagged_union_originally) { |
| 12058 | 12069 | const item_val = sema.resolveValue(item_ref).?; |
| 12059 | 12070 | const field_ty = operand_ty.unionFieldType(item_val, zcu).?; |
| 12060 | 12071 | if (field_ty.isNoReturn(zcu)) return false; |
| ... | ... | @@ -12106,8 +12117,10 @@ fn analyzeSwitchProng( |
| 12106 | 12117 | // No need to load the operand for this prong! |
| 12107 | 12118 | break :need_load false; |
| 12108 | 12119 | } |
| 12109 | | if (capture != .none and operand_ty.zigTypeTag(zcu) == .@"union") { |
| 12110 | | // Non-OPV union payload captures are always runtime-known. |
| 12120 | if (capture != .none and operand_ty.zigTypeTag(zcu) == .@"union" and |
| 12121 | operand_ty.containerLayout(zcu) != .@"packed") |
| 12122 | { |
| 12123 | // Non-OPV tagged union payload captures are always runtime-known. |
| 12111 | 12124 | break :need_load true; |
| 12112 | 12125 | } |
| 12113 | 12126 | if (kind == .inline_ref) { |
| ... | ... | @@ -12202,6 +12215,9 @@ fn analyzeSwitchTagCapture( |
| 12202 | 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 | 12221 | switch (kind) { |
| 12206 | 12222 | .has_ranges => unreachable, |
| 12207 | 12223 | .inline_ref => |ref| return ref, |
| ... | ... | @@ -12215,9 +12231,9 @@ fn analyzeSwitchPayloadCapture( |
| 12215 | 12231 | sema: *Sema, |
| 12216 | 12232 | case_block: *Block, |
| 12217 | 12233 | operand: SwitchOperand, |
| 12218 | | /// Always has to be not-`none` if this is a union payload capture. |
| 12219 | | /// For non-union captures, this may be `none` if this is an inline capture |
| 12220 | | /// or if `kind.item_refs.len == 1` and capture is by val. |
| 12234 | /// Always has to be not-`none` if this is a tagged union payload capture. |
| 12235 | /// For non-tagged-union captures, this may be `none` if this is an inline |
| 12236 | /// capture or if `kind.item_refs.len == 1` and capture is by val. |
| 12221 | 12237 | operand_val: Air.Inst.Ref, |
| 12222 | 12238 | /// May be `none` if `capture_by_ref` is `false` or if `operand_val` is also `none`. |
| 12223 | 12239 | operand_ptr: Air.Inst.Ref, |
| ... | ... | @@ -12234,9 +12250,22 @@ fn analyzeSwitchPayloadCapture( |
| 12234 | 12250 | |
| 12235 | 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 | 12266 | if (kind == .inline_ref) { |
| 12238 | 12267 | const item_val = sema.resolveValue(kind.inline_ref).?; |
| 12239 | | if (operand_ty.zigTypeTag(zcu) == .@"union") { |
| 12268 | if (tagged_union_originally) { |
| 12240 | 12269 | const field_index: u32 = @intCast(operand_ty.unionTagFieldIndex(item_val, zcu).?); |
| 12241 | 12270 | const union_obj = zcu.typeToUnion(operand_ty).?; |
| 12242 | 12271 | const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]); |
| ... | ... | @@ -12267,52 +12296,90 @@ fn analyzeSwitchPayloadCapture( |
| 12267 | 12296 | } |
| 12268 | 12297 | |
| 12269 | 12298 | if (kind == .special) { |
| 12270 | | if (capture_by_ref) return operand_ptr; |
| 12271 | | return switch (operand_ty.zigTypeTag(zcu)) { |
| 12272 | | .error_set => e: { |
| 12273 | | if (else_err_ty) |err_ty| { |
| 12274 | | break :e sema.bitCast(case_block, err_ty, operand_val, operand_src, null); |
| 12275 | | } else { |
| 12276 | | try sema.analyzeUnreachable(case_block, operand_src, false); |
| 12277 | | break :e .unreachable_value; |
| 12278 | | } |
| 12279 | | }, |
| 12280 | | else => operand_val, |
| 12281 | | }; |
| 12299 | if (err_set) { |
| 12300 | if (else_err_ty) |err_ty| { |
| 12301 | return sema.bitCast(case_block, err_ty, operand_val, operand_src, null); |
| 12302 | } else { |
| 12303 | try sema.analyzeUnreachable(case_block, operand_src, false); |
| 12304 | return .unreachable_value; |
| 12305 | } |
| 12306 | } |
| 12307 | if (capture_by_ref) { |
| 12308 | return operand_ptr; |
| 12309 | } |
| 12310 | return operand_val; |
| 12282 | 12311 | } |
| 12283 | 12312 | |
| 12284 | | switch (operand_ty.zigTypeTag(zcu)) { |
| 12285 | | .@"union" => { |
| 12286 | | const case_vals = kind.item_refs; |
| 12313 | if (tagged_union_originally) { |
| 12314 | const case_vals = kind.item_refs; |
| 12287 | 12315 | |
| 12288 | | const union_obj = zcu.typeToUnion(operand_ty).?; |
| 12289 | | const first_item_val = sema.resolveValue(case_vals[0]).?; |
| 12316 | const union_obj = zcu.typeToUnion(operand_ty).?; |
| 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).?; |
| 12292 | | const first_field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[first_field_index]); |
| 12319 | const first_field_index: u32 = zcu.unionTagFieldIndex(union_obj, first_item_val).?; |
| 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); |
| 12295 | | for (case_vals, field_indices) |item, *field_idx| { |
| 12296 | | const item_val = sema.resolveValue(item).?; |
| 12297 | | field_idx.* = zcu.unionTagFieldIndex(union_obj, item_val).?; |
| 12298 | | } |
| 12322 | const field_indices = try sema.arena.alloc(u32, case_vals.len); |
| 12323 | for (case_vals, field_indices) |item, *field_idx| { |
| 12324 | const item_val = sema.resolveValue(item).?; |
| 12325 | field_idx.* = zcu.unionTagFieldIndex(union_obj, item_val).?; |
| 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 |
| 12301 | | // PTR! This will also allow us to emit simpler code. |
| 12302 | | const same_types = for (field_indices[1..]) |field_idx| { |
| 12335 | const capture_ty: Type = capture_ty: { |
| 12336 | if (same_types) break :capture_ty first_field_ty; |
| 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 | 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; |
| 12305 | | } else true; |
| 12341 | dummy.* = try pt.undefRef(field_ty); |
| 12342 | } |
| 12306 | 12343 | |
| 12307 | | const capture_ty: Type = capture_ty: { |
| 12308 | | if (same_types) break :capture_ty first_field_ty; |
| 12344 | const case_srcs = try sema.arena.alloc(?LazySrcLoc, case_vals.len); |
| 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 | 12377 | // We need values to run PTR on, so make a bunch of undef constants. |
| 12310 | 12378 | const dummy_captures = try sema.arena.alloc(Air.Inst.Ref, case_vals.len); |
| 12311 | | for (dummy_captures, field_indices) |*dummy, field_idx| { |
| 12312 | | const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]); |
| 12313 | | dummy.* = try pt.undefRef(field_ty); |
| 12379 | for (field_indices, dummy_captures) |field_index, *dummy| { |
| 12380 | const field_ptr_ty = try operand_ptr_ty.fieldPtrType(field_index, pt); |
| 12381 | dummy.* = try pt.undefRef(field_ptr_ty); |
| 12314 | 12382 | } |
| 12315 | | |
| 12316 | 12383 | const case_srcs = try sema.arena.alloc(?LazySrcLoc, case_vals.len); |
| 12317 | 12384 | for (case_srcs, 0..) |*case_src, item_i| { |
| 12318 | 12385 | case_src.* = .{ |
| ... | ... | @@ -12325,7 +12392,7 @@ fn analyzeSwitchPayloadCapture( |
| 12325 | 12392 | }; |
| 12326 | 12393 | } |
| 12327 | 12394 | |
| 12328 | | break :capture_ty sema.resolvePeerTypes( |
| 12395 | break :resolve sema.resolvePeerTypes( |
| 12329 | 12396 | case_block, |
| 12330 | 12397 | capture_src, |
| 12331 | 12398 | dummy_captures, |
| ... | ... | @@ -12333,6 +12400,7 @@ fn analyzeSwitchPayloadCapture( |
| 12333 | 12400 | ) catch |err| switch (err) { |
| 12334 | 12401 | error.AnalysisFail => { |
| 12335 | 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 | 12404 | try sema.reparentOwnedErrorMsg(capture_src, msg, "capture group with incompatible types", .{}); |
| 12337 | 12405 | return error.AnalysisFail; |
| 12338 | 12406 | }, |
| ... | ... | @@ -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 |
| 12344 | | if (capture_by_ref) { |
| 12345 | | const operand_ptr_ty = sema.typeOf(operand_ptr); |
| 12346 | | const capture_ptr_ty = resolve: { |
| 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); |
| 12411 | if (try sema.resolveDefinedValue(case_block, operand_src, operand_ptr)) |op_ptr_val| { |
| 12412 | if (op_ptr_val.isUndef(zcu)) return pt.undefRef(capture_ptr_ty); |
| 12413 | const field_ptr_val = try op_ptr_val.ptrField(first_field_index, pt); |
| 12414 | return .fromValue(try pt.getCoerced(field_ptr_val, 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| { |
| 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 | | } |
| 12421 | if (try capture_ty.onePossibleValue(pt)) |opv| return .fromValue(opv); |
| 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) { |
| 12406 | | return case_block.addStructFieldVal(operand_val, first_field_index, capture_ty); |
| 12407 | | } |
| 12431 | try sema.requireRuntimeBlock(case_block, operand_src, null); |
| 12408 | 12432 | |
| 12409 | | // We may have to emit a switch block which coerces the operand to the capture type. |
| 12410 | | // If we can, try to avoid that using in-memory coercions. |
| 12411 | | const first_non_imc = in_mem: { |
| 12412 | | for (field_indices, 0..) |field_idx, i| { |
| 12413 | | const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]); |
| 12414 | | if (.ok != try sema.coerceInMemoryAllowed(case_block, capture_ty, field_ty, false, zcu.getTarget(), .unneeded, .unneeded, null)) { |
| 12415 | | break :in_mem i; |
| 12416 | | } |
| 12433 | if (same_types) { |
| 12434 | return case_block.addStructFieldVal(operand_val, first_field_index, capture_ty); |
| 12435 | } |
| 12436 | |
| 12437 | // We may have to emit a switch block which coerces the operand to the capture type. |
| 12438 | // If we can, try to avoid that using in-memory coercions. |
| 12439 | const first_non_imc = in_mem: { |
| 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! |
| 12419 | | // Just take the first field and bitcast the result. |
| 12420 | | const uncoerced = try case_block.addStructFieldVal(operand_val, first_field_index, first_field_ty); |
| 12421 | | return case_block.addBitCast(capture_ty, uncoerced); |
| 12422 | | }; |
| 12445 | } |
| 12446 | // All fields are in-memory coercible to the resolved type! |
| 12447 | // Just take the first field and bitcast the result. |
| 12448 | const uncoerced = try case_block.addStructFieldVal(operand_val, first_field_index, first_field_ty); |
| 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 |
| 12425 | | // the resolved capture type. We finally have to fall back to the ugly method. |
| 12452 | // By-val capture with heterogeneous types which are not all in-memory coercible to |
| 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 |
| 12428 | | // 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. |
| 12455 | // However, let's first track which operands are in-memory coercible. There may well |
| 12456 | // be several, and we can squash all of these cases into the same switch prong using |
| 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); |
| 12432 | | in_mem_coercible.unset(first_non_imc); |
| 12433 | | { |
| 12434 | | const next = first_non_imc + 1; |
| 12435 | | for (field_indices[next..], next..) |field_idx, i| { |
| 12436 | | 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)) { |
| 12438 | | in_mem_coercible.unset(i); |
| 12439 | | } |
| 12459 | var in_mem_coercible: std.DynamicBitSet = try .initFull(sema.arena, field_indices.len); |
| 12460 | in_mem_coercible.unset(first_non_imc); |
| 12461 | { |
| 12462 | const next = first_non_imc + 1; |
| 12463 | for (field_indices[next..], next..) |field_idx, i| { |
| 12464 | const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]); |
| 12465 | if (.ok != try sema.coerceInMemoryAllowed(case_block, capture_ty, field_ty, false, zcu.getTarget(), .unneeded, .unneeded, null)) { |
| 12466 | in_mem_coercible.unset(i); |
| 12440 | 12467 | } |
| 12441 | 12468 | } |
| 12469 | } |
| 12442 | 12470 | |
| 12443 | | const capture_block_inst = try case_block.addInstAsIndex(.{ |
| 12444 | | .tag = .block, |
| 12445 | | .data = .{ |
| 12446 | | .ty_pl = .{ |
| 12447 | | .ty = .fromType(capture_ty), |
| 12448 | | .payload = undefined, // updated below |
| 12449 | | }, |
| 12471 | const capture_block_inst = try case_block.addInstAsIndex(.{ |
| 12472 | .tag = .block, |
| 12473 | .data = .{ |
| 12474 | .ty_pl = .{ |
| 12475 | .ty = .fromType(capture_ty), |
| 12476 | .payload = undefined, // updated below |
| 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 |
| 12456 | | var cases_extra = try std.array_list.Managed(u32).initCapacity(sema.gpa, estimated_extra); |
| 12457 | | defer cases_extra.deinit(); |
| 12483 | const estimated_extra = prong_count * 6 + (prong_count / 10); // 2 for Case, 1 item, probably 3 insts; plus hints |
| 12484 | var cases_extra = try std.array_list.Managed(u32).initCapacity(sema.gpa, estimated_extra); |
| 12485 | defer cases_extra.deinit(); |
| 12458 | 12486 | |
| 12459 | | { |
| 12460 | | // All branch hints are `.none`, so just add zero elems. |
| 12461 | | comptime assert(@intFromEnum(std.builtin.BranchHint.none) == 0); |
| 12462 | | const need_elems = std.math.divCeil(usize, prong_count + 1, 10) catch unreachable; |
| 12463 | | try cases_extra.appendNTimes(0, need_elems); |
| 12464 | | } |
| 12487 | { |
| 12488 | // All branch hints are `.none`, so just add zero elems. |
| 12489 | comptime assert(@intFromEnum(std.builtin.BranchHint.none) == 0); |
| 12490 | const need_elems = std.math.divCeil(usize, prong_count + 1, 10) catch unreachable; |
| 12491 | try cases_extra.appendNTimes(0, need_elems); |
| 12492 | } |
| 12465 | 12493 | |
| 12466 | | { |
| 12467 | | // Non-bitcast cases |
| 12468 | | var it = in_mem_coercible.iterator(.{ .kind = .unset }); |
| 12469 | | while (it.next()) |idx| { |
| 12470 | | var coerce_block = case_block.makeSubBlock(); |
| 12471 | | defer coerce_block.instructions.deinit(sema.gpa); |
| 12494 | { |
| 12495 | // Non-bitcast cases |
| 12496 | var it = in_mem_coercible.iterator(.{ .kind = .unset }); |
| 12497 | while (it.next()) |idx| { |
| 12498 | var coerce_block = case_block.makeSubBlock(); |
| 12499 | defer coerce_block.instructions.deinit(sema.gpa); |
| 12472 | 12500 | |
| 12473 | | const case_src: LazySrcLoc = .{ |
| 12474 | | .base_node_inst = capture_src.base_node_inst, |
| 12475 | | .offset = .{ .switch_case_item = .{ |
| 12476 | | .switch_node_offset = switch_node_offset, |
| 12477 | | .case_idx = capture_src.offset.switch_capture.case_idx, |
| 12478 | | .item_idx = .{ .kind = .single, .value = @intCast(idx) }, |
| 12479 | | } }, |
| 12480 | | }; |
| 12501 | const case_src: LazySrcLoc = .{ |
| 12502 | .base_node_inst = capture_src.base_node_inst, |
| 12503 | .offset = .{ .switch_case_item = .{ |
| 12504 | .switch_node_offset = switch_node_offset, |
| 12505 | .case_idx = capture_src.offset.switch_capture.case_idx, |
| 12506 | .item_idx = .{ .kind = .single, .value = @intCast(idx) }, |
| 12507 | } }, |
| 12508 | }; |
| 12481 | 12509 | |
| 12482 | | const field_idx = field_indices[idx]; |
| 12483 | | 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); |
| 12485 | | const coerced = try sema.coerce(&coerce_block, capture_ty, uncoerced, case_src); |
| 12486 | | _ = try coerce_block.addBr(capture_block_inst, coerced); |
| 12510 | const field_idx = field_indices[idx]; |
| 12511 | const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]); |
| 12512 | const uncoerced = try coerce_block.addStructFieldVal(operand_val, field_idx, field_ty); |
| 12513 | const coerced = try sema.coerce(&coerce_block, capture_ty, uncoerced, case_src); |
| 12514 | _ = try coerce_block.addBr(capture_block_inst, coerced); |
| 12487 | 12515 | |
| 12488 | | try cases_extra.ensureUnusedCapacity(@typeInfo(Air.SwitchBr.Case).@"struct".fields.len + |
| 12489 | | 1 + // `item`, no ranges |
| 12490 | | coerce_block.instructions.items.len); |
| 12491 | | cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{ |
| 12492 | | .items_len = 1, |
| 12493 | | .ranges_len = 0, |
| 12494 | | .body_len = @intCast(coerce_block.instructions.items.len), |
| 12495 | | })); |
| 12496 | | cases_extra.appendAssumeCapacity(@intFromEnum(case_vals[idx])); // item |
| 12497 | | cases_extra.appendSliceAssumeCapacity(@ptrCast(coerce_block.instructions.items)); // body |
| 12498 | | } |
| 12516 | try cases_extra.ensureUnusedCapacity(@typeInfo(Air.SwitchBr.Case).@"struct".fields.len + |
| 12517 | 1 + // `item`, no ranges |
| 12518 | coerce_block.instructions.items.len); |
| 12519 | cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{ |
| 12520 | .items_len = 1, |
| 12521 | .ranges_len = 0, |
| 12522 | .body_len = @intCast(coerce_block.instructions.items.len), |
| 12523 | })); |
| 12524 | cases_extra.appendAssumeCapacity(@intFromEnum(case_vals[idx])); // item |
| 12525 | cases_extra.appendSliceAssumeCapacity(@ptrCast(coerce_block.instructions.items)); // body |
| 12499 | 12526 | } |
| 12500 | | const else_body_len = len: { |
| 12501 | | // 'else' prong uses a bitcast |
| 12502 | | var coerce_block = case_block.makeSubBlock(); |
| 12503 | | defer coerce_block.instructions.deinit(sema.gpa); |
| 12527 | } |
| 12528 | const else_body_len = len: { |
| 12529 | // 'else' prong uses a bitcast |
| 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().?; |
| 12506 | | 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]); |
| 12508 | | 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); |
| 12510 | | _ = try coerce_block.addBr(capture_block_inst, coerced); |
| 12533 | const first_imc_item_idx = in_mem_coercible.findFirstSet().?; |
| 12534 | const first_imc_field_idx = field_indices[first_imc_item_idx]; |
| 12535 | const first_imc_field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[first_imc_field_idx]); |
| 12536 | const uncoerced = try coerce_block.addStructFieldVal(operand_val, first_imc_field_idx, first_imc_field_ty); |
| 12537 | const coerced = try coerce_block.addBitCast(capture_ty, uncoerced); |
| 12538 | _ = try coerce_block.addBr(capture_block_inst, coerced); |
| 12511 | 12539 | |
| 12512 | | try cases_extra.appendSlice(@ptrCast(coerce_block.instructions.items)); |
| 12513 | | break :len coerce_block.instructions.items.len; |
| 12514 | | }; |
| 12540 | try cases_extra.appendSlice(@ptrCast(coerce_block.instructions.items)); |
| 12541 | break :len coerce_block.instructions.items.len; |
| 12542 | }; |
| 12515 | 12543 | |
| 12516 | | try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.SwitchBr).@"struct".fields.len + |
| 12517 | | cases_extra.items.len + |
| 12518 | | @typeInfo(Air.Block).@"struct".fields.len + |
| 12519 | | 1); |
| 12520 | | |
| 12521 | | const switch_br_inst: u32 = @intCast(sema.air_instructions.len); |
| 12522 | | try sema.air_instructions.append(sema.gpa, .{ |
| 12523 | | .tag = .switch_br, |
| 12524 | | .data = .{ |
| 12525 | | .pl_op = .{ |
| 12526 | | .operand = undefined, // set by switch below |
| 12527 | | .payload = sema.addExtraAssumeCapacity(Air.SwitchBr{ |
| 12528 | | .cases_len = @intCast(prong_count), |
| 12529 | | .else_body_len = @intCast(else_body_len), |
| 12530 | | }), |
| 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); |
| 12544 | try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.SwitchBr).@"struct".fields.len + |
| 12545 | cases_extra.items.len + |
| 12546 | @typeInfo(Air.Block).@"struct".fields.len + |
| 12547 | 1); |
| 12548 | |
| 12549 | const switch_br_inst: u32 = @intCast(sema.air_instructions.len); |
| 12550 | try sema.air_instructions.append(sema.gpa, .{ |
| 12551 | .tag = .switch_br, |
| 12552 | .data = .{ |
| 12553 | .pl_op = .{ |
| 12554 | .operand = undefined, // set by switch below |
| 12555 | .payload = sema.addExtraAssumeCapacity(Air.SwitchBr{ |
| 12556 | .cases_len = @intCast(prong_count), |
| 12557 | .else_body_len = @intCast(else_body_len), |
| 12558 | }), |
| 12561 | 12559 | }, |
| 12562 | | } |
| 12560 | }, |
| 12561 | }); |
| 12562 | sema.air_extra.appendSliceAssumeCapacity(cases_extra.items); |
| 12563 | 12563 | |
| 12564 | | return capture_block_inst.toRef(); |
| 12565 | | }, |
| 12566 | | .error_set => { |
| 12567 | | if (capture_by_ref) { |
| 12568 | | return sema.fail( |
| 12569 | | case_block, |
| 12570 | | capture_src, |
| 12571 | | "error set cannot be captured by reference", |
| 12572 | | .{}, |
| 12573 | | ); |
| 12574 | | } |
| 12564 | // Set up block body |
| 12565 | switch (operand) { |
| 12566 | .simple => |s| { |
| 12567 | const air_datas = sema.air_instructions.items(.data); |
| 12568 | air_datas[switch_br_inst].pl_op.operand = s.cond; |
| 12569 | air_datas[@intFromEnum(capture_block_inst)].ty_pl.payload = |
| 12570 | sema.addExtraAssumeCapacity(Air.Block{ .body_len = 1 }); |
| 12571 | sema.air_extra.appendAssumeCapacity(switch_br_inst); |
| 12572 | }, |
| 12573 | .loop => { |
| 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; |
| 12577 | | if (case_vals.len == 1) { |
| 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 | | } |
| 12592 | return capture_block_inst.toRef(); |
| 12593 | } |
| 12582 | 12594 | |
| 12583 | | var names: InferredErrorSet.NameMap = .{}; |
| 12584 | | try names.ensureUnusedCapacity(sema.arena, case_vals.len); |
| 12585 | | for (case_vals) |err| { |
| 12586 | | const err_val = sema.resolveValue(err).?; |
| 12587 | | names.putAssumeCapacityNoClobber(err_val.getErrorName(zcu).unwrap().?, {}); |
| 12588 | | } |
| 12589 | | const error_ty = try pt.errorSetFromUnsortedNames(names.keys()); |
| 12590 | | return sema.bitCast(case_block, error_ty, operand_val, operand_src, null); |
| 12591 | | }, |
| 12592 | | else => { |
| 12593 | | // In this case the capture value is just the passed-through value of the |
| 12594 | | // switch condition. It is comptime-known if there is only one item. |
| 12595 | | if (capture_by_ref) { |
| 12596 | | return operand_ptr; |
| 12597 | | } |
| 12598 | | switch (kind) { |
| 12599 | | .inline_ref, .special => unreachable, |
| 12600 | | .item_refs => |case_vals| { |
| 12601 | | // If there's only a single item, the capture is comptime-known! |
| 12602 | | if (case_vals.len == 1) return case_vals[0]; |
| 12603 | | }, |
| 12604 | | .has_ranges => {}, |
| 12605 | | } |
| 12606 | | return operand_val; |
| 12595 | if (err_set) { |
| 12596 | const case_vals = kind.item_refs; |
| 12597 | if (case_vals.len == 1) { |
| 12598 | const item_val = sema.resolveValue(case_vals[0]).?; |
| 12599 | const item_ty = try pt.singleErrorSetType(item_val.getErrorName(zcu).unwrap().?); |
| 12600 | return sema.bitCast(case_block, item_ty, .fromValue(item_val), operand_src, null); |
| 12601 | } |
| 12602 | |
| 12603 | var names: InferredErrorSet.NameMap = .{}; |
| 12604 | try names.ensureUnusedCapacity(sema.arena, case_vals.len); |
| 12605 | for (case_vals) |err| { |
| 12606 | const err_val = sema.resolveValue(err).?; |
| 12607 | names.putAssumeCapacityNoClobber(err_val.getErrorName(zcu).unwrap().?, {}); |
| 12608 | } |
| 12609 | const error_ty = try pt.errorSetFromUnsortedNames(names.keys()); |
| 12610 | return sema.bitCast(case_block, error_ty, operand_val, operand_src, null); |
| 12611 | } |
| 12612 | |
| 12613 | // In this case the capture value is just the passed-through value of the |
| 12614 | // switch condition. It is comptime-known if there is only one item. |
| 12615 | if (capture_by_ref) { |
| 12616 | return operand_ptr; |
| 12617 | } |
| 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 | 12629 | const ResolvedSwitchItem = struct { |
| ... | ... | @@ -12714,7 +12732,6 @@ fn validateSwitchItemOrRange( |
| 12714 | 12732 | const zcu = pt.zcu; |
| 12715 | 12733 | const ip = &zcu.intern_pool; |
| 12716 | 12734 | const maybe_prev_src: ?LazySrcLoc = maybe_prev_src: switch (item_ty.zigTypeTag(zcu)) { |
| 12717 | | .@"union" => unreachable, |
| 12718 | 12735 | .@"enum" => { |
| 12719 | 12736 | const int = ip.indexToKey(item_val.toIntern()).enum_tag.int; |
| 12720 | 12737 | if (ip.loadEnumType(item_ty.toIntern()).tagValueIndex(ip, int)) |field_index| { |
| ... | ... | @@ -12755,6 +12772,14 @@ fn validateSwitchItemOrRange( |
| 12755 | 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 | 12783 | .enum_literal, .@"fn", .pointer, .type => { |
| 12759 | 12784 | break :maybe_prev_src if (seen_sparse_values.fetchPutAssumeCapacity(item_val.toIntern(), item_src)) |prev| |
| 12760 | 12785 | prev.value |