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:...@@ -82,47 +82,3 @@ Performance optimizations to look into:
82 }82 }
83 }83 }
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;...@@ -22,6 +22,7 @@ const Scope = Module.Scope;
22const GenZir = Scope.GenZir;22const GenZir = Scope.GenZir;
23const InnerError = Module.InnerError;23const InnerError = Module.InnerError;
24const Decl = Module.Decl;24const Decl = Module.Decl;
25const LazySrcLoc = Module.LazySrcLoc;
25const BuiltinFn = @import("BuiltinFn.zig");26const BuiltinFn = @import("BuiltinFn.zig");
2627
27instructions: std.MultiArrayList(zir.Inst) = .{},28instructions: std.MultiArrayList(zir.Inst) = .{},
...@@ -1215,6 +1216,7 @@ fn blockExprStmts(...@@ -1215,6 +1216,7 @@ fn blockExprStmts(
1215 .negate,1216 .negate,
1216 .negate_wrap,1217 .negate_wrap,
1217 .typeof,1218 .typeof,
1219 .typeof_elem,
1218 .xor,1220 .xor,
1219 .optional_type,1221 .optional_type,
1220 .optional_type_from_ptr_elem,1222 .optional_type_from_ptr_elem,
...@@ -1243,6 +1245,24 @@ fn blockExprStmts(...@@ -1243,6 +1245,24 @@ fn blockExprStmts(
1243 .slice_sentinel,1245 .slice_sentinel,
1244 .import,1246 .import,
1245 .typeof_peer,1247 .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,
1246 => break :b false,1266 => break :b false,
12471267
1248 // ZIR instructions that are always either `noreturn` or `void`.1268 // ZIR instructions that are always either `noreturn` or `void`.
...@@ -1257,18 +1277,6 @@ fn blockExprStmts(...@@ -1257,18 +1277,6 @@ fn blockExprStmts(
1257 .break_inline,1277 .break_inline,
1258 .condbr,1278 .condbr,
1259 .condbr_inline,1279 .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,
1272 .compile_error,1280 .compile_error,
1273 .ret_node,1281 .ret_node,
1274 .ret_tok,1282 .ret_tok,
...@@ -1543,7 +1551,7 @@ fn assignOp(...@@ -1543,7 +1551,7 @@ fn assignOp(
15431551
1544 const lhs_ptr = try lvalExpr(gz, scope, node_datas[infix_node].lhs);1552 const lhs_ptr = try lvalExpr(gz, scope, node_datas[infix_node].lhs);
1545 const lhs = try gz.addUnNode(.load, lhs_ptr, infix_node);1553 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);
1547 const rhs = try expr(gz, scope, .{ .ty = lhs_type }, node_datas[infix_node].rhs);1555 const rhs = try expr(gz, scope, .{ .ty = lhs_type }, node_datas[infix_node].rhs);
15481556
1549 const result = try gz.addPlNode(op_inst_tag, infix_node, zir.Inst.Bin{1557 const result = try gz.addPlNode(op_inst_tag, infix_node, zir.Inst.Bin{
...@@ -2548,15 +2556,28 @@ fn switchExpr(...@@ -2548,15 +2556,28 @@ fn switchExpr(
2548 rl: ResultLoc,2556 rl: ResultLoc,
2549 switch_node: ast.Node.Index,2557 switch_node: ast.Node.Index,
2550) InnerError!zir.Inst.Ref {2558) InnerError!zir.Inst.Ref {
2559 const astgen = parent_gz.astgen;
2560 const mod = astgen.mod;
2561 const gpa = mod.gpa;
2551 const tree = parent_gz.tree();2562 const tree = parent_gz.tree();
2552 const node_datas = tree.nodes.items(.data);2563 const node_datas = tree.nodes.items(.data);
2553 const node_tags = tree.nodes.items(.tag);2564 const node_tags = tree.nodes.items(.tag);
2565 const main_tokens = tree.nodes.items(.main_token);
2554 const token_tags = tree.tokens.items(.tag);2566 const token_tags = tree.tokens.items(.tag);
2555 const operand_node = node_datas[switch_node].lhs;2567 const operand_node = node_datas[switch_node].lhs;
2556 const extra = tree.extraData(node_datas[switch_node].rhs, ast.Node.SubRange);2568 const extra = tree.extraData(node_datas[switch_node].rhs, ast.Node.SubRange);
2557 const case_nodes = tree.extra_data[extra.start..extra.end];2569 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.
2559 var any_payload_is_ref = false;2574 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;
2560 for (case_nodes) |case_node| {2581 for (case_nodes) |case_node| {
2561 const case = switch (node_tags[case_node]) {2582 const case = switch (node_tags[case_node]) {
2562 .switch_case_one => tree.switchCaseOne(case_node),2583 .switch_case_one => tree.switchCaseOne(case_node),
...@@ -2568,22 +2589,346 @@ fn switchExpr(...@@ -2568,22 +2589,346 @@ fn switchExpr(
2568 any_payload_is_ref = true;2589 any_payload_is_ref = true;
2569 }2590 }
2570 }2591 }
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 }
2571 }2673 }
25722674
2573 const rl_and_tag: struct { rl: ResultLoc, tag: zir.Inst.Tag } = if (any_payload_is_ref) .{2675 const operand_rl: ResultLoc = if (any_payload_is_ref) .ref else .none;
2574 .rl = .ref,2676 const operand = try expr(parent_gz, scope, operand_rl, operand_node);
2575 .tag = .switch_br_ref,2677 // We need the type of the operand to use as the result location for all the prong items.
2576 } else .{2678 const typeof_tag: zir.Inst.Tag = if (any_payload_is_ref) .typeof_elem else .typeof;
2577 .rl = .none,2679 const operand_ty_inst = try parent_gz.addUnNode(typeof_tag, operand, operand_node);
2578 .tag = .switch_br,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 = .{},
2579 };2697 };
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{2701 // This gets added to the parent block later, after the item expressions.
2583 .operand = operand,2702 const switch_block = try parent_gz.addBlock(undefined, switch_node);
2584 .cases_len = 0,2703
2585 });2704 // We re-use this same scope for all cases, including the special prong, if any.
2586 return rvalue(parent_gz, scope, rl, result, switch_node);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);
2587}2932}
25882933
2589fn ret(gz: *GenZir, scope: *Scope, node: ast.Node.Index) InnerError!zir.Inst.Ref {2934fn ret(gz: *GenZir, scope: *Scope, node: ast.Node.Index) InnerError!zir.Inst.Ref {
...@@ -3021,7 +3366,7 @@ fn typeOf(...@@ -3021,7 +3366,7 @@ fn typeOf(
3021 return gz.astgen.mod.failTok(scope, builtin_token, "expected at least 1 argument, found 0", .{});3366 return gz.astgen.mod.failTok(scope, builtin_token, "expected at least 1 argument, found 0", .{});
3022 }3367 }
3023 if (params.len == 1) {3368 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);
3025 return rvalue(gz, scope, rl, result, node);3370 return rvalue(gz, scope, rl, result, node);
3026 }3371 }
3027 const arena = gz.astgen.arena;3372 const arena = gz.astgen.arena;
src/Sema.zig+126-54
...@@ -84,6 +84,15 @@ pub fn rootAsType(sema: *Sema, root_block: *Scope.Block) !Type {...@@ -84,6 +84,15 @@ pub fn rootAsType(sema: *Sema, root_block: *Scope.Block) !Type {
84 return sema.resolveType(root_block, .unneeded, zir_inst_ref);84 return sema.resolveType(root_block, .unneeded, zir_inst_ref);
85}85}
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
87/// ZIR instructions which are always `noreturn` return this. This matches the96/// ZIR instructions which are always `noreturn` return this. This matches the
88/// return type of `analyzeBody` so that we can tail call them.97/// return type of `analyzeBody` so that we can tail call them.
89/// Only appropriate to return when the instruction is known to be NoReturn98/// Only appropriate to return when the instruction is known to be NoReturn
...@@ -229,7 +238,26 @@ pub fn analyzeBody(...@@ -229,7 +238,26 @@ pub fn analyzeBody(
229 .str => try sema.zirStr(block, inst),238 .str => try sema.zirStr(block, inst),
230 .sub => try sema.zirArithmetic(block, inst),239 .sub => try sema.zirArithmetic(block, inst),
231 .subwrap => try sema.zirArithmetic(block, inst),240 .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),
232 .typeof => try sema.zirTypeof(block, inst),259 .typeof => try sema.zirTypeof(block, inst),
260 .typeof_elem => try sema.zirTypeofElem(block, inst),
233 .typeof_peer => try sema.zirTypeofPeer(block, inst),261 .typeof_peer => try sema.zirTypeofPeer(block, inst),
234 .xor => try sema.zirBitwise(block, inst, .xor),262 .xor => try sema.zirBitwise(block, inst, .xor),
235263
...@@ -245,18 +273,6 @@ pub fn analyzeBody(...@@ -245,18 +273,6 @@ pub fn analyzeBody(
245 .ret_tok => return sema.zirRetTok(block, inst, false),273 .ret_tok => return sema.zirRetTok(block, inst, false),
246 .@"unreachable" => return sema.zirUnreachable(block, inst),274 .@"unreachable" => return sema.zirUnreachable(block, inst),
247 .repeat => return sema.zirRepeat(block, inst),275 .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
261 // Instructions that we know can *never* be noreturn based solely on277 // Instructions that we know can *never* be noreturn based solely on
262 // their tag. We avoid needlessly checking if they are noreturn and278 // their tag. We avoid needlessly checking if they are noreturn and
...@@ -1034,7 +1050,7 @@ fn analyzeBlockBody(...@@ -1034,7 +1050,7 @@ fn analyzeBlockBody(
1034 }1050 }
1035 assert(coerce_block.instructions.items[coerce_block.instructions.items.len - 1] == coerced_operand);1051 assert(coerce_block.instructions.items[coerce_block.instructions.items.len - 1] == coerced_operand);
1036 // Here we depend on the br instruction having been over-allocated (if necessary)1052 // 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.
1038 const br_src = br.base.src;1054 const br_src = br.base.src;
1039 const br_ty = br.base.ty;1055 const br_ty = br.base.ty;
1040 const br_block_flat = @ptrCast(*Inst.BrBlockFlat, br);1056 const br_block_flat = @ptrCast(*Inst.BrBlockFlat, br);
...@@ -1063,22 +1079,15 @@ fn zirBreakpoint(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerEr...@@ -1063,22 +1079,15 @@ fn zirBreakpoint(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerEr
1063 _ = try block.addNoOp(src, Type.initTag(.void), .breakpoint);1079 _ = try block.addNoOp(src, Type.initTag(.void), .breakpoint);
1064}1080}
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 {
1067 const tracy = trace(@src());1083 const tracy = trace(@src());
1068 defer tracy.end();1084 defer tracy.end();
10691085
1070 const inst_data = sema.code.instructions.items(.data)[inst].@"break";1086 const inst_data = sema.code.instructions.items(.data)[inst].@"break";
1087 const src = sema.src;
1071 const operand = try sema.resolveInst(inst_data.operand);1088 const operand = try sema.resolveInst(inst_data.operand);
1072 return sema.analyzeBreak(block, sema.src, inst_data.block_inst, operand);1089 const zir_block = inst_data.block_inst;
1073}
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 {
1082 var block = start_block;1091 var block = start_block;
1083 while (true) {1092 while (true) {
1084 if (block.label) |*label| {1093 if (block.label) |*label| {
...@@ -1103,7 +1112,7 @@ fn analyzeBreak(...@@ -1103,7 +1112,7 @@ fn analyzeBreak(
1103 try start_block.instructions.append(sema.gpa, &br.base);1112 try start_block.instructions.append(sema.gpa, &br.base);
1104 try label.merges.results.append(sema.gpa, operand);1113 try label.merges.results.append(sema.gpa, operand);
1105 try label.merges.br_list.append(sema.gpa, br);1114 try label.merges.br_list.append(sema.gpa, br);
1106 return always_noreturn;1115 return inst;
1107 }1116 }
1108 }1117 }
1109 block = block.parent.?;1118 block = block.parent.?;
...@@ -2208,15 +2217,38 @@ fn zirSliceSentinel(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) Inne...@@ -2208,15 +2217,38 @@ fn zirSliceSentinel(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) Inne
2208 return sema.analyzeSlice(block, src, array_ptr, start, end, sentinel, sentinel_src);2217 return sema.analyzeSlice(block, src, array_ptr, start, end, sentinel, sentinel_src);
2209}2218}
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(
2214 sema: *Sema,2234 sema: *Sema,
2215 block: *Scope.Block,2235 block: *Scope.Block,
2216 inst: zir.Inst.Index,2236 inst: zir.Inst.Index,
2217 is_ref: bool,2237 is_ref: bool,
2218 special_prong: SpecialProng,2238) InnerError!*Inst {
2219) InnerError!zir.Inst.Index {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 {
2220 const tracy = trace(@src());2252 const tracy = trace(@src());
2221 defer tracy.end();2253 defer tracy.end();
22222254
...@@ -2238,17 +2270,18 @@ fn zirSwitchBr(...@@ -2238,17 +2270,18 @@ fn zirSwitchBr(
2238 special_prong,2270 special_prong,
2239 extra.data.cases_len,2271 extra.data.cases_len,
2240 0,2272 0,
2273 inst,
2241 inst_data.src_node,2274 inst_data.src_node,
2242 );2275 );
2243}2276}
22442277
2245fn zirSwitchBrMulti(2278fn zirSwitchBlockMulti(
2246 sema: *Sema,2279 sema: *Sema,
2247 block: *Scope.Block,2280 block: *Scope.Block,
2248 inst: zir.Inst.Index,2281 inst: zir.Inst.Index,
2249 is_ref: bool,2282 is_ref: bool,
2250 special_prong: SpecialProng,2283 special_prong: zir.SpecialProng,
2251) InnerError!zir.Inst.Index {2284) InnerError!*Inst {
2252 const tracy = trace(@src());2285 const tracy = trace(@src());
2253 defer tracy.end();2286 defer tracy.end();
22542287
...@@ -2270,6 +2303,7 @@ fn zirSwitchBrMulti(...@@ -2270,6 +2303,7 @@ fn zirSwitchBrMulti(
2270 special_prong,2303 special_prong,
2271 extra.data.scalar_cases_len,2304 extra.data.scalar_cases_len,
2272 extra.data.multi_cases_len,2305 extra.data.multi_cases_len,
2306 inst,
2273 inst_data.src_node,2307 inst_data.src_node,
2274 );2308 );
2275}2309}
...@@ -2279,11 +2313,12 @@ fn analyzeSwitch(...@@ -2279,11 +2313,12 @@ fn analyzeSwitch(
2279 block: *Scope.Block,2313 block: *Scope.Block,
2280 operand: *Inst,2314 operand: *Inst,
2281 extra_end: usize,2315 extra_end: usize,
2282 special_prong: SpecialProng,2316 special_prong: zir.SpecialProng,
2283 scalar_cases_len: usize,2317 scalar_cases_len: usize,
2284 multi_cases_len: usize,2318 multi_cases_len: usize,
2319 switch_inst: zir.Inst.Index,
2285 src_node_offset: i32,2320 src_node_offset: i32,
2286) InnerError!zir.Inst.Index {2321) InnerError!*Inst {
2287 const special: struct { body: []const zir.Inst.Index, end: usize } = switch (special_prong) {2322 const special: struct { body: []const zir.Inst.Index, end: usize } = switch (special_prong) {
2288 .none => .{ .body = &.{}, .end = extra_end },2323 .none => .{ .body = &.{}, .end = extra_end },
2289 .under, .@"else" => blk: {2324 .under, .@"else" => blk: {
...@@ -2584,7 +2619,7 @@ fn analyzeSwitch(...@@ -2584,7 +2619,7 @@ fn analyzeSwitch(
2584 const item = try sema.resolveInst(item_ref);2619 const item = try sema.resolveInst(item_ref);
2585 const item_val = try sema.resolveConstValue(block, item.src, item);2620 const item_val = try sema.resolveConstValue(block, item.src, item);
2586 if (operand_val.eql(item_val)) {2621 if (operand_val.eql(item_val)) {
2587 return sema.analyzeBody(block, body);2622 return sema.resolveBody(block, body);
2588 }2623 }
2589 }2624 }
2590 }2625 }
...@@ -2605,7 +2640,7 @@ fn analyzeSwitch(...@@ -2605,7 +2640,7 @@ fn analyzeSwitch(
2605 const item = try sema.resolveInst(item_ref);2640 const item = try sema.resolveInst(item_ref);
2606 const item_val = try sema.resolveConstValue(block, item.src, item);2641 const item_val = try sema.resolveConstValue(block, item.src, item);
2607 if (operand_val.eql(item_val)) {2642 if (operand_val.eql(item_val)) {
2608 return sema.analyzeBody(block, body);2643 return sema.resolveBody(block, body);
2609 }2644 }
2610 }2645 }
26112646
...@@ -2621,26 +2656,59 @@ fn analyzeSwitch(...@@ -2621,26 +2656,59 @@ fn analyzeSwitch(
2621 if (Value.compare(operand_val, .gte, first_tv.val) and2656 if (Value.compare(operand_val, .gte, first_tv.val) and
2622 Value.compare(operand_val, .lte, last_tv.val))2657 Value.compare(operand_val, .lte, last_tv.val))
2623 {2658 {
2624 return sema.analyzeBody(block, body);2659 return sema.resolveBody(block, body);
2625 }2660 }
2626 }2661 }
26272662
2628 extra_index += body_len;2663 extra_index += body_len;
2629 }2664 }
2630 }2665 }
2631 return sema.analyzeBody(block, special.body);2666 return sema.resolveBody(block, special.body);
2632 }2667 }
26332668
2634 if (scalar_cases_len + multi_cases_len == 0) {2669 if (scalar_cases_len + multi_cases_len == 0) {
2635 return sema.analyzeBody(block, special.body);2670 return sema.resolveBody(block, special.body);
2636 }2671 }
26372672
2638 try sema.requireRuntimeBlock(block, src);2673 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
2639 // TODO when reworking TZIR memory layout make multi cases get generated as cases,2707 // TODO when reworking TZIR memory layout make multi cases get generated as cases,
2640 // not as part of the "else" block.2708 // not as part of the "else" block.
2641 const cases = try sema.arena.alloc(Inst.SwitchBr.Case, scalar_cases_len);2709 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();
2644 defer case_block.instructions.deinit(sema.gpa);2712 defer case_block.instructions.deinit(sema.gpa);
26452713
2646 var extra_index: usize = special.end;2714 var extra_index: usize = special.end;
...@@ -2656,7 +2724,7 @@ fn analyzeSwitch(...@@ -2656,7 +2724,7 @@ fn analyzeSwitch(
26562724
2657 case_block.instructions.shrinkRetainingCapacity(0);2725 case_block.instructions.shrinkRetainingCapacity(0);
2658 const item = try sema.resolveInst(item_ref);2726 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
2661 _ = try sema.analyzeBody(&case_block, body);2729 _ = try sema.analyzeBody(&case_block, body);
26622730
...@@ -2687,7 +2755,7 @@ fn analyzeSwitch(...@@ -2687,7 +2755,7 @@ fn analyzeSwitch(
26872755
2688 for (items) |item_ref| {2756 for (items) |item_ref| {
2689 const item = try sema.resolveInst(item_ref);2757 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
2692 const cmp_ok = try case_block.addBinOp(item.src, bool_ty, .cmp_eq, operand, item);2760 const cmp_ok = try case_block.addBinOp(item.src, bool_ty, .cmp_eq, operand, item);
2693 if (any_ok) |some| {2761 if (any_ok) |some| {
...@@ -2707,8 +2775,8 @@ fn analyzeSwitch(...@@ -2707,8 +2775,8 @@ fn analyzeSwitch(
2707 const item_first = try sema.resolveInst(first_ref);2775 const item_first = try sema.resolveInst(first_ref);
2708 const item_last = try sema.resolveInst(last_ref);2776 const item_last = try sema.resolveInst(last_ref);
27092777
2710 _ = try sema.resolveConstValue(block, item_first.src, item_first);2778 _ = try sema.resolveConstValue(&child_block, item_first.src, item_first);
2711 _ = try sema.resolveConstValue(block, item_last.src, item_last);2779 _ = try sema.resolveConstValue(&child_block, item_last.src, item_last);
27122780
2713 const range_src = item_first.src;2781 const range_src = item_first.src;
27142782
...@@ -2779,8 +2847,8 @@ fn analyzeSwitch(...@@ -2779,8 +2847,8 @@ fn analyzeSwitch(
2779 .instructions = try sema.arena.dupe(*Inst, &[1]*Inst{&first_condbr.base}),2847 .instructions = try sema.arena.dupe(*Inst, &[1]*Inst{&first_condbr.base}),
2780 };2848 };
27812849
2782 _ = try block.addSwitchBr(src, operand, cases, final_else_body);2850 _ = try child_block.addSwitchBr(src, operand, cases, final_else_body);
2783 return always_noreturn;2851 return sema.analyzeBlockBody(block, &child_block, merges);
2784}2852}
27852853
2786fn validateSwitchItem(2854fn validateSwitchItem(
...@@ -3261,12 +3329,18 @@ fn zirCmp(...@@ -3261,12 +3329,18 @@ fn zirCmp(
3261}3329}
32623330
3263fn zirTypeof(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {3331fn zirTypeof(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
3264 const tracy = trace(@src());3332 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
3265 defer tracy.end();3333 const src = inst_data.src();
3266
3267 const inst_data = sema.code.instructions.items(.data)[inst].un_tok;
3268 const operand = try sema.resolveInst(inst_data.operand);3334 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);
3270}3344}
32713345
3272fn zirTypeofPeer(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {3346fn zirTypeofPeer(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
...@@ -3360,8 +3434,7 @@ fn zirBoolBr(...@@ -3360,8 +3434,7 @@ fn zirBoolBr(
3360 // comptime-known left-hand side. No need for a block here; the result3434 // comptime-known left-hand side. No need for a block here; the result
3361 // is simply the rhs expression. Here we rely on there only being 13435 // is simply the rhs expression. Here we rely on there only being 1
3362 // break instruction (`break_inline`).3436 // break instruction (`break_inline`).
3363 const break_inst = try sema.analyzeBody(parent_block, body);3437 return sema.resolveBody(parent_block, body);
3364 return sema.resolveInst(datas[break_inst].@"break".operand);
3365 }3438 }
33663439
3367 const block_inst = try sema.arena.create(Inst.Block);3440 const block_inst = try sema.arena.create(Inst.Block);
...@@ -3392,8 +3465,7 @@ fn zirBoolBr(...@@ -3392,8 +3465,7 @@ fn zirBoolBr(
3392 });3465 });
3393 _ = try lhs_block.addBr(src, block_inst, lhs_result);3466 _ = try lhs_block.addBr(src, block_inst, lhs_result);
33943467
3395 const rhs_break_inst = try sema.analyzeBody(rhs_block, body);3468 const rhs_result = try sema.resolveBody(rhs_block, body);
3396 const rhs_result = try sema.resolveInst(datas[rhs_break_inst].@"break".operand);
3397 _ = try rhs_block.addBr(src, block_inst, rhs_result);3469 _ = try rhs_block.addBr(src, block_inst, rhs_result);
33983470
3399 const tzir_then_body: ir.Body = .{ .instructions = try sema.arena.dupe(*Inst, then_block.instructions.items) };3471 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 {...@@ -514,6 +514,9 @@ pub const Inst = struct {
514 /// Returns the type of a value.514 /// Returns the type of a value.
515 /// Uses the `un_tok` field.515 /// Uses the `un_tok` field.
516 typeof,516 typeof,
517 /// Given a value which is a pointer, returns the element type.
518 /// Uses the `un_node` field.
519 typeof_elem,
517 /// The builtin `@TypeOf` which returns the type after Peer Type Resolution520 /// The builtin `@TypeOf` which returns the type after Peer Type Resolution
518 /// of one or more params.521 /// of one or more params.
519 /// Uses the `pl_node` field. AST node is the `@TypeOf` call. Payload is `MultiOp`.522 /// Uses the `pl_node` field. AST node is the `@TypeOf` call. Payload is `MultiOp`.
...@@ -588,32 +591,55 @@ pub const Inst = struct {...@@ -588,32 +591,55 @@ pub const Inst = struct {
588 /// A switch expression. Uses the `pl_node` union field.591 /// A switch expression. Uses the `pl_node` union field.
589 /// AST node is the switch, payload is `SwitchBr`.592 /// AST node is the switch, payload is `SwitchBr`.
590 /// All prongs of target handled.593 /// All prongs of target handled.
591 switch_br,594 switch_block,
592 /// Same as switch_br, except one or more prongs have multiple items.595 /// Same as switch_block, except one or more prongs have multiple items.
593 switch_br_multi,596 switch_block_multi,
594 /// Same as switch_br, except has an else prong.597 /// Same as switch_block, except has an else prong.
595 switch_br_else,598 switch_block_else,
596 /// Same as switch_br_else, except one or more prongs have multiple items.599 /// Same as switch_block_else, except one or more prongs have multiple items.
597 switch_br_else_multi,600 switch_block_else_multi,
598 /// Same as switch_br, except has an underscore prong.601 /// Same as switch_block, except has an underscore prong.
599 switch_br_under,602 switch_block_under,
600 /// Same as switch_br, except one or more prongs have multiple items.603 /// Same as switch_block, except one or more prongs have multiple items.
601 switch_br_under_multi,604 switch_block_under_multi,
602 /// Same as `switch_br` but the target is a pointer to the value being switched on.605 /// Same as `switch_block` but the target is a pointer to the value being switched on.
603 switch_br_ref,606 switch_block_ref,
604 /// Same as `switch_br_multi` but the target is a pointer to the value being switched on.607 /// Same as `switch_block_multi` but the target is a pointer to the value being switched on.
605 switch_br_ref_multi,608 switch_block_ref_multi,
606 /// Same as `switch_br_else` but the target is a pointer to the value being switched on.609 /// Same as `switch_block_else` but the target is a pointer to the value being switched on.
607 switch_br_ref_else,610 switch_block_ref_else,
608 /// Same as `switch_br_else_multi` but the target is a pointer to the611 /// Same as `switch_block_else_multi` but the target is a pointer to the
609 /// value being switched on.612 /// value being switched on.
610 switch_br_ref_else_multi,613 switch_block_ref_else_multi,
611 /// Same as `switch_br_under` but the target is a pointer to the value614 /// Same as `switch_block_under` but the target is a pointer to the value
612 /// being switched on.615 /// being switched on.
613 switch_br_ref_under,616 switch_block_ref_under,
614 /// Same as `switch_br_under_multi` but the target is a pointer to617 /// Same as `switch_block_under_multi` but the target is a pointer to
615 /// the value being switched on.618 /// 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
618 /// Returns whether the instruction is one of the control flow "noreturn" types.644 /// Returns whether the instruction is one of the control flow "noreturn" types.
619 /// Function calls do not count.645 /// Function calls do not count.
...@@ -710,6 +736,7 @@ pub const Inst = struct {...@@ -710,6 +736,7 @@ pub const Inst = struct {
710 .negate,736 .negate,
711 .negate_wrap,737 .negate_wrap,
712 .typeof,738 .typeof,
739 .typeof_elem,
713 .xor,740 .xor,
714 .optional_type,741 .optional_type,
715 .optional_type_from_ptr_elem,742 .optional_type_from_ptr_elem,
...@@ -743,6 +770,24 @@ pub const Inst = struct {...@@ -743,6 +770,24 @@ pub const Inst = struct {
743 .set_eval_branch_quota,770 .set_eval_branch_quota,
744 .compile_log,771 .compile_log,
745 .elided,772 .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,
746 => false,791 => false,
747792
748 .@"break",793 .@"break",
...@@ -756,18 +801,6 @@ pub const Inst = struct {...@@ -756,18 +801,6 @@ pub const Inst = struct {
756 .@"unreachable",801 .@"unreachable",
757 .repeat,802 .repeat,
758 .repeat_inline,803 .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,
771 => true,804 => true,
772 };805 };
773 }806 }
...@@ -1223,6 +1256,10 @@ pub const Inst = struct {...@@ -1223,6 +1256,10 @@ pub const Inst = struct {
1223 block_inst: Index,1256 block_inst: Index,
1224 operand: Ref,1257 operand: Ref,
1225 },1258 },
1259 switch_capture: struct {
1260 switch_inst: Index,
1261 prong_index: u32,
1262 },
12261263
1227 // Make sure we don't accidentally add a field to make this union1264 // Make sure we don't accidentally add a field to make this union
1228 // bigger than expected. Note that in Debug builds, Zig is allowed1265 // bigger than expected. Note that in Debug builds, Zig is allowed
...@@ -1394,6 +1431,8 @@ pub const Inst = struct {...@@ -1394,6 +1431,8 @@ pub const Inst = struct {
1394 };1431 };
1395};1432};
13961433
1434pub const SpecialProng = enum { none, @"else", under };
1435
1397const Writer = struct {1436const Writer = struct {
1398 gpa: *Allocator,1437 gpa: *Allocator,
1399 arena: *Allocator,1438 arena: *Allocator,
...@@ -1461,12 +1500,13 @@ const Writer = struct {...@@ -1461,12 +1500,13 @@ const Writer = struct {
1461 .is_null_ptr,1500 .is_null_ptr,
1462 .is_err,1501 .is_err,
1463 .is_err_ptr,1502 .is_err_ptr,
1503 .typeof,
1504 .typeof_elem,
1464 => try self.writeUnNode(stream, inst),1505 => try self.writeUnNode(stream, inst),
14651506
1466 .ref,1507 .ref,
1467 .ret_tok,1508 .ret_tok,
1468 .ret_coerce,1509 .ret_coerce,
1469 .typeof,
1470 .ensure_err_payload_void,1510 .ensure_err_payload_void,
1471 => try self.writeUnTok(stream, inst),1511 => try self.writeUnTok(stream, inst),
14721512
...@@ -1542,21 +1582,19 @@ const Writer = struct {...@@ -1542,21 +1582,19 @@ const Writer = struct {
1542 .condbr_inline,1582 .condbr_inline,
1543 => try self.writePlNodeCondBr(stream, inst),1583 => try self.writePlNodeCondBr(stream, inst),
15441584
1545 .switch_br,1585 .switch_block => try self.writePlNodeSwitchBr(stream, inst, .none),
1546 .switch_br_else,1586 .switch_block_else => try self.writePlNodeSwitchBr(stream, inst, .@"else"),
1547 .switch_br_under,1587 .switch_block_under => try self.writePlNodeSwitchBr(stream, inst, .under),
1548 .switch_br_ref,1588 .switch_block_ref => try self.writePlNodeSwitchBr(stream, inst, .none),
1549 .switch_br_ref_else,1589 .switch_block_ref_else => try self.writePlNodeSwitchBr(stream, inst, .@"else"),
1550 .switch_br_ref_under,1590 .switch_block_ref_under => try self.writePlNodeSwitchBr(stream, inst, .under),
1551 => try self.writePlNodeSwitchBr(stream, inst),1591
15521592 .switch_block_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .none),
1553 .switch_br_multi,1593 .switch_block_else_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .@"else"),
1554 .switch_br_else_multi,1594 .switch_block_under_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .under),
1555 .switch_br_under_multi,1595 .switch_block_ref_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .none),
1556 .switch_br_ref_multi,1596 .switch_block_ref_else_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .@"else"),
1557 .switch_br_ref_else_multi,1597 .switch_block_ref_under_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .under),
1558 .switch_br_ref_under_multi,
1559 => try self.writePlNodeSwitchBrMulti(stream, inst),
15601598
1561 .compile_log,1599 .compile_log,
1562 .typeof_peer,1600 .typeof_peer,
...@@ -1588,10 +1626,19 @@ const Writer = struct {...@@ -1588,10 +1626,19 @@ const Writer = struct {
1588 .fn_type_cc => try self.writeFnTypeCc(stream, inst, false),1626 .fn_type_cc => try self.writeFnTypeCc(stream, inst, false),
1589 .fn_type_var_args => try self.writeFnType(stream, inst, true),1627 .fn_type_var_args => try self.writeFnType(stream, inst, true),
1590 .fn_type_cc_var_args => try self.writeFnTypeCc(stream, inst, true),1628 .fn_type_cc_var_args => try self.writeFnTypeCc(stream, inst, true),
1629
1591 .@"unreachable" => try self.writeUnreachable(stream, inst),1630 .@"unreachable" => try self.writeUnreachable(stream, inst),
15921631
1593 .enum_literal_small => try self.writeSmallStr(stream, inst),1632 .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
1595 .bitcast,1642 .bitcast,
1596 .bitcast_ref,1643 .bitcast_ref,
1597 .bitcast_result_ptr,1644 .bitcast_result_ptr,
...@@ -1763,11 +1810,46 @@ const Writer = struct {...@@ -1763,11 +1810,46 @@ const Writer = struct {
1763 try self.writeSrc(stream, inst_data.src());1810 try self.writeSrc(stream, inst_data.src());
1764 }1811 }
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 {
1767 const inst_data = self.code.instructions.items(.data)[inst].pl_node;1819 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
1768 const extra = self.code.extraData(Inst.SwitchBr, inst_data.payload_index);1820 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
1769 try self.writeInstRef(stream, extra.data.operand);1836 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;
1771 {1853 {
1772 var scalar_i: usize = 0;1854 var scalar_i: usize = 0;
1773 while (scalar_i < extra.data.cases_len) : (scalar_i += 1) {1855 while (scalar_i < extra.data.cases_len) : (scalar_i += 1) {
...@@ -1792,11 +1874,46 @@ const Writer = struct {...@@ -1792,11 +1874,46 @@ const Writer = struct {
1792 try self.writeSrc(stream, inst_data.src());1874 try self.writeSrc(stream, inst_data.src());
1793 }1875 }
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 {
1796 const inst_data = self.code.instructions.items(.data)[inst].pl_node;1883 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
1797 const extra = self.code.extraData(Inst.SwitchBrMulti, inst_data.payload_index);1884 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
1798 try self.writeInstRef(stream, extra.data.operand);1900 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;
1800 {1917 {
1801 var scalar_i: usize = 0;1918 var scalar_i: usize = 0;
1802 while (scalar_i < extra.data.scalar_cases_len) : (scalar_i += 1) {1919 while (scalar_i < extra.data.scalar_cases_len) : (scalar_i += 1) {
...@@ -2015,6 +2132,12 @@ const Writer = struct {...@@ -2015,6 +2132,12 @@ const Writer = struct {
2015 try stream.print("\"{}\")", .{std.zig.fmtEscapes(str)});2132 try stream.print("\"{}\")", .{std.zig.fmtEscapes(str)});
2016 }2133 }
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
2018 fn writeInstRef(self: *Writer, stream: anytype, ref: Inst.Ref) !void {2141 fn writeInstRef(self: *Writer, stream: anytype, ref: Inst.Ref) !void {
2019 var i: usize = @enumToInt(ref);2142 var i: usize = @enumToInt(ref);
20202143