authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-03-29 21:59:08-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-03-29 21:59:08-07:00
log195ddab2be938c1201767909d39106cdf99fd07e
treea24f32c4829370474b2319c9bbdd3478974d1bad
parent623d5f442c832ec0ea2a07aba73b8e2eae57191c

Sema: implement switch expressions

The logic for putting ranges into the else prong is moved from AstGen to Sema. However, logic to emit multi-items the same as single-items cannot be done until TZIR supports mapping multiple items to the same block of code. This will be simple to represent when we do the upcoming TZIR memory layout changes. Not yet implemented in this commit is the validation of duplicate values. The trick is going to be emitting error messages with accurate source locations, without adding extra source nodes to the ZIR switch instruction. This will be done by computing the respective AST node based on the switch node (which we do have available), only when a compile error occurs and we need to know the source location to attach the message to.

7 files changed, 827 insertions(+), 535 deletions(-)

BRANCH_TODO+91
...@@ -35,3 +35,94 @@ Performance optimizations to look into:...@@ -35,3 +35,94 @@ Performance optimizations to look into:
35 var decl and assignment instructions, etc.35 var decl and assignment instructions, etc.
36 - make it set sema.src where appropriate36 - make it set sema.src where appropriate
37 * look into not emitting redundant dbg stmts to TZIR37 * look into not emitting redundant dbg stmts to TZIR
38 * make decl references in ZIR be u32 indexes to the Decl dependencies array hash map
39 instead of duplicating *Decl entries in zir.Code.
40
41 if (maybe_src) |previous_src| {
42 return sema.mod.fail(&block.base, item.src, "duplicate switch value", .{});
43 // TODO notes "previous value is here" previous_src
44 }
45
46 const item = try sema.resolveInst(item_ref);
47 const value = try sema.resolveConstValue(block, item.src, item);
48 const maybe_src = try range_set.add(value, value, item.src);
49 try sema.validateSwitchDupeValue(parent_block, maybe_src, item.src);
50
51
52 const first = try sema.resolveInst(item_first);
53 const last = try sema.resolveInst(item_last);
54 const maybe_src = try range_set.add(
55 try sema.resolveConstValue(block, range_first_src, first_casted),
56 try sema.resolveConstValue(block, range_last_src, last_casted),
57 item.src,
58 );
59 };
60
61
62 const item = try sema.resolveInst(item_ref);
63 if ((try sema.resolveConstValue(block, item.src, item)).toBool()) {
64 true_count += 1;
65 } else {
66 false_count += 1;
67 }
68 if (true_count + false_count > 2) {
69 return sema.mod.fail(&block.base, item.src, "duplicate switch value", .{});
70 }
71
72
73
74 for (inst.positionals.items) |item| {
75 const resolved = try sema.resolveInst(item);
76 const casted = try sema.coerce(block, operand.ty, resolved);
77 const val = try sema.resolveConstValue(block, item_src, casted);
78
79 if (try seen_values.fetchPut(val, item.src)) |prev| {
80 return sema.mod.fail(&block.base, item.src, "duplicate switch value", .{});
81 // TODO notes "previous value here" prev.value
82 }
83 }
84
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+18-303
...@@ -1258,17 +1258,17 @@ fn blockExprStmts(...@@ -1258,17 +1258,17 @@ fn blockExprStmts(
1258 .condbr,1258 .condbr,
1259 .condbr_inline,1259 .condbr_inline,
1260 .switch_br,1260 .switch_br,
1261 .switch_br_range,1261 .switch_br_multi,
1262 .switch_br_else,1262 .switch_br_else,
1263 .switch_br_else_range,1263 .switch_br_else_multi,
1264 .switch_br_underscore,1264 .switch_br_under,
1265 .switch_br_underscore_range,1265 .switch_br_under_multi,
1266 .switch_br_ref,1266 .switch_br_ref,
1267 .switch_br_ref_range,1267 .switch_br_ref_multi,
1268 .switch_br_ref_else,1268 .switch_br_ref_else,
1269 .switch_br_ref_else_range,1269 .switch_br_ref_else_multi,
1270 .switch_br_ref_underscore,1270 .switch_br_ref_under,
1271 .switch_br_ref_underscore_range,1271 .switch_br_ref_under_multi,
1272 .compile_error,1272 .compile_error,
1273 .ret_node,1273 .ret_node,
1274 .ret_tok,1274 .ret_tok,
...@@ -2550,35 +2550,12 @@ fn switchExpr(...@@ -2550,35 +2550,12 @@ fn switchExpr(
2550) InnerError!zir.Inst.Ref {2550) InnerError!zir.Inst.Ref {
2551 const tree = parent_gz.tree();2551 const tree = parent_gz.tree();
2552 const node_datas = tree.nodes.items(.data);2552 const node_datas = tree.nodes.items(.data);
2553 const main_tokens = tree.nodes.items(.main_token);
2554 const token_tags = tree.tokens.items(.tag);
2555 const node_tags = tree.nodes.items(.tag);2553 const node_tags = tree.nodes.items(.tag);
25562554 const token_tags = tree.tokens.items(.tag);
2557 if (true) @panic("TODO rework for zir-memory-layout branch");2555 const operand_node = node_datas[switch_node].lhs;
2558
2559 const switch_token = main_tokens[switch_node];
2560 const target_node = node_datas[switch_node].lhs;
2561 const extra = tree.extraData(node_datas[switch_node].rhs, ast.Node.SubRange);2556 const extra = tree.extraData(node_datas[switch_node].rhs, ast.Node.SubRange);
2562 const case_nodes = tree.extra_data[extra.start..extra.end];2557 const case_nodes = tree.extra_data[extra.start..extra.end];
25632558
2564 var block_scope: GenZir = .{
2565 .parent = scope,
2566 .decl = scope.ownerDecl().?,
2567 .arena = parent_gz.astgen.arena,
2568 .force_comptime = parent_gz.force_comptime,
2569 .instructions = .{},
2570 };
2571 block_scope.setBreakResultLoc(rl);
2572 defer block_scope.instructions.deinit(mod.gpa);
2573
2574 var items = std.ArrayList(zir.Inst.Ref).init(mod.gpa);
2575 defer items.deinit();
2576
2577 // First we gather all the switch items and check else/'_' prongs.
2578 var else_src: ?usize = null;
2579 var underscore_src: ?usize = null;
2580 var first_range: ?*zir.Inst = null;
2581 var simple_case_count: usize = 0;
2582 var any_payload_is_ref = false;2559 var any_payload_is_ref = false;
2583 for (case_nodes) |case_node| {2560 for (case_nodes) |case_node| {
2584 const case = switch (node_tags[case_node]) {2561 const case = switch (node_tags[case_node]) {
...@@ -2591,284 +2568,22 @@ fn switchExpr(...@@ -2591,284 +2568,22 @@ fn switchExpr(
2591 any_payload_is_ref = true;2568 any_payload_is_ref = true;
2592 }2569 }
2593 }2570 }
2594 // Check for else/_ prong, those are handled last.
2595 if (case.ast.values.len == 0) {
2596 const case_src = token_starts[case.ast.arrow_token - 1];
2597 if (else_src) |src| {
2598 const msg = msg: {
2599 const msg = try mod.errMsg(
2600 scope,
2601 case_src,
2602 "multiple else prongs in switch expression",
2603 .{},
2604 );
2605 errdefer msg.destroy(mod.gpa);
2606 try mod.errNote(scope, src, msg, "previous else prong is here", .{});
2607 break :msg msg;
2608 };
2609 return mod.failWithOwnedErrorMsg(scope, msg);
2610 }
2611 else_src = case_src;
2612 continue;
2613 } else if (case.ast.values.len == 1 and
2614 node_tags[case.ast.values[0]] == .identifier and
2615 mem.eql(u8, tree.tokenSlice(main_tokens[case.ast.values[0]]), "_"))
2616 {
2617 const case_src = token_starts[case.ast.arrow_token - 1];
2618 if (underscore_src) |src| {
2619 const msg = msg: {
2620 const msg = try mod.errMsg(
2621 scope,
2622 case_src,
2623 "multiple '_' prongs in switch expression",
2624 .{},
2625 );
2626 errdefer msg.destroy(mod.gpa);
2627 try mod.errNote(scope, src, msg, "previous '_' prong is here", .{});
2628 break :msg msg;
2629 };
2630 return mod.failWithOwnedErrorMsg(scope, msg);
2631 }
2632 underscore_src = case_src;
2633 continue;
2634 }
2635
2636 if (else_src) |some_else| {
2637 if (underscore_src) |some_underscore| {
2638 const msg = msg: {
2639 const msg = try mod.errMsg(
2640 scope,
2641 parent_gz.nodeSrcLoc(switch_node),
2642 "else and '_' prong in switch expression",
2643 .{},
2644 );
2645 errdefer msg.destroy(mod.gpa);
2646 try mod.errNote(scope, some_else, msg, "else prong is here", .{});
2647 try mod.errNote(scope, some_underscore, msg, "'_' prong is here", .{});
2648 break :msg msg;
2649 };
2650 return mod.failWithOwnedErrorMsg(scope, msg);
2651 }
2652 }
2653
2654 if (case.ast.values.len == 1 and
2655 getRangeNode(node_tags, node_datas, case.ast.values[0]) == null)
2656 {
2657 simple_case_count += 1;
2658 }
2659
2660 // Generate all the switch items as comptime expressions.
2661 for (case.ast.values) |item| {
2662 if (getRangeNode(node_tags, node_datas, item)) |range| {
2663 const start = try comptimeExpr(&block_scope, &block_scope.base, .none, node_datas[range].lhs);
2664 const end = try comptimeExpr(&block_scope, &block_scope.base, .none, node_datas[range].rhs);
2665 const range_src = token_starts[main_tokens[range]];
2666 const range_inst = try addZIRBinOp(mod, &block_scope.base, range_src, .switch_range, start, end);
2667 try items.append(range_inst);
2668 } else {
2669 const item_inst = try comptimeExpr(&block_scope, &block_scope.base, .none, item);
2670 try items.append(item_inst);
2671 }
2672 }
2673 }2571 }
26742572
2675 var special_prong: zir.Inst.SwitchBr.SpecialProng = .none;
2676 if (else_src != null) special_prong = .@"else";
2677 if (underscore_src != null) special_prong = .underscore;
2678 var cases = try block_scope.arena.alloc(zir.Inst.SwitchBr.Case, simple_case_count);
2679
2680 const rl_and_tag: struct { rl: ResultLoc, tag: zir.Inst.Tag } = if (any_payload_is_ref) .{2573 const rl_and_tag: struct { rl: ResultLoc, tag: zir.Inst.Tag } = if (any_payload_is_ref) .{
2681 .rl = .ref,2574 .rl = .ref,
2682 .tag = .switchbr_ref,2575 .tag = .switch_br_ref,
2683 } else .{2576 } else .{
2684 .rl = .none,2577 .rl = .none,
2685 .tag = .switchbr,2578 .tag = .switch_br,
2686 };
2687 const target = try expr(&block_scope, &block_scope.base, rl_and_tag.rl, target_node);
2688 const switch_inst = try addZirInstT(mod, &block_scope.base, switch_src, zir.Inst.SwitchBr, rl_and_tag.tag, .{
2689 .target = target,
2690 .cases = cases,
2691 .items = try block_scope.arena.dupe(zir.Inst.Ref, items.items),
2692 .else_body = undefined, // populated below
2693 .range = first_range,
2694 .special_prong = special_prong,
2695 });
2696 const block = try addZIRInstBlock(mod, scope, switch_src, .block, .{
2697 .instructions = try block_scope.arena.dupe(zir.Inst.Ref, block_scope.instructions.items),
2698 });
2699
2700 var case_scope: GenZir = .{
2701 .parent = scope,
2702 .decl = block_scope.decl,
2703 .arena = block_scope.arena,
2704 .force_comptime = block_scope.force_comptime,
2705 .instructions = .{},
2706 };2579 };
2707 defer case_scope.instructions.deinit(mod.gpa);2580 const operand = try expr(parent_gz, scope, rl_and_tag.rl, operand_node);
27082581
2709 var else_scope: GenZir = .{2582 const result = try parent_gz.addPlNode(.switch_br, switch_node, zir.Inst.SwitchBr{
2710 .parent = scope,2583 .operand = operand,
2711 .decl = case_scope.decl,2584 .cases_len = 0,
2712 .arena = case_scope.arena,2585 });
2713 .force_comptime = case_scope.force_comptime,2586 return rvalue(parent_gz, scope, rl, result, switch_node);
2714 .instructions = .{},
2715 };
2716 defer else_scope.instructions.deinit(mod.gpa);
2717
2718 // Now generate all but the special cases.
2719 var special_case: ?ast.full.SwitchCase = null;
2720 var items_index: usize = 0;
2721 var case_index: usize = 0;
2722 for (case_nodes) |case_node| {
2723 const case = switch (node_tags[case_node]) {
2724 .switch_case_one => tree.switchCaseOne(case_node),
2725 .switch_case => tree.switchCase(case_node),
2726 else => unreachable,
2727 };
2728 const case_src = token_starts[main_tokens[case_node]];
2729 case_scope.instructions.shrinkRetainingCapacity(0);
2730
2731 // Check for else/_ prong, those are handled last.
2732 if (case.ast.values.len == 0) {
2733 special_case = case;
2734 continue;
2735 } else if (case.ast.values.len == 1 and
2736 node_tags[case.ast.values[0]] == .identifier and
2737 mem.eql(u8, tree.tokenSlice(main_tokens[case.ast.values[0]]), "_"))
2738 {
2739 special_case = case;
2740 continue;
2741 }
2742
2743 // If this is a simple one item prong then it is handled by the switchbr.
2744 if (case.ast.values.len == 1 and
2745 getRangeNode(node_tags, node_datas, case.ast.values[0]) == null)
2746 {
2747 const item = items.items[items_index];
2748 items_index += 1;
2749 try switchCaseExpr(mod, &case_scope.base, block_scope.break_result_loc, block, case, target);
2750
2751 cases[case_index] = .{
2752 .item = item,
2753 .body = .{ .instructions = try parent_gz.astgen.arena.dupe(zir.Inst.Ref, case_scope.instructions.items) },
2754 };
2755 case_index += 1;
2756 continue;
2757 }
2758
2759 // Check if the target matches any of the items.
2760 // 1, 2, 3..6 will result in
2761 // target == 1 or target == 2 or (target >= 3 and target <= 6)
2762 // TODO handle multiple items as switch prongs rather than along with ranges.
2763 var any_ok: ?*zir.Inst = null;
2764 for (case.ast.values) |item| {
2765 if (getRangeNode(node_tags, node_datas, item)) |range| {
2766 const range_src = token_starts[main_tokens[range]];
2767 const range_inst = items.items[items_index].castTag(.switch_range).?;
2768 items_index += 1;
2769
2770 // target >= start and target <= end
2771 const range_start_ok = try addZIRBinOp(mod, &else_scope.base, range_src, .cmp_gte, target, range_inst.positionals.lhs);
2772 const range_end_ok = try addZIRBinOp(mod, &else_scope.base, range_src, .cmp_lte, target, range_inst.positionals.rhs);
2773 const range_ok = try addZIRBinOp(mod, &else_scope.base, range_src, .bool_and, range_start_ok, range_end_ok);
2774
2775 if (any_ok) |some| {
2776 any_ok = try addZIRBinOp(mod, &else_scope.base, range_src, .bool_or, some, range_ok);
2777 } else {
2778 any_ok = range_ok;
2779 }
2780 continue;
2781 }
2782
2783 const item_inst = items.items[items_index];
2784 items_index += 1;
2785 const cpm_ok = try addZIRBinOp(mod, &else_scope.base, item_inst.src, .cmp_eq, target, item_inst);
2786
2787 if (any_ok) |some| {
2788 any_ok = try addZIRBinOp(mod, &else_scope.base, item_inst.src, .bool_or, some, cpm_ok);
2789 } else {
2790 any_ok = cpm_ok;
2791 }
2792 }
2793
2794 const condbr = try addZIRInstSpecial(mod, &case_scope.base, case_src, zir.Inst.CondBr, .{
2795 .condition = any_ok.?,
2796 .then_body = undefined, // populated below
2797 .else_body = undefined, // populated below
2798 }, .{});
2799 const cond_block = try addZIRInstBlock(mod, &else_scope.base, case_src, .block, .{
2800 .instructions = try parent_gz.astgen.arena.dupe(zir.Inst.Ref, case_scope.instructions.items),
2801 });
2802
2803 // reset cond_scope for then_body
2804 case_scope.instructions.items.len = 0;
2805 try switchCaseExpr(mod, &case_scope.base, block_scope.break_result_loc, block, case, target);
2806 condbr.positionals.then_body = .{
2807 .instructions = try parent_gz.astgen.arena.dupe(zir.Inst.Ref, case_scope.instructions.items),
2808 };
2809
2810 // reset cond_scope for else_body
2811 case_scope.instructions.items.len = 0;
2812 _ = try addZIRInst(mod, &case_scope.base, case_src, zir.Inst.BreakVoid, .{
2813 .block = cond_block,
2814 }, .{});
2815 condbr.positionals.else_body = .{
2816 .instructions = try parent_gz.astgen.arena.dupe(zir.Inst.Ref, case_scope.instructions.items),
2817 };
2818 }
2819
2820 // Finally generate else block or a break.
2821 if (special_case) |case| {
2822 try switchCaseExpr(mod, &else_scope.base, block_scope.break_result_loc, block, case, target);
2823 } else {
2824 // Not handling all possible cases is a compile error.
2825 _ = try addZIRNoOp(mod, &else_scope.base, switch_src, .unreachable_unsafe);
2826 }
2827 switch_inst.positionals.else_body = .{
2828 .instructions = try block_scope.arena.dupe(zir.Inst.Ref, else_scope.instructions.items),
2829 };
2830
2831 return &block.base;
2832}
2833
2834fn switchCaseExpr(
2835 gz: *GenZir,
2836 scope: *Scope,
2837 rl: ResultLoc,
2838 block: *zir.Inst.Block,
2839 case: ast.full.SwitchCase,
2840 target: zir.Inst.Ref,
2841) !void {
2842 const tree = gz.tree();
2843 const node_datas = tree.nodes.items(.data);
2844 const main_tokens = tree.nodes.items(.main_token);
2845 const token_tags = tree.tokens.items(.tag);
2846
2847 const case_src = token_starts[case.ast.arrow_token];
2848 const sub_scope = blk: {
2849 const payload_token = case.payload_token orelse break :blk scope;
2850 const ident = if (token_tags[payload_token] == .asterisk)
2851 payload_token + 1
2852 else
2853 payload_token;
2854 const is_ptr = ident != payload_token;
2855 const value_name = tree.tokenSlice(ident);
2856 if (mem.eql(u8, value_name, "_")) {
2857 if (is_ptr) {
2858 return mod.failTok(scope, payload_token, "pointer modifier invalid on discard", .{});
2859 }
2860 break :blk scope;
2861 }
2862 return mod.failTok(scope, ident, "TODO implement switch value payload", .{});
2863 };
2864
2865 const case_body = try expr(gz, sub_scope, rl, case.ast.target_expr);
2866 if (!case_body.tag.isNoReturn()) {
2867 _ = try addZIRInst(mod, sub_scope, case_src, zir.Inst.Break, .{
2868 .block = block,
2869 .operand = case_body,
2870 }, .{});
2871 }
2872}2587}
28732588
2874fn ret(gz: *GenZir, scope: *Scope, node: ast.Node.Index) InnerError!zir.Inst.Ref {2589fn ret(gz: *GenZir, scope: *Scope, node: ast.Node.Index) InnerError!zir.Inst.Ref {
src/Module.zig+21-2
...@@ -888,7 +888,7 @@ pub const Scope = struct {...@@ -888,7 +888,7 @@ pub const Scope = struct {
888 pub fn addSwitchBr(888 pub fn addSwitchBr(
889 block: *Scope.Block,889 block: *Scope.Block,
890 src: LazySrcLoc,890 src: LazySrcLoc,
891 target: *ir.Inst,891 operand: *ir.Inst,
892 cases: []ir.Inst.SwitchBr.Case,892 cases: []ir.Inst.SwitchBr.Case,
893 else_body: ir.Body,893 else_body: ir.Body,
894 ) !*ir.Inst {894 ) !*ir.Inst {
...@@ -899,7 +899,7 @@ pub const Scope = struct {...@@ -899,7 +899,7 @@ pub const Scope = struct {
899 .ty = Type.initTag(.noreturn),899 .ty = Type.initTag(.noreturn),
900 .src = src,900 .src = src,
901 },901 },
902 .target = target,902 .target = operand,
903 .cases = cases,903 .cases = cases,
904 .else_body = else_body,904 .else_body = else_body,
905 };905 };
...@@ -1533,6 +1533,8 @@ pub const SrcLoc = struct {...@@ -1533,6 +1533,8 @@ pub const SrcLoc = struct {
1533 .node_offset_bin_lhs,1533 .node_offset_bin_lhs,
1534 .node_offset_bin_rhs,1534 .node_offset_bin_rhs,
1535 .node_offset_switch_operand,1535 .node_offset_switch_operand,
1536 .node_offset_switch_special_prong,
1537 .node_offset_switch_range,
1536 => src_loc.container.decl.container.file_scope,1538 => src_loc.container.decl.container.file_scope,
1537 };1539 };
1538 }1540 }
...@@ -1665,6 +1667,8 @@ pub const SrcLoc = struct {...@@ -1665,6 +1667,8 @@ pub const SrcLoc = struct {
1665 return token_starts[tok_index];1667 return token_starts[tok_index];
1666 },1668 },
1667 .node_offset_switch_operand => @panic("TODO"),1669 .node_offset_switch_operand => @panic("TODO"),
1670 .node_offset_switch_special_prong => @panic("TODO"),
1671 .node_offset_switch_range => @panic("TODO"),
1668 }1672 }
1669 }1673 }
1670};1674};
...@@ -1802,6 +1806,17 @@ pub const LazySrcLoc = union(enum) {...@@ -1802,6 +1806,17 @@ pub const LazySrcLoc = union(enum) {
1802 /// which points to a switch expression AST node. Next, nagivate to the operand.1806 /// which points to a switch expression AST node. Next, nagivate to the operand.
1803 /// The Decl is determined contextually.1807 /// The Decl is determined contextually.
1804 node_offset_switch_operand: i32,1808 node_offset_switch_operand: i32,
1809 /// The source location points to the else/`_` prong of a switch expression, found
1810 /// by taking this AST node index offset from the containing Decl AST node,
1811 /// which points to a switch expression AST node. Next, nagivate to the else/`_` prong.
1812 /// The Decl is determined contextually.
1813 node_offset_switch_special_prong: i32,
1814 /// The source location points to all the ranges of a switch expression, found
1815 /// by taking this AST node index offset from the containing Decl AST node,
1816 /// which points to a switch expression AST node. Next, nagivate to any of the
1817 /// range nodes. The error applies to all of them.
1818 /// The Decl is determined contextually.
1819 node_offset_switch_range: i32,
18051820
1806 /// Upgrade to a `SrcLoc` based on the `Decl` or file in the provided scope.1821 /// Upgrade to a `SrcLoc` based on the `Decl` or file in the provided scope.
1807 pub fn toSrcLoc(lazy: LazySrcLoc, scope: *Scope) SrcLoc {1822 pub fn toSrcLoc(lazy: LazySrcLoc, scope: *Scope) SrcLoc {
...@@ -1836,6 +1851,8 @@ pub const LazySrcLoc = union(enum) {...@@ -1836,6 +1851,8 @@ pub const LazySrcLoc = union(enum) {
1836 .node_offset_bin_lhs,1851 .node_offset_bin_lhs,
1837 .node_offset_bin_rhs,1852 .node_offset_bin_rhs,
1838 .node_offset_switch_operand,1853 .node_offset_switch_operand,
1854 .node_offset_switch_special_prong,
1855 .node_offset_switch_range,
1839 => .{1856 => .{
1840 .container = .{ .decl = scope.srcDecl().? },1857 .container = .{ .decl = scope.srcDecl().? },
1841 .lazy = lazy,1858 .lazy = lazy,
...@@ -1876,6 +1893,8 @@ pub const LazySrcLoc = union(enum) {...@@ -1876,6 +1893,8 @@ pub const LazySrcLoc = union(enum) {
1876 .node_offset_bin_lhs,1893 .node_offset_bin_lhs,
1877 .node_offset_bin_rhs,1894 .node_offset_bin_rhs,
1878 .node_offset_switch_operand,1895 .node_offset_switch_operand,
1896 .node_offset_switch_special_prong,
1897 .node_offset_switch_range,
1879 => .{1898 => .{
1880 .container = .{ .decl = decl },1899 .container = .{ .decl = decl },
1881 .lazy = lazy,1900 .lazy = lazy,
src/RangeSet.zig+3
...@@ -44,6 +44,9 @@ fn lessThan(_: void, a: Range, b: Range) bool {...@@ -44,6 +44,9 @@ fn lessThan(_: void, a: Range, b: Range) bool {
44}44}
4545
46pub fn spans(self: *RangeSet, start: Value, end: Value) !bool {46pub fn spans(self: *RangeSet, start: Value, end: Value) !bool {
47 if (self.ranges.items.len == 0)
48 return false;
49
47 std.sort.sort(Range, self.ranges.items, {}, lessThan);50 std.sort.sort(Range, self.ranges.items, {}, lessThan);
4851
49 if (!self.ranges.items[0].start.eql(start) or52 if (!self.ranges.items[0].start.eql(start) or
src/Sema.zig+561-182
...@@ -59,6 +59,9 @@ const Scope = Module.Scope;...@@ -59,6 +59,9 @@ const Scope = Module.Scope;
59const InnerError = Module.InnerError;59const InnerError = Module.InnerError;
60const Decl = Module.Decl;60const Decl = Module.Decl;
61const LazySrcLoc = Module.LazySrcLoc;61const LazySrcLoc = Module.LazySrcLoc;
62const RangeSet = @import("RangeSet.zig");
63
64const ValueSrcMap = std.HashMap(Value, LazySrcLoc, Value.hash, Value.eql, std.hash_map.DefaultMaxLoadPercentage);
6265
63pub fn root(sema: *Sema, root_block: *Scope.Block) !zir.Inst.Index {66pub fn root(sema: *Sema, root_block: *Scope.Block) !zir.Inst.Index {
64 const inst_data = sema.code.instructions.items(.data)[0].pl_node;67 const inst_data = sema.code.instructions.items(.data)[0].pl_node;
...@@ -242,18 +245,18 @@ pub fn analyzeBody(...@@ -242,18 +245,18 @@ pub fn analyzeBody(
242 .ret_tok => return sema.zirRetTok(block, inst, false),245 .ret_tok => return sema.zirRetTok(block, inst, false),
243 .@"unreachable" => return sema.zirUnreachable(block, inst),246 .@"unreachable" => return sema.zirUnreachable(block, inst),
244 .repeat => return sema.zirRepeat(block, inst),247 .repeat => return sema.zirRepeat(block, inst),
245 .switch_br => return sema.zirSwitchBr(block, inst, false, .full),248 .switch_br => return sema.zirSwitchBr(block, inst, false, .none),
246 .switch_br_range => return sema.zirSwitchBrRange(block, inst, false, .full),249 .switch_br_multi => return sema.zirSwitchBrMulti(block, inst, false, .none),
247 .switch_br_else => return sema.zirSwitchBr(block, inst, false, .@"else"),250 .switch_br_else => return sema.zirSwitchBr(block, inst, false, .@"else"),
248 .switch_br_else_range => return sema.zirSwitchBrRange(block, inst, false, .@"else"),251 .switch_br_else_multi => return sema.zirSwitchBrMulti(block, inst, false, .@"else"),
249 .switch_br_underscore => return sema.zirSwitchBr(block, inst, false, .under),252 .switch_br_under => return sema.zirSwitchBr(block, inst, false, .under),
250 .switch_br_underscore_range => return sema.zirSwitchBrRange(block, inst, false, .under),253 .switch_br_under_multi => return sema.zirSwitchBrMulti(block, inst, false, .under),
251 .switch_br_ref => return sema.zirSwitchBr(block, inst, true, .full),254 .switch_br_ref => return sema.zirSwitchBr(block, inst, true, .none),
252 .switch_br_ref_range => return sema.zirSwitchBrRange(block, inst, true, .full),255 .switch_br_ref_multi => return sema.zirSwitchBrMulti(block, inst, true, .none),
253 .switch_br_ref_else => return sema.zirSwitchBr(block, inst, true, .@"else"),256 .switch_br_ref_else => return sema.zirSwitchBr(block, inst, true, .@"else"),
254 .switch_br_ref_else_range => return sema.zirSwitchBrRange(block, inst, true, .@"else"),257 .switch_br_ref_else_multi => return sema.zirSwitchBrMulti(block, inst, true, .@"else"),
255 .switch_br_ref_underscore => return sema.zirSwitchBr(block, inst, true, .under),258 .switch_br_ref_under => return sema.zirSwitchBr(block, inst, true, .under),
256 .switch_br_ref_underscore_range => return sema.zirSwitchBrRange(block, inst, true, .under),259 .switch_br_ref_under_multi => return sema.zirSwitchBrMulti(block, inst, true, .under),
257260
258 // Instructions that we know can *never* be noreturn based solely on261 // Instructions that we know can *never* be noreturn based solely on
259 // their tag. We avoid needlessly checking if they are noreturn and262 // their tag. We avoid needlessly checking if they are noreturn and
...@@ -2205,14 +2208,14 @@ fn zirSliceSentinel(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) Inne...@@ -2205,14 +2208,14 @@ fn zirSliceSentinel(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) Inne
2205 return sema.analyzeSlice(block, src, array_ptr, start, end, sentinel, sentinel_src);2208 return sema.analyzeSlice(block, src, array_ptr, start, end, sentinel, sentinel_src);
2206}2209}
22072210
2208const ElseProng = enum { full, @"else", under };2211const SpecialProng = enum { none, @"else", under };
22092212
2210fn zirSwitchBr(2213fn zirSwitchBr(
2211 sema: *Sema,2214 sema: *Sema,
2212 block: *Scope.Block,2215 block: *Scope.Block,
2213 inst: zir.Inst.Index,2216 inst: zir.Inst.Index,
2214 is_ref: bool,2217 is_ref: bool,
2215 else_prong: ElseProng,2218 special_prong: SpecialProng,
2216) InnerError!zir.Inst.Index {2219) InnerError!zir.Inst.Index {
2217 const tracy = trace(@src());2220 const tracy = trace(@src());
2218 defer tracy.end();2221 defer tracy.end();
...@@ -2228,15 +2231,23 @@ fn zirSwitchBr(...@@ -2228,15 +2231,23 @@ fn zirSwitchBr(
2228 else2231 else
2229 operand_ptr;2232 operand_ptr;
22302233
2231 return sema.analyzeSwitch(block, operand, extra.end, else_prong, extra.data.cases_len, 0, 0);2234 return sema.analyzeSwitch(
2235 block,
2236 operand,
2237 extra.end,
2238 special_prong,
2239 extra.data.cases_len,
2240 0,
2241 inst_data.src_node,
2242 );
2232}2243}
22332244
2234fn zirSwitchBrRange(2245fn zirSwitchBrMulti(
2235 sema: *Sema,2246 sema: *Sema,
2236 block: *Scope.Block,2247 block: *Scope.Block,
2237 inst: zir.Inst.Index,2248 inst: zir.Inst.Index,
2238 is_ref: bool,2249 is_ref: bool,
2239 else_prong: ElseProng,2250 special_prong: SpecialProng,
2240) InnerError!zir.Inst.Index {2251) InnerError!zir.Inst.Index {
2241 const tracy = trace(@src());2252 const tracy = trace(@src());
2242 defer tracy.end();2253 defer tracy.end();
...@@ -2244,7 +2255,7 @@ fn zirSwitchBrRange(...@@ -2244,7 +2255,7 @@ fn zirSwitchBrRange(
2244 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;2255 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
2245 const src = inst_data.src();2256 const src = inst_data.src();
2246 const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = inst_data.src_node };2257 const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = inst_data.src_node };
2247 const extra = sema.code.extraData(zir.Inst.SwitchBrRange, inst_data.payload_index);2258 const extra = sema.code.extraData(zir.Inst.SwitchBrMulti, inst_data.payload_index);
22482259
2249 const operand_ptr = try sema.resolveInst(extra.data.operand);2260 const operand_ptr = try sema.resolveInst(extra.data.operand);
2250 const operand = if (is_ref)2261 const operand = if (is_ref)
...@@ -2256,196 +2267,285 @@ fn zirSwitchBrRange(...@@ -2256,196 +2267,285 @@ fn zirSwitchBrRange(
2256 block,2267 block,
2257 operand,2268 operand,
2258 extra.end,2269 extra.end,
2259 else_prong,2270 special_prong,
2260 extra.data.scalar_cases_len,2271 extra.data.scalar_cases_len,
2261 extra.data.multi_cases_len,2272 extra.data.multi_cases_len,
2262 extra.data.range_cases_len,2273 inst_data.src_node,
2263 );2274 );
2264}2275}
22652276
2266fn analyzeSwitch(2277fn analyzeSwitch(
2267 sema: *Sema,2278 sema: *Sema,
2268 parent_block: *Scope.Block,2279 block: *Scope.Block,
2269 operand: *Inst,2280 operand: *Inst,
2270 extra_end: usize,2281 extra_end: usize,
2271 else_prong: ElseProng,2282 special_prong: SpecialProng,
2272 scalar_cases_len: usize,2283 scalar_cases_len: usize,
2273 multi_cases_len: usize,2284 multi_cases_len: usize,
2274 range_cases_len: usize,2285 src_node_offset: i32,
2275) InnerError!zir.Inst.Index {2286) InnerError!zir.Inst.Index {
2276 if (true) @panic("TODO rework for zir-memory-layout branch");2287 const special: struct { body: []const zir.Inst.Index, end: usize } = switch (special_prong) {
22772288 .none => .{ .body = &.{}, .end = extra_end },
2278 try sema.validateSwitch(parent_block, operand, inst);2289 .under, .@"else" => blk: {
22792290 const body_len = sema.code.extra[extra_end];
2280 if (try sema.resolveDefinedValue(parent_block, inst.base.src, operand)) |target_val| {2291 const extra_body_start = extra_end + 1;
2281 for (inst.positionals.cases) |case| {2292 break :blk .{
2282 const resolved = try sema.resolveInst(case.item);2293 .body = sema.code.extra[extra_body_start..][0..body_len],
2283 const casted = try sema.coerce(block, operand.ty, resolved, resolved_src);2294 .end = extra_body_start + body_len,
2284 const item = try sema.resolveConstValue(parent_block, case_src, casted);2295 };
22852296 },
2286 if (target_val.eql(item)) {
2287 _ = try sema.analyzeBody(parent_block, case.body);
2288 return always_noreturn;
2289 }
2290 }
2291 _ = try sema.analyzeBody(parent_block, inst.positionals.else_body);
2292 return always_noreturn;
2293 }
2294
2295 if (inst.positionals.cases.len == 0) {
2296 // no cases just analyze else_branch
2297 _ = try sema.analyzeBody(parent_block, inst.positionals.else_body);
2298 return always_noreturn;
2299 }
2300
2301 try sema.requireRuntimeBlock(parent_block, inst.base.src);
2302 const cases = try sema.arena.alloc(Inst.SwitchBr.Case, inst.positionals.cases.len);
2303
2304 var case_block: Scope.Block = .{
2305 .parent = parent_block,
2306 .sema = sema,
2307 .src_decl = parent_block.src_decl,
2308 .instructions = .{},
2309 .inlining = parent_block.inlining,
2310 .is_comptime = parent_block.is_comptime,
2311 };2297 };
2312 defer case_block.instructions.deinit(sema.gpa);
23132298
2314 for (inst.positionals.cases) |case, i| {2299 const src: LazySrcLoc = .{ .node_offset = src_node_offset };
2315 // Reset without freeing.2300 const special_prong_src: LazySrcLoc = .{ .node_offset_switch_special_prong = src_node_offset };
2316 case_block.instructions.items.len = 0;2301 const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = src_node_offset };
23172302
2318 const resolved = try sema.resolveInst(case.item);2303 // Validate usage of '_' prongs.
2319 const casted = try sema.coerce(block, operand.ty, resolved, resolved_src);2304 if (special_prong == .under and !operand.ty.isExhaustiveEnum()) {
2320 const item = try sema.resolveConstValue(parent_block, case_src, casted);2305 const msg = msg: {
23212306 const msg = try sema.mod.errMsg(
2322 _ = try sema.analyzeBody(&case_block, case.body);2307 &block.base,
23232308 src,
2324 cases[i] = .{2309 "'_' prong only allowed when switching on non-exhaustive enums",
2325 .item = item,2310 .{},
2326 .body = .{ .instructions = try sema.arena.dupe(*Inst, case_block.instructions.items) },2311 );
2312 errdefer msg.destroy(sema.gpa);
2313 try sema.mod.errNote(
2314 &block.base,
2315 special_prong_src,
2316 msg,
2317 "'_' prong here",
2318 .{},
2319 );
2320 break :msg msg;
2327 };2321 };
2322 return sema.mod.failWithOwnedErrorMsg(&block.base, msg);
2328 }2323 }
23292324
2330 case_block.instructions.items.len = 0;2325 // Validate for duplicate items, missing else prong, and invalid range.
2331 _ = try sema.analyzeBody(&case_block, inst.positionals.else_body);
2332
2333 const else_body: ir.Body = .{
2334 .instructions = try sema.arena.dupe(*Inst, case_block.instructions.items),
2335 };
2336
2337 return mod.addSwitchBr(parent_block, inst.base.src, operand, cases, else_body);
2338}
2339
2340fn validateSwitch(sema: *Sema, block: *Scope.Block, operand: *Inst, inst: zir.Inst.Index) InnerError!void {
2341 // validate usage of '_' prongs
2342 if (inst.positionals.special_prong == .underscore and operand.ty.zigTypeTag() != .Enum) {
2343 return sema.mod.fail(&block.base, inst.base.src, "'_' prong only allowed when switching on non-exhaustive enums", .{});
2344 // TODO notes "'_' prong here" inst.positionals.cases[last].src
2345 }
2346
2347 // check that operand type supports ranges
2348 if (inst.positionals.range) |range_inst| {
2349 switch (operand.ty.zigTypeTag()) {
2350 .Int, .ComptimeInt => {},
2351 else => {
2352 return sema.mod.fail(&block.base, operand.src, "ranges not allowed when switching on type {}", .{operand.ty});
2353 // TODO notes "range used here" range_inst.src
2354 },
2355 }
2356 }
2357
2358 // validate for duplicate items/missing else prong
2359 switch (operand.ty.zigTypeTag()) {2326 switch (operand.ty.zigTypeTag()) {
2360 .Enum => return sema.mod.fail(&block.base, inst.base.src, "TODO validateSwitch .Enum", .{}),2327 .Enum => return sema.mod.fail(&block.base, src, "TODO validate switch .Enum", .{}),
2361 .ErrorSet => return sema.mod.fail(&block.base, inst.base.src, "TODO validateSwitch .ErrorSet", .{}),2328 .ErrorSet => return sema.mod.fail(&block.base, src, "TODO validate switch .ErrorSet", .{}),
2362 .Union => return sema.mod.fail(&block.base, inst.base.src, "TODO validateSwitch .Union", .{}),2329 .Union => return sema.mod.fail(&block.base, src, "TODO validate switch .Union", .{}),
2363 .Int, .ComptimeInt => {2330 .Int, .ComptimeInt => {
2364 var range_set = @import("RangeSet.zig").init(sema.gpa);2331 var range_set = RangeSet.init(sema.gpa);
2365 defer range_set.deinit();2332 defer range_set.deinit();
23662333
2367 for (inst.positionals.items) |item| {2334 var extra_index: usize = special.end;
2368 const maybe_src = if (item.castTag(.switch_range)) |range| blk: {2335 {
2369 const start_resolved = try sema.resolveInst(range.positionals.lhs);2336 var scalar_i: usize = 0;
2370 const start_casted = try sema.coerce(block, operand.ty, start_resolved);2337 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
2371 const end_resolved = try sema.resolveInst(range.positionals.rhs);2338 const item_ref = @intToEnum(zir.Inst.Ref, sema.code.extra[extra_index]);
2372 const end_casted = try sema.coerce(block, operand.ty, end_resolved);2339 extra_index += 1;
23732340 const body_len = sema.code.extra[extra_index];
2374 break :blk try range_set.add(2341 extra_index += 1;
2375 try sema.resolveConstValue(block, range_start_src, start_casted),2342 const body = sema.code.extra[extra_index..][0..body_len];
2376 try sema.resolveConstValue(block, range_end_src, end_casted),2343 extra_index += body_len;
2377 item.src,2344
2345 try sema.validateSwitchItem(
2346 block,
2347 &range_set,
2348 item_ref,
2349 src_node_offset,
2378 );2350 );
2379 } else blk: {
2380 const resolved = try sema.resolveInst(item);
2381 const casted = try sema.coerce(block, operand.ty, resolved);
2382 const value = try sema.resolveConstValue(block, item_src, casted);
2383 break :blk try range_set.add(value, value, item.src);
2384 };
2385
2386 if (maybe_src) |previous_src| {
2387 return sema.mod.fail(&block.base, item.src, "duplicate switch value", .{});
2388 // TODO notes "previous value is here" previous_src
2389 }2351 }
2390 }2352 }
2353 {
2354 var multi_i: usize = 0;
2355 while (multi_i < multi_cases_len) : (multi_i += 1) {
2356 const items_len = sema.code.extra[extra_index];
2357 extra_index += 1;
2358 const ranges_len = sema.code.extra[extra_index];
2359 extra_index += 1;
2360 const body_len = sema.code.extra[extra_index];
2361 extra_index += 1;
2362 const items = sema.code.refSlice(extra_index, items_len);
2363 extra_index += items_len;
2364
2365 for (items) |item_ref| {
2366 try sema.validateSwitchItem(
2367 block,
2368 &range_set,
2369 item_ref,
2370 src_node_offset,
2371 );
2372 }
23912373
2392 if (operand.ty.zigTypeTag() == .Int) {2374 var range_i: usize = 0;
2393 var arena = std.heap.ArenaAllocator.init(sema.gpa);2375 while (range_i < ranges_len) : (range_i += 1) {
2394 defer arena.deinit();2376 const item_first = @intToEnum(zir.Inst.Ref, sema.code.extra[extra_index]);
23952377 extra_index += 1;
2396 const start = try operand.ty.minInt(&arena, mod.getTarget());2378 const item_last = @intToEnum(zir.Inst.Ref, sema.code.extra[extra_index]);
2397 const end = try operand.ty.maxInt(&arena, mod.getTarget());2379 extra_index += 1;
2398 if (try range_set.spans(start, end)) {2380
2399 if (inst.positionals.special_prong == .@"else") {2381 try sema.validateSwitchRange(
2400 return sema.mod.fail(&block.base, inst.base.src, "unreachable else prong, all cases already handled", .{});2382 block,
2383 &range_set,
2384 item_first,
2385 item_last,
2386 src_node_offset,
2387 );
2401 }2388 }
2402 return;2389
2390 extra_index += body_len;
2403 }2391 }
2404 }2392 }
24052393
2406 if (inst.positionals.special_prong != .@"else") {2394 check_range: {
2407 return sema.mod.fail(&block.base, inst.base.src, "switch must handle all possibilities", .{});2395 if (operand.ty.zigTypeTag() == .Int) {
2396 var arena = std.heap.ArenaAllocator.init(sema.gpa);
2397 defer arena.deinit();
2398
2399 const min_int = try operand.ty.minInt(&arena, sema.mod.getTarget());
2400 const max_int = try operand.ty.maxInt(&arena, sema.mod.getTarget());
2401 if (try range_set.spans(min_int, max_int)) {
2402 if (special_prong == .@"else") {
2403 return sema.mod.fail(
2404 &block.base,
2405 special_prong_src,
2406 "unreachable else prong; all cases already handled",
2407 .{},
2408 );
2409 }
2410 break :check_range;
2411 }
2412 }
2413 if (special_prong != .@"else") {
2414 return sema.mod.fail(
2415 &block.base,
2416 src,
2417 "switch must handle all possibilities",
2418 .{},
2419 );
2420 }
2408 }2421 }
2409 },2422 },
2410 .Bool => {2423 .Bool => {
2411 var true_count: u8 = 0;2424 var true_count: u8 = 0;
2412 var false_count: u8 = 0;2425 var false_count: u8 = 0;
2413 for (inst.positionals.items) |item| {
2414 const resolved = try sema.resolveInst(item);
2415 const casted = try sema.coerce(block, Type.initTag(.bool), resolved);
2416 if ((try sema.resolveConstValue(block, item_src, casted)).toBool()) {
2417 true_count += 1;
2418 } else {
2419 false_count += 1;
2420 }
24212426
2422 if (true_count + false_count > 2) {2427 var extra_index: usize = special.end;
2423 return sema.mod.fail(&block.base, item.src, "duplicate switch value", .{});2428 {
2429 var scalar_i: usize = 0;
2430 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
2431 const item_ref = @intToEnum(zir.Inst.Ref, sema.code.extra[extra_index]);
2432 extra_index += 1;
2433 const body_len = sema.code.extra[extra_index];
2434 extra_index += 1;
2435 const body = sema.code.extra[extra_index..][0..body_len];
2436 extra_index += body_len;
2437
2438 try sema.validateSwitchItemBool(
2439 block,
2440 &true_count,
2441 &false_count,
2442 item_ref,
2443 src_node_offset,
2444 );
2424 }2445 }
2425 }2446 }
2426 if ((true_count + false_count < 2) and inst.positionals.special_prong != .@"else") {2447 {
2427 return sema.mod.fail(&block.base, inst.base.src, "switch must handle all possibilities", .{});2448 var multi_i: usize = 0;
2449 while (multi_i < multi_cases_len) : (multi_i += 1) {
2450 const items_len = sema.code.extra[extra_index];
2451 extra_index += 1;
2452 const ranges_len = sema.code.extra[extra_index];
2453 extra_index += 1;
2454 const body_len = sema.code.extra[extra_index];
2455 extra_index += 1;
2456 const items = sema.code.refSlice(extra_index, items_len);
2457 extra_index += items_len + body_len;
2458
2459 for (items) |item_ref| {
2460 try sema.validateSwitchItemBool(
2461 block,
2462 &true_count,
2463 &false_count,
2464 item_ref,
2465 src_node_offset,
2466 );
2467 }
2468
2469 try sema.validateSwitchNoRange(block, ranges_len, operand.ty, src_node_offset);
2470 }
2428 }2471 }
2429 if ((true_count + false_count == 2) and inst.positionals.special_prong == .@"else") {2472 switch (special_prong) {
2430 return sema.mod.fail(&block.base, inst.base.src, "unreachable else prong, all cases already handled", .{});2473 .@"else" => {
2474 if (true_count + false_count == 2) {
2475 return sema.mod.fail(
2476 &block.base,
2477 src,
2478 "unreachable else prong; all cases already handled",
2479 .{},
2480 );
2481 }
2482 },
2483 .under, .none => {
2484 if (true_count + false_count < 2) {
2485 return sema.mod.fail(
2486 &block.base,
2487 src,
2488 "switch must handle all possibilities",
2489 .{},
2490 );
2491 }
2492 },
2431 }2493 }
2432 },2494 },
2433 .EnumLiteral, .Void, .Fn, .Pointer, .Type => {2495 .EnumLiteral, .Void, .Fn, .Pointer, .Type => {
2434 if (inst.positionals.special_prong != .@"else") {2496 if (special_prong != .@"else") {
2435 return sema.mod.fail(&block.base, inst.base.src, "else prong required when switching on type '{}'", .{operand.ty});2497 return sema.mod.fail(
2498 &block.base,
2499 src,
2500 "else prong required when switching on type '{}'",
2501 .{operand.ty},
2502 );
2436 }2503 }
24372504
2438 var seen_values = std.HashMap(Value, usize, Value.hash, Value.eql, std.hash_map.DefaultMaxLoadPercentage).init(sema.gpa);2505 var seen_values = ValueSrcMap.init(sema.gpa);
2439 defer seen_values.deinit();2506 defer seen_values.deinit();
24402507
2441 for (inst.positionals.items) |item| {2508 var extra_index: usize = special.end;
2442 const resolved = try sema.resolveInst(item);2509 {
2443 const casted = try sema.coerce(block, operand.ty, resolved);2510 var scalar_i: usize = 0;
2444 const val = try sema.resolveConstValue(block, item_src, casted);2511 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
2512 const item_ref = @intToEnum(zir.Inst.Ref, sema.code.extra[extra_index]);
2513 extra_index += 1;
2514 const body_len = sema.code.extra[extra_index];
2515 extra_index += 1;
2516 const body = sema.code.extra[extra_index..][0..body_len];
2517 extra_index += body_len;
2518
2519 try sema.validateSwitchItemSparse(
2520 block,
2521 &seen_values,
2522 item_ref,
2523 src_node_offset,
2524 );
2525 }
2526 }
2527 {
2528 var multi_i: usize = 0;
2529 while (multi_i < multi_cases_len) : (multi_i += 1) {
2530 const items_len = sema.code.extra[extra_index];
2531 extra_index += 1;
2532 const ranges_len = sema.code.extra[extra_index];
2533 extra_index += 1;
2534 const body_len = sema.code.extra[extra_index];
2535 extra_index += 1;
2536 const items = sema.code.refSlice(extra_index, items_len);
2537 extra_index += items_len + body_len;
2538
2539 for (items) |item_ref| {
2540 try sema.validateSwitchItemSparse(
2541 block,
2542 &seen_values,
2543 item_ref,
2544 src_node_offset,
2545 );
2546 }
24452547
2446 if (try seen_values.fetchPut(val, item.src)) |prev| {2548 try sema.validateSwitchNoRange(block, ranges_len, operand.ty, src_node_offset);
2447 return sema.mod.fail(&block.base, item.src, "duplicate switch value", .{});
2448 // TODO notes "previous value here" prev.value
2449 }2549 }
2450 }2550 }
2451 },2551 },
...@@ -2464,10 +2564,298 @@ fn validateSwitch(sema: *Sema, block: *Scope.Block, operand: *Inst, inst: zir.In...@@ -2464,10 +2564,298 @@ fn validateSwitch(sema: *Sema, block: *Scope.Block, operand: *Inst, inst: zir.In
2464 .AnyFrame,2564 .AnyFrame,
2465 .ComptimeFloat,2565 .ComptimeFloat,
2466 .Float,2566 .Float,
2467 => {2567 => return sema.mod.fail(&block.base, operand_src, "invalid switch operand type '{}'", .{
2468 return sema.mod.fail(&block.base, operand.src, "invalid switch operand type '{}'", .{operand.ty});2568 operand.ty,
2469 },2569 }),
2570 }
2571
2572 if (try sema.resolveDefinedValue(block, src, operand)) |operand_val| {
2573 var extra_index: usize = special.end;
2574 {
2575 var scalar_i: usize = 0;
2576 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
2577 const item_ref = @intToEnum(zir.Inst.Ref, sema.code.extra[extra_index]);
2578 extra_index += 1;
2579 const body_len = sema.code.extra[extra_index];
2580 extra_index += 1;
2581 const body = sema.code.extra[extra_index..][0..body_len];
2582 extra_index += body_len;
2583
2584 const item = try sema.resolveInst(item_ref);
2585 const item_val = try sema.resolveConstValue(block, item.src, item);
2586 if (operand_val.eql(item_val)) {
2587 return sema.analyzeBody(block, body);
2588 }
2589 }
2590 }
2591 {
2592 var multi_i: usize = 0;
2593 while (multi_i < multi_cases_len) : (multi_i += 1) {
2594 const items_len = sema.code.extra[extra_index];
2595 extra_index += 1;
2596 const ranges_len = sema.code.extra[extra_index];
2597 extra_index += 1;
2598 const body_len = sema.code.extra[extra_index];
2599 extra_index += 1;
2600 const items = sema.code.refSlice(extra_index, items_len);
2601 extra_index += items_len;
2602 const body = sema.code.extra[extra_index + 2 * ranges_len ..][0..body_len];
2603
2604 for (items) |item_ref| {
2605 const item = try sema.resolveInst(item_ref);
2606 const item_val = try sema.resolveConstValue(block, item.src, item);
2607 if (operand_val.eql(item_val)) {
2608 return sema.analyzeBody(block, body);
2609 }
2610 }
2611
2612 var range_i: usize = 0;
2613 while (range_i < ranges_len) : (range_i += 1) {
2614 const item_first = @intToEnum(zir.Inst.Ref, sema.code.extra[extra_index]);
2615 extra_index += 1;
2616 const item_last = @intToEnum(zir.Inst.Ref, sema.code.extra[extra_index]);
2617 extra_index += 1;
2618
2619 const first_tv = try sema.resolveInstConst(block, .todo, item_first);
2620 const last_tv = try sema.resolveInstConst(block, .todo, item_last);
2621 if (Value.compare(operand_val, .gte, first_tv.val) and
2622 Value.compare(operand_val, .lte, last_tv.val))
2623 {
2624 return sema.analyzeBody(block, body);
2625 }
2626 }
2627
2628 extra_index += body_len;
2629 }
2630 }
2631 return sema.analyzeBody(block, special.body);
2632 }
2633
2634 if (scalar_cases_len + multi_cases_len == 0) {
2635 return sema.analyzeBody(block, special.body);
2636 }
2637
2638 try sema.requireRuntimeBlock(block, src);
2639 // TODO when reworking TZIR memory layout make multi cases get generated as cases,
2640 // not as part of the "else" block.
2641 const cases = try sema.arena.alloc(Inst.SwitchBr.Case, scalar_cases_len);
2642
2643 var case_block = block.makeSubBlock();
2644 defer case_block.instructions.deinit(sema.gpa);
2645
2646 var extra_index: usize = special.end;
2647
2648 var scalar_i: usize = 0;
2649 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
2650 const item_ref = @intToEnum(zir.Inst.Ref, sema.code.extra[extra_index]);
2651 extra_index += 1;
2652 const body_len = sema.code.extra[extra_index];
2653 extra_index += 1;
2654 const body = sema.code.extra[extra_index..][0..body_len];
2655 extra_index += body_len;
2656
2657 case_block.instructions.shrinkRetainingCapacity(0);
2658 const item = try sema.resolveInst(item_ref);
2659 const item_val = try sema.resolveConstValue(block, item.src, item);
2660
2661 _ = try sema.analyzeBody(&case_block, body);
2662
2663 cases[scalar_i] = .{
2664 .item = item_val,
2665 .body = .{ .instructions = try sema.arena.dupe(*Inst, case_block.instructions.items) },
2666 };
2667 }
2668
2669 var first_condbr: *Inst.CondBr = undefined;
2670 var prev_condbr: ?*Inst.CondBr = null;
2671
2672 var multi_i: usize = 0;
2673 while (multi_i < multi_cases_len) : (multi_i += 1) {
2674 const items_len = sema.code.extra[extra_index];
2675 extra_index += 1;
2676 const ranges_len = sema.code.extra[extra_index];
2677 extra_index += 1;
2678 const body_len = sema.code.extra[extra_index];
2679 extra_index += 1;
2680 const items = sema.code.refSlice(extra_index, items_len);
2681 extra_index += items_len;
2682
2683 case_block.instructions.shrinkRetainingCapacity(0);
2684
2685 var any_ok: ?*Inst = null;
2686 const bool_ty = comptime Type.initTag(.bool);
2687
2688 for (items) |item_ref| {
2689 const item = try sema.resolveInst(item_ref);
2690 _ = try sema.resolveConstValue(block, item.src, item);
2691
2692 const cmp_ok = try case_block.addBinOp(item.src, bool_ty, .cmp_eq, operand, item);
2693 if (any_ok) |some| {
2694 any_ok = try case_block.addBinOp(item.src, bool_ty, .bool_or, some, cmp_ok);
2695 } else {
2696 any_ok = cmp_ok;
2697 }
2698 }
2699
2700 var range_i: usize = 0;
2701 while (range_i < ranges_len) : (range_i += 1) {
2702 const first_ref = @intToEnum(zir.Inst.Ref, sema.code.extra[extra_index]);
2703 extra_index += 1;
2704 const last_ref = @intToEnum(zir.Inst.Ref, sema.code.extra[extra_index]);
2705 extra_index += 1;
2706
2707 const item_first = try sema.resolveInst(first_ref);
2708 const item_last = try sema.resolveInst(last_ref);
2709
2710 _ = try sema.resolveConstValue(block, item_first.src, item_first);
2711 _ = try sema.resolveConstValue(block, item_last.src, item_last);
2712
2713 const range_src = item_first.src;
2714
2715 // operand >= first and operand <= last
2716 const range_first_ok = try case_block.addBinOp(
2717 item_first.src,
2718 bool_ty,
2719 .cmp_gte,
2720 operand,
2721 item_first,
2722 );
2723 const range_last_ok = try case_block.addBinOp(
2724 item_last.src,
2725 bool_ty,
2726 .cmp_lte,
2727 operand,
2728 item_last,
2729 );
2730 const range_ok = try case_block.addBinOp(
2731 range_src,
2732 bool_ty,
2733 .bool_and,
2734 range_first_ok,
2735 range_last_ok,
2736 );
2737 if (any_ok) |some| {
2738 any_ok = try case_block.addBinOp(range_src, bool_ty, .bool_or, some, range_ok);
2739 } else {
2740 any_ok = range_ok;
2741 }
2742 }
2743
2744 const body = sema.code.extra[extra_index..][0..body_len];
2745 extra_index += body_len;
2746 _ = try sema.analyzeBody(&case_block, body);
2747 const then_body: Body = .{
2748 .instructions = try sema.arena.dupe(*Inst, case_block.instructions.items),
2749 };
2750 const new_condbr = try sema.arena.create(Inst.CondBr);
2751 new_condbr.* = .{
2752 .base = .{
2753 .tag = .condbr,
2754 .ty = Type.initTag(.noreturn),
2755 .src = src,
2756 },
2757 .condition = any_ok.?,
2758 .then_body = then_body,
2759 .else_body = undefined,
2760 };
2761 if (prev_condbr) |condbr| {
2762 condbr.else_body = .{
2763 .instructions = try sema.arena.dupe(*Inst, &[1]*Inst{&new_condbr.base}),
2764 };
2765 } else {
2766 first_condbr = new_condbr;
2767 }
2768 prev_condbr = new_condbr;
2470 }2769 }
2770
2771 case_block.instructions.shrinkRetainingCapacity(0);
2772 _ = try sema.analyzeBody(&case_block, special.body);
2773 const else_body: Body = .{
2774 .instructions = try sema.arena.dupe(*Inst, case_block.instructions.items),
2775 };
2776 first_condbr.else_body = else_body;
2777
2778 const final_else_body: Body = .{
2779 .instructions = try sema.arena.dupe(*Inst, &[1]*Inst{&first_condbr.base}),
2780 };
2781
2782 _ = try block.addSwitchBr(src, operand, cases, final_else_body);
2783 return always_noreturn;
2784}
2785
2786fn validateSwitchItem(
2787 sema: *Sema,
2788 block: *Scope.Block,
2789 range_set: *RangeSet,
2790 item_ref: zir.Inst.Ref,
2791 src_node_offset: i32,
2792) InnerError!void {
2793 @panic("TODO");
2794}
2795
2796fn validateSwitchItemBool(
2797 sema: *Sema,
2798 block: *Scope.Block,
2799 true_count: *u8,
2800 false_count: *u8,
2801 item_ref: zir.Inst.Ref,
2802 src_node_offset: i32,
2803) InnerError!void {
2804 @panic("TODO");
2805}
2806
2807fn validateSwitchRange(
2808 sema: *Sema,
2809 block: *Scope.Block,
2810 range_set: *RangeSet,
2811 item_first: zir.Inst.Ref,
2812 item_last: zir.Inst.Ref,
2813 src_node_offset: i32,
2814) InnerError!void {
2815 @panic("TODO");
2816}
2817
2818fn validateSwitchItemSparse(
2819 sema: *Sema,
2820 block: *Scope.Block,
2821 seen_values: *ValueSrcMap,
2822 item_ref: zir.Inst.Ref,
2823 src_node_offset: i32,
2824) InnerError!void {
2825 @panic("TODO");
2826}
2827
2828fn validateSwitchNoRange(
2829 sema: *Sema,
2830 block: *Scope.Block,
2831 ranges_len: u32,
2832 operand_ty: Type,
2833 src_node_offset: i32,
2834) InnerError!void {
2835 if (ranges_len == 0)
2836 return;
2837
2838 const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = src_node_offset };
2839 const range_src: LazySrcLoc = .{ .node_offset_switch_range = src_node_offset };
2840
2841 const msg = msg: {
2842 const msg = try sema.mod.errMsg(
2843 &block.base,
2844 operand_src,
2845 "ranges not allowed when switching on type '{}'",
2846 .{operand_ty},
2847 );
2848 errdefer msg.destroy(sema.gpa);
2849 try sema.mod.errNote(
2850 &block.base,
2851 range_src,
2852 msg,
2853 "range here",
2854 .{},
2855 );
2856 break :msg msg;
2857 };
2858 return sema.mod.failWithOwnedErrorMsg(&block.base, msg);
2471}2859}
24722860
2473fn zirImport(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {2861fn zirImport(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
...@@ -3095,30 +3483,21 @@ fn zirCondbr(...@@ -3095,30 +3483,21 @@ fn zirCondbr(
3095 return always_noreturn;3483 return always_noreturn;
3096 }3484 }
30973485
3098 var true_block: Scope.Block = .{3486 var sub_block = parent_block.makeSubBlock();
3099 .parent = parent_block,3487 defer sub_block.instructions.deinit(sema.gpa);
3100 .sema = sema,3488
3101 .src_decl = parent_block.src_decl,3489 _ = try sema.analyzeBody(&sub_block, then_body);
3102 .instructions = .{},3490 const tzir_then_body: ir.Body = .{
3103 .inlining = parent_block.inlining,3491 .instructions = try sema.arena.dupe(*Inst, sub_block.instructions.items),
3104 .is_comptime = parent_block.is_comptime,
3105 };3492 };
3106 defer true_block.instructions.deinit(sema.gpa);
3107 _ = try sema.analyzeBody(&true_block, then_body);
31083493
3109 var false_block: Scope.Block = .{3494 sub_block.instructions.shrinkRetainingCapacity(0);
3110 .parent = parent_block,3495
3111 .sema = sema,3496 _ = try sema.analyzeBody(&sub_block, else_body);
3112 .src_decl = parent_block.src_decl,3497 const tzir_else_body: ir.Body = .{
3113 .instructions = .{},3498 .instructions = try sema.arena.dupe(*Inst, sub_block.instructions.items),
3114 .inlining = parent_block.inlining,
3115 .is_comptime = parent_block.is_comptime,
3116 };3499 };
3117 defer false_block.instructions.deinit(sema.gpa);
3118 _ = try sema.analyzeBody(&false_block, else_body);
31193500
3120 const tzir_then_body: ir.Body = .{ .instructions = try sema.arena.dupe(*Inst, true_block.instructions.items) };
3121 const tzir_else_body: ir.Body = .{ .instructions = try sema.arena.dupe(*Inst, false_block.instructions.items) };
3122 _ = try parent_block.addCondBr(src, cond, tzir_then_body, tzir_else_body);3501 _ = try parent_block.addCondBr(src, cond, tzir_then_body, tzir_else_body);
3123 return always_noreturn;3502 return always_noreturn;
3124}3503}
src/type.zig+4
...@@ -3303,6 +3303,10 @@ pub const Type = extern union {...@@ -3303,6 +3303,10 @@ pub const Type = extern union {
3303 }3303 }
3304 }3304 }
33053305
3306 pub fn isExhaustiveEnum(ty: Type) bool {
3307 return false; // TODO
3308 }
3309
3306 /// This enum does not directly correspond to `std.builtin.TypeId` because3310 /// This enum does not directly correspond to `std.builtin.TypeId` because
3307 /// it has extra enum tags in it, as a way of using less memory. For example,3311 /// it has extra enum tags in it, as a way of using less memory. For example,
3308 /// even though Zig recognizes `*align(10) i32` and `*i32` both as Pointer types3312 /// even though Zig recognizes `*align(10) i32` and `*i32` both as Pointer types
src/zir.zig+129-48
...@@ -589,31 +589,31 @@ pub const Inst = struct {...@@ -589,31 +589,31 @@ pub const Inst = struct {
589 /// AST node is the switch, payload is `SwitchBr`.589 /// AST node is the switch, payload is `SwitchBr`.
590 /// All prongs of target handled.590 /// All prongs of target handled.
591 switch_br,591 switch_br,
592 /// Same as switch_br, except has a range field.592 /// Same as switch_br, except one or more prongs have multiple items.
593 switch_br_range,593 switch_br_multi,
594 /// Same as switch_br, except has an else prong.594 /// Same as switch_br, except has an else prong.
595 switch_br_else,595 switch_br_else,
596 /// Same as switch_br_else, except has a range field.596 /// Same as switch_br_else, except one or more prongs have multiple items.
597 switch_br_else_range,597 switch_br_else_multi,
598 /// Same as switch_br, except has an underscore prong.598 /// Same as switch_br, except has an underscore prong.
599 switch_br_underscore,599 switch_br_under,
600 /// Same as switch_br, except has a range field.600 /// Same as switch_br, except one or more prongs have multiple items.
601 switch_br_underscore_range,601 switch_br_under_multi,
602 /// Same as `switch_br` but the target is a pointer to the value being switched on.602 /// Same as `switch_br` but the target is a pointer to the value being switched on.
603 switch_br_ref,603 switch_br_ref,
604 /// Same as `switch_br_range` but the target is a pointer to the value being switched on.604 /// Same as `switch_br_multi` but the target is a pointer to the value being switched on.
605 switch_br_ref_range,605 switch_br_ref_multi,
606 /// Same as `switch_br_else` but the target is a pointer to the value being switched on.606 /// Same as `switch_br_else` but the target is a pointer to the value being switched on.
607 switch_br_ref_else,607 switch_br_ref_else,
608 /// Same as `switch_br_else_range` but the target is a pointer to the608 /// Same as `switch_br_else_multi` but the target is a pointer to the
609 /// value being switched on.609 /// value being switched on.
610 switch_br_ref_else_range,610 switch_br_ref_else_multi,
611 /// Same as `switch_br_underscore` but the target is a pointer to the value611 /// Same as `switch_br_under` but the target is a pointer to the value
612 /// being switched on.612 /// being switched on.
613 switch_br_ref_underscore,613 switch_br_ref_under,
614 /// Same as `switch_br_underscore_range` but the target is a pointer to614 /// Same as `switch_br_under_multi` but the target is a pointer to
615 /// the value being switched on.615 /// the value being switched on.
616 switch_br_ref_underscore_range,616 switch_br_ref_under_multi,
617617
618 /// Returns whether the instruction is one of the control flow "noreturn" types.618 /// Returns whether the instruction is one of the control flow "noreturn" types.
619 /// Function calls do not count.619 /// Function calls do not count.
...@@ -757,17 +757,17 @@ pub const Inst = struct {...@@ -757,17 +757,17 @@ pub const Inst = struct {
757 .repeat,757 .repeat,
758 .repeat_inline,758 .repeat_inline,
759 .switch_br,759 .switch_br,
760 .switch_br_range,760 .switch_br_multi,
761 .switch_br_else,761 .switch_br_else,
762 .switch_br_else_range,762 .switch_br_else_multi,
763 .switch_br_underscore,763 .switch_br_under,
764 .switch_br_underscore_range,764 .switch_br_under_multi,
765 .switch_br_ref,765 .switch_br_ref,
766 .switch_br_ref_range,766 .switch_br_ref_multi,
767 .switch_br_ref_else,767 .switch_br_ref_else,
768 .switch_br_ref_else_range,768 .switch_br_ref_else_multi,
769 .switch_br_ref_underscore,769 .switch_br_ref_under,
770 .switch_br_ref_underscore_range,770 .switch_br_ref_under_multi,
771 => true,771 => true,
772 };772 };
773 }773 }
...@@ -1333,7 +1333,7 @@ pub const Inst = struct {...@@ -1333,7 +1333,7 @@ pub const Inst = struct {
1333 /// This form is supported when there are no ranges, and exactly 1 item per block.1333 /// This form is supported when there are no ranges, and exactly 1 item per block.
1334 /// Depending on zir tag and len fields, extra fields trail1334 /// Depending on zir tag and len fields, extra fields trail
1335 /// this one in the extra array.1335 /// this one in the extra array.
1336 /// 0. else_body { // If the tag has "_else" or "_underscore" in it.1336 /// 0. else_body { // If the tag has "_else" or "_under" in it.
1337 /// body_len: u32,1337 /// body_len: u32,
1338 /// body member Index for every body_len1338 /// body member Index for every body_len
1339 /// }1339 /// }
...@@ -1351,7 +1351,7 @@ pub const Inst = struct {...@@ -1351,7 +1351,7 @@ pub const Inst = struct {
1351 /// or a range.1351 /// or a range.
1352 /// Depending on zir tag and len fields, extra fields trail1352 /// Depending on zir tag and len fields, extra fields trail
1353 /// this one in the extra array.1353 /// this one in the extra array.
1354 /// 0. else_body { // If the tag has "_else" or "_underscore" in it.1354 /// 0. else_body { // If the tag has "_else" or "_under" in it.
1355 /// body_len: u32,1355 /// body_len: u32,
1356 /// body member Index for every body_len1356 /// body member Index for every body_len
1357 /// }1357 /// }
...@@ -1362,19 +1362,19 @@ pub const Inst = struct {...@@ -1362,19 +1362,19 @@ pub const Inst = struct {
1362 /// }1362 /// }
1363 /// 2. multi_cases: { // for every multi_cases_len1363 /// 2. multi_cases: { // for every multi_cases_len
1364 /// items_len: u32,1364 /// items_len: u32,
1365 /// item: Ref for every items_len1365 /// ranges_len: u32,
1366 /// block_index: u32, // index in extra to a `Block`1366 /// body_len: u32,
1367 /// }1367 /// item: Ref // for every items_len
1368 /// 3. range_cases: { // for every range_cases_len1368 /// ranges: { // for every ranges_len
1369 /// item_start: Ref,1369 /// item_first: Ref,
1370 /// item_end: Ref,1370 /// item_last: Ref,
1371 /// block_index: u32, // index in extra to a `Block`1371 /// }
1372 /// body member Index for every body_len
1372 /// }1373 /// }
1373 pub const SwitchBrRange = struct {1374 pub const SwitchBrMulti = struct {
1374 operand: Ref,1375 operand: Ref,
1375 scalar_cases_len: u32,1376 scalar_cases_len: u32,
1376 multi_cases_len: u32,1377 multi_cases_len: u32,
1377 range_cases_len: u32,
1378 };1378 };
13791379
1380 pub const Field = struct {1380 pub const Field = struct {
...@@ -1544,19 +1544,19 @@ const Writer = struct {...@@ -1544,19 +1544,19 @@ const Writer = struct {
15441544
1545 .switch_br,1545 .switch_br,
1546 .switch_br_else,1546 .switch_br_else,
1547 .switch_br_underscore,1547 .switch_br_under,
1548 .switch_br_ref,1548 .switch_br_ref,
1549 .switch_br_ref_else,1549 .switch_br_ref_else,
1550 .switch_br_ref_underscore,1550 .switch_br_ref_under,
1551 => try self.writePlNodeSwitchBr(stream, inst),1551 => try self.writePlNodeSwitchBr(stream, inst),
15521552
1553 .switch_br_range,1553 .switch_br_multi,
1554 .switch_br_else_range,1554 .switch_br_else_multi,
1555 .switch_br_underscore_range,1555 .switch_br_under_multi,
1556 .switch_br_ref_range,1556 .switch_br_ref_multi,
1557 .switch_br_ref_else_range,1557 .switch_br_ref_else_multi,
1558 .switch_br_ref_underscore_range,1558 .switch_br_ref_under_multi,
1559 => try self.writePlNodeSwitchBrRange(stream, inst),1559 => try self.writePlNodeSwitchBrMulti(stream, inst),
15601560
1561 .compile_log,1561 .compile_log,
1562 .typeof_peer,1562 .typeof_peer,
...@@ -1766,17 +1766,98 @@ const Writer = struct {...@@ -1766,17 +1766,98 @@ const Writer = struct {
1766 fn writePlNodeSwitchBr(self: *Writer, stream: anytype, inst: Inst.Index) !void {1766 fn writePlNodeSwitchBr(self: *Writer, stream: anytype, inst: Inst.Index) !void {
1767 const inst_data = self.code.instructions.items(.data)[inst].pl_node;1767 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
1768 const extra = self.code.extraData(Inst.SwitchBr, inst_data.payload_index);1768 const extra = self.code.extraData(Inst.SwitchBr, inst_data.payload_index);
1769
1770 try self.writeInstRef(stream, extra.data.operand);1769 try self.writeInstRef(stream, extra.data.operand);
1771 try stream.writeAll(", TODO) ");1770 var extra_index: usize = extra.end;
1771 {
1772 var scalar_i: usize = 0;
1773 while (scalar_i < extra.data.cases_len) : (scalar_i += 1) {
1774 const item_ref = @intToEnum(Inst.Ref, self.code.extra[extra_index]);
1775 extra_index += 1;
1776 const body_len = self.code.extra[extra_index];
1777 extra_index += 1;
1778 const body = self.code.extra[extra_index..][0..body_len];
1779 extra_index += body_len;
1780
1781 try stream.writeAll(", ");
1782 try self.writeInstRef(stream, item_ref);
1783 try stream.writeAll(" => {\n");
1784 self.indent += 2;
1785 try self.writeBody(stream, body);
1786 self.indent -= 2;
1787 try stream.writeByteNTimes(' ', self.indent);
1788 try stream.writeAll("}");
1789 }
1790 }
1791 try stream.writeAll(") ");
1772 try self.writeSrc(stream, inst_data.src());1792 try self.writeSrc(stream, inst_data.src());
1773 }1793 }
17741794
1775 fn writePlNodeSwitchBrRange(self: *Writer, stream: anytype, inst: Inst.Index) !void {1795 fn writePlNodeSwitchBrMulti(self: *Writer, stream: anytype, inst: Inst.Index) !void {
1776 const inst_data = self.code.instructions.items(.data)[inst].pl_node;1796 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
1777 const extra = self.code.extraData(Inst.SwitchBrRange, inst_data.payload_index);1797 const extra = self.code.extraData(Inst.SwitchBrMulti, inst_data.payload_index);
1778 try self.writeInstRef(stream, extra.data.operand);1798 try self.writeInstRef(stream, extra.data.operand);
1779 try stream.writeAll(", TODO) ");1799 var extra_index: usize = extra.end;
1800 {
1801 var scalar_i: usize = 0;
1802 while (scalar_i < extra.data.scalar_cases_len) : (scalar_i += 1) {
1803 const item_ref = @intToEnum(Inst.Ref, self.code.extra[extra_index]);
1804 extra_index += 1;
1805 const body_len = self.code.extra[extra_index];
1806 extra_index += 1;
1807 const body = self.code.extra[extra_index..][0..body_len];
1808 extra_index += body_len;
1809
1810 try stream.writeAll(", ");
1811 try self.writeInstRef(stream, item_ref);
1812 try stream.writeAll(" => {\n");
1813 self.indent += 2;
1814 try self.writeBody(stream, body);
1815 self.indent -= 2;
1816 try stream.writeByteNTimes(' ', self.indent);
1817 try stream.writeAll("}");
1818 }
1819 }
1820 {
1821 var multi_i: usize = 0;
1822 while (multi_i < extra.data.multi_cases_len) : (multi_i += 1) {
1823 const items_len = self.code.extra[extra_index];
1824 extra_index += 1;
1825 const ranges_len = self.code.extra[extra_index];
1826 extra_index += 1;
1827 const body_len = self.code.extra[extra_index];
1828 extra_index += 1;
1829 const items = self.code.refSlice(extra_index, items_len);
1830 extra_index += items_len;
1831
1832 for (items) |item_ref| {
1833 try stream.writeAll(", ");
1834 try self.writeInstRef(stream, item_ref);
1835 }
1836
1837 var range_i: usize = 0;
1838 while (range_i < ranges_len) : (range_i += 1) {
1839 const item_first = @intToEnum(Inst.Ref, self.code.extra[extra_index]);
1840 extra_index += 1;
1841 const item_last = @intToEnum(Inst.Ref, self.code.extra[extra_index]);
1842 extra_index += 1;
1843
1844 try stream.writeAll(", ");
1845 try self.writeInstRef(stream, item_first);
1846 try stream.writeAll("...");
1847 try self.writeInstRef(stream, item_last);
1848 }
1849
1850 const body = self.code.extra[extra_index..][0..body_len];
1851 extra_index += body_len;
1852 try stream.writeAll(" => {\n");
1853 self.indent += 2;
1854 try self.writeBody(stream, body);
1855 self.indent -= 2;
1856 try stream.writeByteNTimes(' ', self.indent);
1857 try stream.writeAll("}");
1858 }
1859 }
1860 try stream.writeAll(") ");
1780 try self.writeSrc(stream, inst_data.src());1861 try self.writeSrc(stream, inst_data.src());
1781 }1862 }
17821863