authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-03-30 21:22:30-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-03-30 21:28:36-07:00
log2a1dd174cdb3a084eef295613b03e94be4d843b9
tree49be90404a7d3471aac6f3ab1ebe864ca438629f
parent195ddab2be938c1201767909d39106cdf99fd07e

stage2: rework AstGen for switch expressions

The switch_br ZIR instructions are now switch_block instructions. This avoids a pointless block always surrounding a switchbr in emitted ZIR code. Introduce typeof_elem ZIR instruction for getting the type of the element of a pointer value in 1 instruction. Change typeof to be un_node, not un_tok. Introduce switch_capture ZIR instructions for obtaining the capture value of switch prongs. Introduce Sema.resolveBody for when you want to extract a *Inst out of a block and you know that there is only going to be 1 break from it. What's not working yet: AstGen does not correctly elide store instructions when it turns out that the result location does not need to be used as a pointer. Also Sema validation code for duplicate switch items is not yet implemented.

4 files changed, 675 insertions(+), 179 deletions(-)

BRANCH_TODO-44
......@@ -82,47 +82,3 @@ Performance optimizations to look into:
8282 }
8383 }
8484
85
86
87
88
89
90fn switchCaseExpr(
91 gz: *GenZir,
92 scope: *Scope,
93 rl: ResultLoc,
94 block: *zir.Inst.Block,
95 case: ast.full.SwitchCase,
96 target: zir.Inst.Ref,
97) !void {
98 const tree = gz.tree();
99 const node_datas = tree.nodes.items(.data);
100 const main_tokens = tree.nodes.items(.main_token);
101 const token_tags = tree.tokens.items(.tag);
102
103 const case_src = token_starts[case.ast.arrow_token];
104 const sub_scope = blk: {
105 const payload_token = case.payload_token orelse break :blk scope;
106 const ident = if (token_tags[payload_token] == .asterisk)
107 payload_token + 1
108 else
109 payload_token;
110 const is_ptr = ident != payload_token;
111 const value_name = tree.tokenSlice(ident);
112 if (mem.eql(u8, value_name, "_")) {
113 if (is_ptr) {
114 return mod.failTok(scope, payload_token, "pointer modifier invalid on discard", .{});
115 }
116 break :blk scope;
117 }
118 return mod.failTok(scope, ident, "TODO implement switch value payload", .{});
119 };
120
121 const case_body = try expr(gz, sub_scope, rl, case.ast.target_expr);
122 if (!case_body.tag.isNoReturn()) {
123 _ = try addZIRInst(mod, sub_scope, case_src, zir.Inst.Break, .{
124 .block = block,
125 .operand = case_body,
126 }, .{});
127 }
128}
src/AstGen.zig+371-26
......@@ -22,6 +22,7 @@ const Scope = Module.Scope;
2222const GenZir = Scope.GenZir;
2323const InnerError = Module.InnerError;
2424const Decl = Module.Decl;
25const LazySrcLoc = Module.LazySrcLoc;
2526const BuiltinFn = @import("BuiltinFn.zig");
2627
2728instructions: std.MultiArrayList(zir.Inst) = .{},
......@@ -1215,6 +1216,7 @@ fn blockExprStmts(
12151216 .negate,
12161217 .negate_wrap,
12171218 .typeof,
1219 .typeof_elem,
12181220 .xor,
12191221 .optional_type,
12201222 .optional_type_from_ptr_elem,
......@@ -1243,6 +1245,24 @@ fn blockExprStmts(
12431245 .slice_sentinel,
12441246 .import,
12451247 .typeof_peer,
1248 .switch_block,
1249 .switch_block_multi,
1250 .switch_block_else,
1251 .switch_block_else_multi,
1252 .switch_block_under,
1253 .switch_block_under_multi,
1254 .switch_block_ref,
1255 .switch_block_ref_multi,
1256 .switch_block_ref_else,
1257 .switch_block_ref_else_multi,
1258 .switch_block_ref_under,
1259 .switch_block_ref_under_multi,
1260 .switch_capture,
1261 .switch_capture_ref,
1262 .switch_capture_multi,
1263 .switch_capture_multi_ref,
1264 .switch_capture_else,
1265 .switch_capture_else_ref,
12461266 => break :b false,
12471267
12481268 // ZIR instructions that are always either `noreturn` or `void`.
......@@ -1257,18 +1277,6 @@ fn blockExprStmts(
12571277 .break_inline,
12581278 .condbr,
12591279 .condbr_inline,
1260 .switch_br,
1261 .switch_br_multi,
1262 .switch_br_else,
1263 .switch_br_else_multi,
1264 .switch_br_under,
1265 .switch_br_under_multi,
1266 .switch_br_ref,
1267 .switch_br_ref_multi,
1268 .switch_br_ref_else,
1269 .switch_br_ref_else_multi,
1270 .switch_br_ref_under,
1271 .switch_br_ref_under_multi,
12721280 .compile_error,
12731281 .ret_node,
12741282 .ret_tok,
......@@ -1543,7 +1551,7 @@ fn assignOp(
15431551
15441552 const lhs_ptr = try lvalExpr(gz, scope, node_datas[infix_node].lhs);
15451553 const lhs = try gz.addUnNode(.load, lhs_ptr, infix_node);
1546 const lhs_type = try gz.addUnTok(.typeof, lhs, infix_node);
1554 const lhs_type = try gz.addUnNode(.typeof, lhs, infix_node);
15471555 const rhs = try expr(gz, scope, .{ .ty = lhs_type }, node_datas[infix_node].rhs);
15481556
15491557 const result = try gz.addPlNode(op_inst_tag, infix_node, zir.Inst.Bin{
......@@ -2548,15 +2556,28 @@ fn switchExpr(
25482556 rl: ResultLoc,
25492557 switch_node: ast.Node.Index,
25502558) InnerError!zir.Inst.Ref {
2559 const astgen = parent_gz.astgen;
2560 const mod = astgen.mod;
2561 const gpa = mod.gpa;
25512562 const tree = parent_gz.tree();
25522563 const node_datas = tree.nodes.items(.data);
25532564 const node_tags = tree.nodes.items(.tag);
2565 const main_tokens = tree.nodes.items(.main_token);
25542566 const token_tags = tree.tokens.items(.tag);
25552567 const operand_node = node_datas[switch_node].lhs;
25562568 const extra = tree.extraData(node_datas[switch_node].rhs, ast.Node.SubRange);
25572569 const case_nodes = tree.extra_data[extra.start..extra.end];
25582570
2571 // We perform two passes over the AST. This first pass is to collect information
2572 // for the following variables, make note of the special prong AST node index,
2573 // and bail out with a compile error if there are multiple special prongs present.
25592574 var any_payload_is_ref = false;
2575 var scalar_cases_len: u32 = 0;
2576 var multi_cases_len: u32 = 0;
2577 var special_prong: zir.SpecialProng = .none;
2578 var special_node: ast.Node.Index = 0;
2579 var else_src: ?LazySrcLoc = null;
2580 var underscore_src: ?LazySrcLoc = null;
25602581 for (case_nodes) |case_node| {
25612582 const case = switch (node_tags[case_node]) {
25622583 .switch_case_one => tree.switchCaseOne(case_node),
......@@ -2568,22 +2589,346 @@ fn switchExpr(
25682589 any_payload_is_ref = true;
25692590 }
25702591 }
2592 // Check for else/`_` prong.
2593 if (case.ast.values.len == 0) {
2594 const case_src = parent_gz.tokSrcLoc(case.ast.arrow_token - 1);
2595 if (else_src) |src| {
2596 const msg = msg: {
2597 const msg = try mod.errMsg(
2598 scope,
2599 case_src,
2600 "multiple else prongs in switch expression",
2601 .{},
2602 );
2603 errdefer msg.destroy(gpa);
2604 try mod.errNote(scope, src, msg, "previous else prong is here", .{});
2605 break :msg msg;
2606 };
2607 return mod.failWithOwnedErrorMsg(scope, msg);
2608 } else if (underscore_src) |some_underscore| {
2609 const msg = msg: {
2610 const msg = try mod.errMsg(
2611 scope,
2612 parent_gz.nodeSrcLoc(switch_node),
2613 "else and '_' prong in switch expression",
2614 .{},
2615 );
2616 errdefer msg.destroy(gpa);
2617 try mod.errNote(scope, case_src, msg, "else prong is here", .{});
2618 try mod.errNote(scope, some_underscore, msg, "'_' prong is here", .{});
2619 break :msg msg;
2620 };
2621 return mod.failWithOwnedErrorMsg(scope, msg);
2622 }
2623 special_node = case_node;
2624 special_prong = .@"else";
2625 else_src = case_src;
2626 continue;
2627 } else if (case.ast.values.len == 1 and
2628 node_tags[case.ast.values[0]] == .identifier and
2629 mem.eql(u8, tree.tokenSlice(main_tokens[case.ast.values[0]]), "_"))
2630 {
2631 const case_src = parent_gz.tokSrcLoc(case.ast.arrow_token - 1);
2632 if (underscore_src) |src| {
2633 const msg = msg: {
2634 const msg = try mod.errMsg(
2635 scope,
2636 case_src,
2637 "multiple '_' prongs in switch expression",
2638 .{},
2639 );
2640 errdefer msg.destroy(gpa);
2641 try mod.errNote(scope, src, msg, "previous '_' prong is here", .{});
2642 break :msg msg;
2643 };
2644 return mod.failWithOwnedErrorMsg(scope, msg);
2645 } else if (else_src) |some_else| {
2646 const msg = msg: {
2647 const msg = try mod.errMsg(
2648 scope,
2649 parent_gz.nodeSrcLoc(switch_node),
2650 "else and '_' prong in switch expression",
2651 .{},
2652 );
2653 errdefer msg.destroy(gpa);
2654 try mod.errNote(scope, some_else, msg, "else prong is here", .{});
2655 try mod.errNote(scope, case_src, msg, "'_' prong is here", .{});
2656 break :msg msg;
2657 };
2658 return mod.failWithOwnedErrorMsg(scope, msg);
2659 }
2660 special_node = case_node;
2661 special_prong = .under;
2662 underscore_src = case_src;
2663 continue;
2664 }
2665
2666 if (case.ast.values.len == 1 and
2667 getRangeNode(node_tags, node_datas, case.ast.values[0]) == null)
2668 {
2669 scalar_cases_len += 1;
2670 } else {
2671 multi_cases_len += 1;
2672 }
25712673 }
25722674
2573 const rl_and_tag: struct { rl: ResultLoc, tag: zir.Inst.Tag } = if (any_payload_is_ref) .{
2574 .rl = .ref,
2575 .tag = .switch_br_ref,
2576 } else .{
2577 .rl = .none,
2578 .tag = .switch_br,
2675 const operand_rl: ResultLoc = if (any_payload_is_ref) .ref else .none;
2676 const operand = try expr(parent_gz, scope, operand_rl, operand_node);
2677 // We need the type of the operand to use as the result location for all the prong items.
2678 const typeof_tag: zir.Inst.Tag = if (any_payload_is_ref) .typeof_elem else .typeof;
2679 const operand_ty_inst = try parent_gz.addUnNode(typeof_tag, operand, operand_node);
2680 const item_rl: ResultLoc = .{ .ty = operand_ty_inst };
2681
2682 // Contains the data that goes into the `extra` array for the SwitchBr/SwitchBrMulti.
2683 // This is the header as well as the optional else prong body, as well as all the
2684 // scalar cases.
2685 // At the end we will memcpy this into place.
2686 var scalar_cases_payload = std.ArrayListUnmanaged(u32){};
2687 defer scalar_cases_payload.deinit(gpa);
2688 // Same deal, but this is only the `extra` data for the multi cases.
2689 var multi_cases_payload = std.ArrayListUnmanaged(u32){};
2690 defer multi_cases_payload.deinit(gpa);
2691
2692 var block_scope: GenZir = .{
2693 .parent = scope,
2694 .astgen = astgen,
2695 .force_comptime = parent_gz.force_comptime,
2696 .instructions = .{},
25792697 };
2580 const operand = try expr(parent_gz, scope, rl_and_tag.rl, operand_node);
2698 block_scope.setBreakResultLoc(rl);
2699 defer block_scope.instructions.deinit(gpa);
25812700
2582 const result = try parent_gz.addPlNode(.switch_br, switch_node, zir.Inst.SwitchBr{
2583 .operand = operand,
2584 .cases_len = 0,
2585 });
2586 return rvalue(parent_gz, scope, rl, result, switch_node);
2701 // This gets added to the parent block later, after the item expressions.
2702 const switch_block = try parent_gz.addBlock(undefined, switch_node);
2703
2704 // We re-use this same scope for all cases, including the special prong, if any.
2705 var case_scope: GenZir = .{
2706 .parent = &block_scope.base,
2707 .astgen = astgen,
2708 .force_comptime = parent_gz.force_comptime,
2709 .instructions = .{},
2710 };
2711 defer case_scope.instructions.deinit(gpa);
2712
2713 // Do the else/`_` first because it goes first in the payload.
2714 var capture_val_scope: Scope.LocalVal = undefined;
2715 if (special_node != 0) {
2716 const case = switch (node_tags[special_node]) {
2717 .switch_case_one => tree.switchCaseOne(special_node),
2718 .switch_case => tree.switchCase(special_node),
2719 else => unreachable,
2720 };
2721 const sub_scope = blk: {
2722 const payload_token = case.payload_token orelse break :blk &case_scope.base;
2723 const ident = if (token_tags[payload_token] == .asterisk)
2724 payload_token + 1
2725 else
2726 payload_token;
2727 const is_ptr = ident != payload_token;
2728 if (mem.eql(u8, tree.tokenSlice(ident), "_")) {
2729 if (is_ptr) {
2730 return mod.failTok(&case_scope.base, payload_token, "pointer modifier invalid on discard", .{});
2731 }
2732 break :blk &case_scope.base;
2733 }
2734 const capture_tag: zir.Inst.Tag = if (is_ptr)
2735 .switch_capture_else_ref
2736 else
2737 .switch_capture_else;
2738 const capture = try case_scope.add(.{
2739 .tag = capture_tag,
2740 .data = .{ .switch_capture = .{
2741 .switch_inst = switch_block,
2742 .prong_index = undefined,
2743 } },
2744 });
2745 const capture_name = try mod.identifierTokenString(&parent_gz.base, payload_token);
2746 capture_val_scope = .{
2747 .parent = &case_scope.base,
2748 .gen_zir = &case_scope,
2749 .name = capture_name,
2750 .inst = capture,
2751 .src = parent_gz.tokSrcLoc(payload_token),
2752 };
2753 break :blk &capture_val_scope.base;
2754 };
2755 block_scope.break_count += 1;
2756 const case_result = try expr(&case_scope, sub_scope, block_scope.break_result_loc, case.ast.target_expr);
2757 if (!astgen.refIsNoReturn(case_result)) {
2758 _ = try case_scope.addBreak(.@"break", switch_block, case_result);
2759 }
2760 // Documentation for this: `zir.Inst.SwitchBr` and `zir.Inst.SwitchBrMulti`.
2761 try scalar_cases_payload.ensureCapacity(gpa, scalar_cases_payload.items.len +
2762 3 + // operand, scalar_cases_len, else body len
2763 @boolToInt(multi_cases_len != 0) +
2764 case_scope.instructions.items.len);
2765 scalar_cases_payload.appendAssumeCapacity(@enumToInt(operand));
2766 scalar_cases_payload.appendAssumeCapacity(scalar_cases_len);
2767 if (multi_cases_len != 0) {
2768 scalar_cases_payload.appendAssumeCapacity(multi_cases_len);
2769 }
2770 scalar_cases_payload.appendAssumeCapacity(@intCast(u32, case_scope.instructions.items.len));
2771 scalar_cases_payload.appendSliceAssumeCapacity(case_scope.instructions.items);
2772 } else {
2773 // Documentation for this: `zir.Inst.SwitchBr` and `zir.Inst.SwitchBrMulti`.
2774 try scalar_cases_payload.ensureCapacity(gpa, scalar_cases_payload.items.len +
2775 2 + // operand, scalar_cases_len
2776 @boolToInt(multi_cases_len != 0));
2777 scalar_cases_payload.appendAssumeCapacity(@enumToInt(operand));
2778 scalar_cases_payload.appendAssumeCapacity(scalar_cases_len);
2779 if (multi_cases_len != 0) {
2780 scalar_cases_payload.appendAssumeCapacity(multi_cases_len);
2781 }
2782 }
2783
2784 // In this pass we generate all the item and prong expressions except the special case.
2785 for (case_nodes) |case_node| {
2786 if (case_node == special_node)
2787 continue;
2788 const case = switch (node_tags[case_node]) {
2789 .switch_case_one => tree.switchCaseOne(case_node),
2790 .switch_case => tree.switchCase(case_node),
2791 else => unreachable,
2792 };
2793
2794 // Reset the scope.
2795 case_scope.instructions.shrinkRetainingCapacity(0);
2796
2797 const is_multi_case = case.ast.values.len != 1 or
2798 getRangeNode(node_tags, node_datas, case.ast.values[0]) != null;
2799
2800 const sub_scope = blk: {
2801 const payload_token = case.payload_token orelse break :blk &case_scope.base;
2802 const ident = if (token_tags[payload_token] == .asterisk)
2803 payload_token + 1
2804 else
2805 payload_token;
2806 const is_ptr = ident != payload_token;
2807 if (mem.eql(u8, tree.tokenSlice(ident), "_")) {
2808 if (is_ptr) {
2809 return mod.failTok(&case_scope.base, payload_token, "pointer modifier invalid on discard", .{});
2810 }
2811 break :blk &case_scope.base;
2812 }
2813 const is_multi_case_bits: u2 = @boolToInt(is_multi_case);
2814 const is_ptr_bits: u2 = @boolToInt(is_ptr);
2815 const capture_tag: zir.Inst.Tag = switch ((is_multi_case_bits << 1) | is_ptr_bits) {
2816 0b00 => .switch_capture,
2817 0b01 => .switch_capture_ref,
2818 0b10 => .switch_capture_multi,
2819 0b11 => .switch_capture_multi_ref,
2820 };
2821 const capture_index = if (is_multi_case) multi_cases_len else scalar_cases_len;
2822 const capture = try case_scope.add(.{
2823 .tag = capture_tag,
2824 .data = .{ .switch_capture = .{
2825 .switch_inst = switch_block,
2826 .prong_index = capture_index,
2827 } },
2828 });
2829 const capture_name = try mod.identifierTokenString(&parent_gz.base, payload_token);
2830 capture_val_scope = .{
2831 .parent = &case_scope.base,
2832 .gen_zir = &case_scope,
2833 .name = capture_name,
2834 .inst = capture,
2835 .src = parent_gz.tokSrcLoc(payload_token),
2836 };
2837 break :blk &capture_val_scope.base;
2838 };
2839
2840 if (is_multi_case) {
2841 // items_len, ranges_len, body_len
2842 const header_index = multi_cases_payload.items.len;
2843 try multi_cases_payload.resize(gpa, multi_cases_payload.items.len + 3);
2844
2845 // items
2846 var items_len: u32 = 0;
2847 for (case.ast.values) |item_node| {
2848 if (getRangeNode(node_tags, node_datas, item_node) != null) continue;
2849 items_len += 1;
2850
2851 const item_inst = try comptimeExpr(parent_gz, scope, item_rl, item_node);
2852 try multi_cases_payload.append(gpa, @enumToInt(item_inst));
2853 }
2854
2855 // ranges
2856 var ranges_len: u32 = 0;
2857 for (case.ast.values) |item_node| {
2858 const range = getRangeNode(node_tags, node_datas, item_node) orelse continue;
2859 ranges_len += 1;
2860
2861 const first = try comptimeExpr(parent_gz, scope, item_rl, node_datas[range].lhs);
2862 const last = try comptimeExpr(parent_gz, scope, item_rl, node_datas[range].rhs);
2863 try multi_cases_payload.appendSlice(gpa, &[_]u32{
2864 @enumToInt(first), @enumToInt(last),
2865 });
2866 }
2867
2868 block_scope.break_count += 1;
2869 const case_result = try expr(&case_scope, sub_scope, block_scope.break_result_loc, case.ast.target_expr);
2870 if (!astgen.refIsNoReturn(case_result)) {
2871 _ = try case_scope.addBreak(.@"break", switch_block, case_result);
2872 }
2873
2874 multi_cases_payload.items[header_index + 0] = items_len;
2875 multi_cases_payload.items[header_index + 1] = ranges_len;
2876 multi_cases_payload.items[header_index + 2] = @intCast(u32, case_scope.instructions.items.len);
2877 try multi_cases_payload.appendSlice(gpa, case_scope.instructions.items);
2878 } else {
2879 const item_node = case.ast.values[0];
2880 const item_inst = try comptimeExpr(parent_gz, scope, item_rl, item_node);
2881 block_scope.break_count += 1;
2882 const case_result = try expr(&case_scope, sub_scope, block_scope.break_result_loc, case.ast.target_expr);
2883 if (!astgen.refIsNoReturn(case_result)) {
2884 _ = try case_scope.addBreak(.@"break", switch_block, case_result);
2885 }
2886 try scalar_cases_payload.ensureCapacity(gpa, scalar_cases_payload.items.len +
2887 2 + case_scope.instructions.items.len);
2888 scalar_cases_payload.appendAssumeCapacity(@enumToInt(item_inst));
2889 scalar_cases_payload.appendAssumeCapacity(@intCast(u32, case_scope.instructions.items.len));
2890 scalar_cases_payload.appendSliceAssumeCapacity(case_scope.instructions.items);
2891 }
2892 }
2893 // Now that the item expressions are generated we can add this.
2894 try parent_gz.instructions.append(gpa, switch_block);
2895
2896 const ref_bit: u4 = @boolToInt(any_payload_is_ref);
2897 const multi_bit: u4 = @boolToInt(multi_cases_len != 0);
2898 const special_prong_bits: u4 = @enumToInt(special_prong);
2899 comptime {
2900 assert(@enumToInt(zir.SpecialProng.none) == 0b00);
2901 assert(@enumToInt(zir.SpecialProng.@"else") == 0b01);
2902 assert(@enumToInt(zir.SpecialProng.under) == 0b10);
2903 }
2904 const zir_tags = astgen.instructions.items(.tag);
2905 zir_tags[switch_block] = switch ((ref_bit << 3) | (special_prong_bits << 1) | multi_bit) {
2906 0b0_00_0 => .switch_block,
2907 0b0_00_1 => .switch_block_multi,
2908 0b0_01_0 => .switch_block_else,
2909 0b0_01_1 => .switch_block_else_multi,
2910 0b0_10_0 => .switch_block_under,
2911 0b0_10_1 => .switch_block_under_multi,
2912 0b1_00_0 => .switch_block_ref,
2913 0b1_00_1 => .switch_block_ref_multi,
2914 0b1_01_0 => .switch_block_ref_else,
2915 0b1_01_1 => .switch_block_ref_else_multi,
2916 0b1_10_0 => .switch_block_ref_under,
2917 0b1_10_1 => .switch_block_ref_under_multi,
2918 else => unreachable,
2919 };
2920 const zir_datas = astgen.instructions.items(.data);
2921 zir_datas[switch_block].pl_node.payload_index = @intCast(u32, astgen.extra.items.len);
2922 try astgen.extra.ensureCapacity(gpa, astgen.extra.items.len +
2923 scalar_cases_payload.items.len + multi_cases_payload.items.len);
2924 astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items);
2925 astgen.extra.appendSliceAssumeCapacity(multi_cases_payload.items);
2926 const strat = rl.strategy(&block_scope);
2927 assert(strat.tag == .break_operand); // TODO
2928 assert(!strat.elide_store_to_block_ptr_instructions); // TODO
2929 assert(rl != .ref); // TODO
2930 const switch_block_ref = astgen.indexToRef(switch_block);
2931 return rvalue(parent_gz, scope, rl, switch_block_ref, switch_node);
25872932}
25882933
25892934fn ret(gz: *GenZir, scope: *Scope, node: ast.Node.Index) InnerError!zir.Inst.Ref {
......@@ -3021,7 +3366,7 @@ fn typeOf(
30213366 return gz.astgen.mod.failTok(scope, builtin_token, "expected at least 1 argument, found 0", .{});
30223367 }
30233368 if (params.len == 1) {
3024 const result = try gz.addUnTok(.typeof, try expr(gz, scope, .none, params[0]), node);
3369 const result = try gz.addUnNode(.typeof, try expr(gz, scope, .none, params[0]), node);
30253370 return rvalue(gz, scope, rl, result, node);
30263371 }
30273372 const arena = gz.astgen.arena;
src/Sema.zig+126-54
......@@ -84,6 +84,15 @@ pub fn rootAsType(sema: *Sema, root_block: *Scope.Block) !Type {
8484 return sema.resolveType(root_block, .unneeded, zir_inst_ref);
8585}
8686
87/// Returns only the result from the body that is specified.
88/// Only appropriate to call when it is determined at comptime that this body
89/// has no peers.
90fn resolveBody(sema: *Sema, block: *Scope.Block, body: []const zir.Inst.Index) InnerError!*Inst {
91 const break_inst = try sema.analyzeBody(block, body);
92 const operand_ref = sema.code.instructions.items(.data)[break_inst].@"break".operand;
93 return sema.resolveInst(operand_ref);
94}
95
8796/// ZIR instructions which are always `noreturn` return this. This matches the
8897/// return type of `analyzeBody` so that we can tail call them.
8998/// Only appropriate to return when the instruction is known to be NoReturn
......@@ -229,7 +238,26 @@ pub fn analyzeBody(
229238 .str => try sema.zirStr(block, inst),
230239 .sub => try sema.zirArithmetic(block, inst),
231240 .subwrap => try sema.zirArithmetic(block, inst),
241 .switch_block => try sema.zirSwitchBlock(block, inst, false, .none),
242 .switch_block_multi => try sema.zirSwitchBlockMulti(block, inst, false, .none),
243 .switch_block_else => try sema.zirSwitchBlock(block, inst, false, .@"else"),
244 .switch_block_else_multi => try sema.zirSwitchBlockMulti(block, inst, false, .@"else"),
245 .switch_block_under => try sema.zirSwitchBlock(block, inst, false, .under),
246 .switch_block_under_multi => try sema.zirSwitchBlockMulti(block, inst, false, .under),
247 .switch_block_ref => try sema.zirSwitchBlock(block, inst, true, .none),
248 .switch_block_ref_multi => try sema.zirSwitchBlockMulti(block, inst, true, .none),
249 .switch_block_ref_else => try sema.zirSwitchBlock(block, inst, true, .@"else"),
250 .switch_block_ref_else_multi => try sema.zirSwitchBlockMulti(block, inst, true, .@"else"),
251 .switch_block_ref_under => try sema.zirSwitchBlock(block, inst, true, .under),
252 .switch_block_ref_under_multi => try sema.zirSwitchBlockMulti(block, inst, true, .under),
253 .switch_capture => try sema.zirSwitchCapture(block, inst, false, false),
254 .switch_capture_ref => try sema.zirSwitchCapture(block, inst, false, true),
255 .switch_capture_multi => try sema.zirSwitchCapture(block, inst, true, false),
256 .switch_capture_multi_ref => try sema.zirSwitchCapture(block, inst, true, true),
257 .switch_capture_else => try sema.zirSwitchCaptureElse(block, inst, false),
258 .switch_capture_else_ref => try sema.zirSwitchCaptureElse(block, inst, true),
232259 .typeof => try sema.zirTypeof(block, inst),
260 .typeof_elem => try sema.zirTypeofElem(block, inst),
233261 .typeof_peer => try sema.zirTypeofPeer(block, inst),
234262 .xor => try sema.zirBitwise(block, inst, .xor),
235263
......@@ -245,18 +273,6 @@ pub fn analyzeBody(
245273 .ret_tok => return sema.zirRetTok(block, inst, false),
246274 .@"unreachable" => return sema.zirUnreachable(block, inst),
247275 .repeat => return sema.zirRepeat(block, inst),
248 .switch_br => return sema.zirSwitchBr(block, inst, false, .none),
249 .switch_br_multi => return sema.zirSwitchBrMulti(block, inst, false, .none),
250 .switch_br_else => return sema.zirSwitchBr(block, inst, false, .@"else"),
251 .switch_br_else_multi => return sema.zirSwitchBrMulti(block, inst, false, .@"else"),
252 .switch_br_under => return sema.zirSwitchBr(block, inst, false, .under),
253 .switch_br_under_multi => return sema.zirSwitchBrMulti(block, inst, false, .under),
254 .switch_br_ref => return sema.zirSwitchBr(block, inst, true, .none),
255 .switch_br_ref_multi => return sema.zirSwitchBrMulti(block, inst, true, .none),
256 .switch_br_ref_else => return sema.zirSwitchBr(block, inst, true, .@"else"),
257 .switch_br_ref_else_multi => return sema.zirSwitchBrMulti(block, inst, true, .@"else"),
258 .switch_br_ref_under => return sema.zirSwitchBr(block, inst, true, .under),
259 .switch_br_ref_under_multi => return sema.zirSwitchBrMulti(block, inst, true, .under),
260276
261277 // Instructions that we know can *never* be noreturn based solely on
262278 // their tag. We avoid needlessly checking if they are noreturn and
......@@ -1034,7 +1050,7 @@ fn analyzeBlockBody(
10341050 }
10351051 assert(coerce_block.instructions.items[coerce_block.instructions.items.len - 1] == coerced_operand);
10361052 // Here we depend on the br instruction having been over-allocated (if necessary)
1037 // inide analyzeBreak so that it can be converted into a br_block_flat instruction.
1053 // inside zirBreak so that it can be converted into a br_block_flat instruction.
10381054 const br_src = br.base.src;
10391055 const br_ty = br.base.ty;
10401056 const br_block_flat = @ptrCast(*Inst.BrBlockFlat, br);
......@@ -1063,22 +1079,15 @@ fn zirBreakpoint(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerEr
10631079 _ = try block.addNoOp(src, Type.initTag(.void), .breakpoint);
10641080}
10651081
1066fn zirBreak(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Index {
1082fn zirBreak(sema: *Sema, start_block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Index {
10671083 const tracy = trace(@src());
10681084 defer tracy.end();
10691085
10701086 const inst_data = sema.code.instructions.items(.data)[inst].@"break";
1087 const src = sema.src;
10711088 const operand = try sema.resolveInst(inst_data.operand);
1072 return sema.analyzeBreak(block, sema.src, inst_data.block_inst, operand);
1073}
1089 const zir_block = inst_data.block_inst;
10741090
1075fn analyzeBreak(
1076 sema: *Sema,
1077 start_block: *Scope.Block,
1078 src: LazySrcLoc,
1079 zir_block: zir.Inst.Index,
1080 operand: *Inst,
1081) InnerError!zir.Inst.Index {
10821091 var block = start_block;
10831092 while (true) {
10841093 if (block.label) |*label| {
......@@ -1103,7 +1112,7 @@ fn analyzeBreak(
11031112 try start_block.instructions.append(sema.gpa, &br.base);
11041113 try label.merges.results.append(sema.gpa, operand);
11051114 try label.merges.br_list.append(sema.gpa, br);
1106 return always_noreturn;
1115 return inst;
11071116 }
11081117 }
11091118 block = block.parent.?;
......@@ -2208,15 +2217,38 @@ fn zirSliceSentinel(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) Inne
22082217 return sema.analyzeSlice(block, src, array_ptr, start, end, sentinel, sentinel_src);
22092218}
22102219
2211const SpecialProng = enum { none, @"else", under };
2220fn zirSwitchCapture(
2221 sema: *Sema,
2222 block: *Scope.Block,
2223 inst: zir.Inst.Index,
2224 is_multi: bool,
2225 is_ref: bool,
2226) InnerError!*Inst {
2227 const tracy = trace(@src());
2228 defer tracy.end();
2229
2230 @panic("TODO implement Sema for zirSwitchCapture");
2231}
22122232
2213fn zirSwitchBr(
2233fn zirSwitchCaptureElse(
22142234 sema: *Sema,
22152235 block: *Scope.Block,
22162236 inst: zir.Inst.Index,
22172237 is_ref: bool,
2218 special_prong: SpecialProng,
2219) InnerError!zir.Inst.Index {
2238) InnerError!*Inst {
2239 const tracy = trace(@src());
2240 defer tracy.end();
2241
2242 @panic("TODO implement Sema for zirSwitchCaptureElse");
2243}
2244
2245fn zirSwitchBlock(
2246 sema: *Sema,
2247 block: *Scope.Block,
2248 inst: zir.Inst.Index,
2249 is_ref: bool,
2250 special_prong: zir.SpecialProng,
2251) InnerError!*Inst {
22202252 const tracy = trace(@src());
22212253 defer tracy.end();
22222254
......@@ -2238,17 +2270,18 @@ fn zirSwitchBr(
22382270 special_prong,
22392271 extra.data.cases_len,
22402272 0,
2273 inst,
22412274 inst_data.src_node,
22422275 );
22432276}
22442277
2245fn zirSwitchBrMulti(
2278fn zirSwitchBlockMulti(
22462279 sema: *Sema,
22472280 block: *Scope.Block,
22482281 inst: zir.Inst.Index,
22492282 is_ref: bool,
2250 special_prong: SpecialProng,
2251) InnerError!zir.Inst.Index {
2283 special_prong: zir.SpecialProng,
2284) InnerError!*Inst {
22522285 const tracy = trace(@src());
22532286 defer tracy.end();
22542287
......@@ -2270,6 +2303,7 @@ fn zirSwitchBrMulti(
22702303 special_prong,
22712304 extra.data.scalar_cases_len,
22722305 extra.data.multi_cases_len,
2306 inst,
22732307 inst_data.src_node,
22742308 );
22752309}
......@@ -2279,11 +2313,12 @@ fn analyzeSwitch(
22792313 block: *Scope.Block,
22802314 operand: *Inst,
22812315 extra_end: usize,
2282 special_prong: SpecialProng,
2316 special_prong: zir.SpecialProng,
22832317 scalar_cases_len: usize,
22842318 multi_cases_len: usize,
2319 switch_inst: zir.Inst.Index,
22852320 src_node_offset: i32,
2286) InnerError!zir.Inst.Index {
2321) InnerError!*Inst {
22872322 const special: struct { body: []const zir.Inst.Index, end: usize } = switch (special_prong) {
22882323 .none => .{ .body = &.{}, .end = extra_end },
22892324 .under, .@"else" => blk: {
......@@ -2584,7 +2619,7 @@ fn analyzeSwitch(
25842619 const item = try sema.resolveInst(item_ref);
25852620 const item_val = try sema.resolveConstValue(block, item.src, item);
25862621 if (operand_val.eql(item_val)) {
2587 return sema.analyzeBody(block, body);
2622 return sema.resolveBody(block, body);
25882623 }
25892624 }
25902625 }
......@@ -2605,7 +2640,7 @@ fn analyzeSwitch(
26052640 const item = try sema.resolveInst(item_ref);
26062641 const item_val = try sema.resolveConstValue(block, item.src, item);
26072642 if (operand_val.eql(item_val)) {
2608 return sema.analyzeBody(block, body);
2643 return sema.resolveBody(block, body);
26092644 }
26102645 }
26112646
......@@ -2621,26 +2656,59 @@ fn analyzeSwitch(
26212656 if (Value.compare(operand_val, .gte, first_tv.val) and
26222657 Value.compare(operand_val, .lte, last_tv.val))
26232658 {
2624 return sema.analyzeBody(block, body);
2659 return sema.resolveBody(block, body);
26252660 }
26262661 }
26272662
26282663 extra_index += body_len;
26292664 }
26302665 }
2631 return sema.analyzeBody(block, special.body);
2666 return sema.resolveBody(block, special.body);
26322667 }
26332668
26342669 if (scalar_cases_len + multi_cases_len == 0) {
2635 return sema.analyzeBody(block, special.body);
2670 return sema.resolveBody(block, special.body);
26362671 }
26372672
26382673 try sema.requireRuntimeBlock(block, src);
2674
2675 const block_inst = try sema.arena.create(Inst.Block);
2676 block_inst.* = .{
2677 .base = .{
2678 .tag = Inst.Block.base_tag,
2679 .ty = undefined, // Set after analysis.
2680 .src = src,
2681 },
2682 .body = undefined,
2683 };
2684
2685 var child_block: Scope.Block = .{
2686 .parent = block,
2687 .sema = sema,
2688 .src_decl = block.src_decl,
2689 .instructions = .{},
2690 // TODO @as here is working around a stage1 miscompilation bug :(
2691 .label = @as(?Scope.Block.Label, Scope.Block.Label{
2692 .zir_block = switch_inst,
2693 .merges = .{
2694 .results = .{},
2695 .br_list = .{},
2696 .block_inst = block_inst,
2697 },
2698 }),
2699 .inlining = block.inlining,
2700 .is_comptime = block.is_comptime,
2701 };
2702 const merges = &child_block.label.?.merges;
2703 defer child_block.instructions.deinit(sema.gpa);
2704 defer merges.results.deinit(sema.gpa);
2705 defer merges.br_list.deinit(sema.gpa);
2706
26392707 // TODO when reworking TZIR memory layout make multi cases get generated as cases,
26402708 // not as part of the "else" block.
26412709 const cases = try sema.arena.alloc(Inst.SwitchBr.Case, scalar_cases_len);
26422710
2643 var case_block = block.makeSubBlock();
2711 var case_block = child_block.makeSubBlock();
26442712 defer case_block.instructions.deinit(sema.gpa);
26452713
26462714 var extra_index: usize = special.end;
......@@ -2656,7 +2724,7 @@ fn analyzeSwitch(
26562724
26572725 case_block.instructions.shrinkRetainingCapacity(0);
26582726 const item = try sema.resolveInst(item_ref);
2659 const item_val = try sema.resolveConstValue(block, item.src, item);
2727 const item_val = try sema.resolveConstValue(&case_block, item.src, item);
26602728
26612729 _ = try sema.analyzeBody(&case_block, body);
26622730
......@@ -2687,7 +2755,7 @@ fn analyzeSwitch(
26872755
26882756 for (items) |item_ref| {
26892757 const item = try sema.resolveInst(item_ref);
2690 _ = try sema.resolveConstValue(block, item.src, item);
2758 _ = try sema.resolveConstValue(&child_block, item.src, item);
26912759
26922760 const cmp_ok = try case_block.addBinOp(item.src, bool_ty, .cmp_eq, operand, item);
26932761 if (any_ok) |some| {
......@@ -2707,8 +2775,8 @@ fn analyzeSwitch(
27072775 const item_first = try sema.resolveInst(first_ref);
27082776 const item_last = try sema.resolveInst(last_ref);
27092777
2710 _ = try sema.resolveConstValue(block, item_first.src, item_first);
2711 _ = try sema.resolveConstValue(block, item_last.src, item_last);
2778 _ = try sema.resolveConstValue(&child_block, item_first.src, item_first);
2779 _ = try sema.resolveConstValue(&child_block, item_last.src, item_last);
27122780
27132781 const range_src = item_first.src;
27142782
......@@ -2779,8 +2847,8 @@ fn analyzeSwitch(
27792847 .instructions = try sema.arena.dupe(*Inst, &[1]*Inst{&first_condbr.base}),
27802848 };
27812849
2782 _ = try block.addSwitchBr(src, operand, cases, final_else_body);
2783 return always_noreturn;
2850 _ = try child_block.addSwitchBr(src, operand, cases, final_else_body);
2851 return sema.analyzeBlockBody(block, &child_block, merges);
27842852}
27852853
27862854fn validateSwitchItem(
......@@ -3261,12 +3329,18 @@ fn zirCmp(
32613329}
32623330
32633331fn zirTypeof(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
3264 const tracy = trace(@src());
3265 defer tracy.end();
3266
3267 const inst_data = sema.code.instructions.items(.data)[inst].un_tok;
3332 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
3333 const src = inst_data.src();
32683334 const operand = try sema.resolveInst(inst_data.operand);
3269 return sema.mod.constType(sema.arena, inst_data.src(), operand.ty);
3335 return sema.mod.constType(sema.arena, src, operand.ty);
3336}
3337
3338fn zirTypeofElem(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
3339 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
3340 const src = inst_data.src();
3341 const operand_ptr = try sema.resolveInst(inst_data.operand);
3342 const elem_ty = operand_ptr.ty.elemType();
3343 return sema.mod.constType(sema.arena, src, elem_ty);
32703344}
32713345
32723346fn zirTypeofPeer(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
......@@ -3360,8 +3434,7 @@ fn zirBoolBr(
33603434 // comptime-known left-hand side. No need for a block here; the result
33613435 // is simply the rhs expression. Here we rely on there only being 1
33623436 // break instruction (`break_inline`).
3363 const break_inst = try sema.analyzeBody(parent_block, body);
3364 return sema.resolveInst(datas[break_inst].@"break".operand);
3437 return sema.resolveBody(parent_block, body);
33653438 }
33663439
33673440 const block_inst = try sema.arena.create(Inst.Block);
......@@ -3392,8 +3465,7 @@ fn zirBoolBr(
33923465 });
33933466 _ = try lhs_block.addBr(src, block_inst, lhs_result);
33943467
3395 const rhs_break_inst = try sema.analyzeBody(rhs_block, body);
3396 const rhs_result = try sema.resolveInst(datas[rhs_break_inst].@"break".operand);
3468 const rhs_result = try sema.resolveBody(rhs_block, body);
33973469 _ = try rhs_block.addBr(src, block_inst, rhs_result);
33983470
33993471 const tzir_then_body: ir.Body = .{ .instructions = try sema.arena.dupe(*Inst, then_block.instructions.items) };
src/zir.zig+178-55
......@@ -514,6 +514,9 @@ pub const Inst = struct {
514514 /// Returns the type of a value.
515515 /// Uses the `un_tok` field.
516516 typeof,
517 /// Given a value which is a pointer, returns the element type.
518 /// Uses the `un_node` field.
519 typeof_elem,
517520 /// The builtin `@TypeOf` which returns the type after Peer Type Resolution
518521 /// of one or more params.
519522 /// Uses the `pl_node` field. AST node is the `@TypeOf` call. Payload is `MultiOp`.
......@@ -588,32 +591,55 @@ pub const Inst = struct {
588591 /// A switch expression. Uses the `pl_node` union field.
589592 /// AST node is the switch, payload is `SwitchBr`.
590593 /// All prongs of target handled.
591 switch_br,
592 /// Same as switch_br, except one or more prongs have multiple items.
593 switch_br_multi,
594 /// Same as switch_br, except has an else prong.
595 switch_br_else,
596 /// Same as switch_br_else, except one or more prongs have multiple items.
597 switch_br_else_multi,
598 /// Same as switch_br, except has an underscore prong.
599 switch_br_under,
600 /// Same as switch_br, except one or more prongs have multiple items.
601 switch_br_under_multi,
602 /// Same as `switch_br` but the target is a pointer to the value being switched on.
603 switch_br_ref,
604 /// Same as `switch_br_multi` but the target is a pointer to the value being switched on.
605 switch_br_ref_multi,
606 /// Same as `switch_br_else` but the target is a pointer to the value being switched on.
607 switch_br_ref_else,
608 /// Same as `switch_br_else_multi` but the target is a pointer to the
594 switch_block,
595 /// Same as switch_block, except one or more prongs have multiple items.
596 switch_block_multi,
597 /// Same as switch_block, except has an else prong.
598 switch_block_else,
599 /// Same as switch_block_else, except one or more prongs have multiple items.
600 switch_block_else_multi,
601 /// Same as switch_block, except has an underscore prong.
602 switch_block_under,
603 /// Same as switch_block, except one or more prongs have multiple items.
604 switch_block_under_multi,
605 /// Same as `switch_block` but the target is a pointer to the value being switched on.
606 switch_block_ref,
607 /// Same as `switch_block_multi` but the target is a pointer to the value being switched on.
608 switch_block_ref_multi,
609 /// Same as `switch_block_else` but the target is a pointer to the value being switched on.
610 switch_block_ref_else,
611 /// Same as `switch_block_else_multi` but the target is a pointer to the
609612 /// value being switched on.
610 switch_br_ref_else_multi,
611 /// Same as `switch_br_under` but the target is a pointer to the value
613 switch_block_ref_else_multi,
614 /// Same as `switch_block_under` but the target is a pointer to the value
612615 /// being switched on.
613 switch_br_ref_under,
614 /// Same as `switch_br_under_multi` but the target is a pointer to
616 switch_block_ref_under,
617 /// Same as `switch_block_under_multi` but the target is a pointer to
615618 /// the value being switched on.
616 switch_br_ref_under_multi,
619 switch_block_ref_under_multi,
620 /// Produces the capture value for a switch prong.
621 /// Uses the `switch_capture` field.
622 switch_capture,
623 /// Produces the capture value for a switch prong.
624 /// Result is a pointer to the value.
625 /// Uses the `switch_capture` field.
626 switch_capture_ref,
627 /// Produces the capture value for a switch prong.
628 /// The prong is one of the multi cases.
629 /// Uses the `switch_capture` field.
630 switch_capture_multi,
631 /// Produces the capture value for a switch prong.
632 /// The prong is one of the multi cases.
633 /// Result is a pointer to the value.
634 /// Uses the `switch_capture` field.
635 switch_capture_multi_ref,
636 /// Produces the capture value for the else/'_' switch prong.
637 /// Uses the `switch_capture` field.
638 switch_capture_else,
639 /// Produces the capture value for the else/'_' switch prong.
640 /// Result is a pointer to the value.
641 /// Uses the `switch_capture` field.
642 switch_capture_else_ref,
617643
618644 /// Returns whether the instruction is one of the control flow "noreturn" types.
619645 /// Function calls do not count.
......@@ -710,6 +736,7 @@ pub const Inst = struct {
710736 .negate,
711737 .negate_wrap,
712738 .typeof,
739 .typeof_elem,
713740 .xor,
714741 .optional_type,
715742 .optional_type_from_ptr_elem,
......@@ -743,6 +770,24 @@ pub const Inst = struct {
743770 .set_eval_branch_quota,
744771 .compile_log,
745772 .elided,
773 .switch_capture,
774 .switch_capture_ref,
775 .switch_capture_multi,
776 .switch_capture_multi_ref,
777 .switch_capture_else,
778 .switch_capture_else_ref,
779 .switch_block,
780 .switch_block_multi,
781 .switch_block_else,
782 .switch_block_else_multi,
783 .switch_block_under,
784 .switch_block_under_multi,
785 .switch_block_ref,
786 .switch_block_ref_multi,
787 .switch_block_ref_else,
788 .switch_block_ref_else_multi,
789 .switch_block_ref_under,
790 .switch_block_ref_under_multi,
746791 => false,
747792
748793 .@"break",
......@@ -756,18 +801,6 @@ pub const Inst = struct {
756801 .@"unreachable",
757802 .repeat,
758803 .repeat_inline,
759 .switch_br,
760 .switch_br_multi,
761 .switch_br_else,
762 .switch_br_else_multi,
763 .switch_br_under,
764 .switch_br_under_multi,
765 .switch_br_ref,
766 .switch_br_ref_multi,
767 .switch_br_ref_else,
768 .switch_br_ref_else_multi,
769 .switch_br_ref_under,
770 .switch_br_ref_under_multi,
771804 => true,
772805 };
773806 }
......@@ -1223,6 +1256,10 @@ pub const Inst = struct {
12231256 block_inst: Index,
12241257 operand: Ref,
12251258 },
1259 switch_capture: struct {
1260 switch_inst: Index,
1261 prong_index: u32,
1262 },
12261263
12271264 // Make sure we don't accidentally add a field to make this union
12281265 // bigger than expected. Note that in Debug builds, Zig is allowed
......@@ -1394,6 +1431,8 @@ pub const Inst = struct {
13941431 };
13951432};
13961433
1434pub const SpecialProng = enum { none, @"else", under };
1435
13971436const Writer = struct {
13981437 gpa: *Allocator,
13991438 arena: *Allocator,
......@@ -1461,12 +1500,13 @@ const Writer = struct {
14611500 .is_null_ptr,
14621501 .is_err,
14631502 .is_err_ptr,
1503 .typeof,
1504 .typeof_elem,
14641505 => try self.writeUnNode(stream, inst),
14651506
14661507 .ref,
14671508 .ret_tok,
14681509 .ret_coerce,
1469 .typeof,
14701510 .ensure_err_payload_void,
14711511 => try self.writeUnTok(stream, inst),
14721512
......@@ -1542,21 +1582,19 @@ const Writer = struct {
15421582 .condbr_inline,
15431583 => try self.writePlNodeCondBr(stream, inst),
15441584
1545 .switch_br,
1546 .switch_br_else,
1547 .switch_br_under,
1548 .switch_br_ref,
1549 .switch_br_ref_else,
1550 .switch_br_ref_under,
1551 => try self.writePlNodeSwitchBr(stream, inst),
1552
1553 .switch_br_multi,
1554 .switch_br_else_multi,
1555 .switch_br_under_multi,
1556 .switch_br_ref_multi,
1557 .switch_br_ref_else_multi,
1558 .switch_br_ref_under_multi,
1559 => try self.writePlNodeSwitchBrMulti(stream, inst),
1585 .switch_block => try self.writePlNodeSwitchBr(stream, inst, .none),
1586 .switch_block_else => try self.writePlNodeSwitchBr(stream, inst, .@"else"),
1587 .switch_block_under => try self.writePlNodeSwitchBr(stream, inst, .under),
1588 .switch_block_ref => try self.writePlNodeSwitchBr(stream, inst, .none),
1589 .switch_block_ref_else => try self.writePlNodeSwitchBr(stream, inst, .@"else"),
1590 .switch_block_ref_under => try self.writePlNodeSwitchBr(stream, inst, .under),
1591
1592 .switch_block_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .none),
1593 .switch_block_else_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .@"else"),
1594 .switch_block_under_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .under),
1595 .switch_block_ref_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .none),
1596 .switch_block_ref_else_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .@"else"),
1597 .switch_block_ref_under_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .under),
15601598
15611599 .compile_log,
15621600 .typeof_peer,
......@@ -1588,10 +1626,19 @@ const Writer = struct {
15881626 .fn_type_cc => try self.writeFnTypeCc(stream, inst, false),
15891627 .fn_type_var_args => try self.writeFnType(stream, inst, true),
15901628 .fn_type_cc_var_args => try self.writeFnTypeCc(stream, inst, true),
1629
15911630 .@"unreachable" => try self.writeUnreachable(stream, inst),
15921631
15931632 .enum_literal_small => try self.writeSmallStr(stream, inst),
15941633
1634 .switch_capture,
1635 .switch_capture_ref,
1636 .switch_capture_multi,
1637 .switch_capture_multi_ref,
1638 .switch_capture_else,
1639 .switch_capture_else_ref,
1640 => try self.writeSwitchCapture(stream, inst),
1641
15951642 .bitcast,
15961643 .bitcast_ref,
15971644 .bitcast_result_ptr,
......@@ -1763,11 +1810,46 @@ const Writer = struct {
17631810 try self.writeSrc(stream, inst_data.src());
17641811 }
17651812
1766 fn writePlNodeSwitchBr(self: *Writer, stream: anytype, inst: Inst.Index) !void {
1813 fn writePlNodeSwitchBr(
1814 self: *Writer,
1815 stream: anytype,
1816 inst: Inst.Index,
1817 special_prong: SpecialProng,
1818 ) !void {
17671819 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
17681820 const extra = self.code.extraData(Inst.SwitchBr, inst_data.payload_index);
1821 const special: struct {
1822 body: []const Inst.Index,
1823 end: usize,
1824 } = switch (special_prong) {
1825 .none => .{ .body = &.{}, .end = extra.end },
1826 .under, .@"else" => blk: {
1827 const body_len = self.code.extra[extra.end];
1828 const extra_body_start = extra.end + 1;
1829 break :blk .{
1830 .body = self.code.extra[extra_body_start..][0..body_len],
1831 .end = extra_body_start + body_len,
1832 };
1833 },
1834 };
1835
17691836 try self.writeInstRef(stream, extra.data.operand);
1770 var extra_index: usize = extra.end;
1837
1838 if (special.body.len != 0) {
1839 const prong_name = switch (special_prong) {
1840 .@"else" => "else",
1841 .under => "_",
1842 else => unreachable,
1843 };
1844 try stream.print(", {s} => {{\n", .{prong_name});
1845 self.indent += 2;
1846 try self.writeBody(stream, special.body);
1847 self.indent -= 2;
1848 try stream.writeByteNTimes(' ', self.indent);
1849 try stream.writeAll("}");
1850 }
1851
1852 var extra_index: usize = special.end;
17711853 {
17721854 var scalar_i: usize = 0;
17731855 while (scalar_i < extra.data.cases_len) : (scalar_i += 1) {
......@@ -1792,11 +1874,46 @@ const Writer = struct {
17921874 try self.writeSrc(stream, inst_data.src());
17931875 }
17941876
1795 fn writePlNodeSwitchBrMulti(self: *Writer, stream: anytype, inst: Inst.Index) !void {
1877 fn writePlNodeSwitchBlockMulti(
1878 self: *Writer,
1879 stream: anytype,
1880 inst: Inst.Index,
1881 special_prong: SpecialProng,
1882 ) !void {
17961883 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
17971884 const extra = self.code.extraData(Inst.SwitchBrMulti, inst_data.payload_index);
1885 const special: struct {
1886 body: []const Inst.Index,
1887 end: usize,
1888 } = switch (special_prong) {
1889 .none => .{ .body = &.{}, .end = extra.end },
1890 .under, .@"else" => blk: {
1891 const body_len = self.code.extra[extra.end];
1892 const extra_body_start = extra.end + 1;
1893 break :blk .{
1894 .body = self.code.extra[extra_body_start..][0..body_len],
1895 .end = extra_body_start + body_len,
1896 };
1897 },
1898 };
1899
17981900 try self.writeInstRef(stream, extra.data.operand);
1799 var extra_index: usize = extra.end;
1901
1902 if (special.body.len != 0) {
1903 const prong_name = switch (special_prong) {
1904 .@"else" => "else",
1905 .under => "_",
1906 else => unreachable,
1907 };
1908 try stream.print(", {s} => {{\n", .{prong_name});
1909 self.indent += 2;
1910 try self.writeBody(stream, special.body);
1911 self.indent -= 2;
1912 try stream.writeByteNTimes(' ', self.indent);
1913 try stream.writeAll("}");
1914 }
1915
1916 var extra_index: usize = special.end;
18001917 {
18011918 var scalar_i: usize = 0;
18021919 while (scalar_i < extra.data.scalar_cases_len) : (scalar_i += 1) {
......@@ -2015,6 +2132,12 @@ const Writer = struct {
20152132 try stream.print("\"{}\")", .{std.zig.fmtEscapes(str)});
20162133 }
20172134
2135 fn writeSwitchCapture(self: *Writer, stream: anytype, inst: Inst.Index) !void {
2136 const inst_data = self.code.instructions.items(.data)[inst].switch_capture;
2137 try self.writeInstIndex(stream, inst_data.switch_inst);
2138 try stream.print(", {d})", .{inst_data.prong_index});
2139 }
2140
20182141 fn writeInstRef(self: *Writer, stream: anytype, ref: Inst.Ref) !void {
20192142 var i: usize = @enumToInt(ref);
20202143