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:
3535 var decl and assignment instructions, etc.
3636 - make it set sema.src where appropriate
3737 * 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(
12581258 .condbr,
12591259 .condbr_inline,
12601260 .switch_br,
1261 .switch_br_range,
1261 .switch_br_multi,
12621262 .switch_br_else,
1263 .switch_br_else_range,
1264 .switch_br_underscore,
1265 .switch_br_underscore_range,
1263 .switch_br_else_multi,
1264 .switch_br_under,
1265 .switch_br_under_multi,
12661266 .switch_br_ref,
1267 .switch_br_ref_range,
1267 .switch_br_ref_multi,
12681268 .switch_br_ref_else,
1269 .switch_br_ref_else_range,
1270 .switch_br_ref_underscore,
1271 .switch_br_ref_underscore_range,
1269 .switch_br_ref_else_multi,
1270 .switch_br_ref_under,
1271 .switch_br_ref_under_multi,
12721272 .compile_error,
12731273 .ret_node,
12741274 .ret_tok,
......@@ -2550,35 +2550,12 @@ fn switchExpr(
25502550) InnerError!zir.Inst.Ref {
25512551 const tree = parent_gz.tree();
25522552 const node_datas = tree.nodes.items(.data);
2553 const main_tokens = tree.nodes.items(.main_token);
2554 const token_tags = tree.tokens.items(.tag);
25552553 const node_tags = tree.nodes.items(.tag);
2556
2557 if (true) @panic("TODO rework for zir-memory-layout branch");
2558
2559 const switch_token = main_tokens[switch_node];
2560 const target_node = node_datas[switch_node].lhs;
2554 const token_tags = tree.tokens.items(.tag);
2555 const operand_node = node_datas[switch_node].lhs;
25612556 const extra = tree.extraData(node_datas[switch_node].rhs, ast.Node.SubRange);
25622557 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;
25822559 var any_payload_is_ref = false;
25832560 for (case_nodes) |case_node| {
25842561 const case = switch (node_tags[case_node]) {
......@@ -2591,284 +2568,22 @@ fn switchExpr(
25912568 any_payload_is_ref = true;
25922569 }
25932570 }
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 }
26732571 }
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
26802573 const rl_and_tag: struct { rl: ResultLoc, tag: zir.Inst.Tag } = if (any_payload_is_ref) .{
26812574 .rl = .ref,
2682 .tag = .switchbr_ref,
2575 .tag = .switch_br_ref,
26832576 } else .{
26842577 .rl = .none,
2685 .tag = .switchbr,
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 = .{},
2578 .tag = .switch_br,
27062579 };
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 = .{
2710 .parent = scope,
2711 .decl = case_scope.decl,
2712 .arena = case_scope.arena,
2713 .force_comptime = case_scope.force_comptime,
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 }
2582 const result = try parent_gz.addPlNode(.switch_br, switch_node, zir.Inst.SwitchBr{
2583 .operand = operand,
2584 .cases_len = 0,
2585 });
2586 return rvalue(parent_gz, scope, rl, result, switch_node);
28722587}
28732588
28742589fn 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 {
888888 pub fn addSwitchBr(
889889 block: *Scope.Block,
890890 src: LazySrcLoc,
891 target: *ir.Inst,
891 operand: *ir.Inst,
892892 cases: []ir.Inst.SwitchBr.Case,
893893 else_body: ir.Body,
894894 ) !*ir.Inst {
......@@ -899,7 +899,7 @@ pub const Scope = struct {
899899 .ty = Type.initTag(.noreturn),
900900 .src = src,
901901 },
902 .target = target,
902 .target = operand,
903903 .cases = cases,
904904 .else_body = else_body,
905905 };
......@@ -1533,6 +1533,8 @@ pub const SrcLoc = struct {
15331533 .node_offset_bin_lhs,
15341534 .node_offset_bin_rhs,
15351535 .node_offset_switch_operand,
1536 .node_offset_switch_special_prong,
1537 .node_offset_switch_range,
15361538 => src_loc.container.decl.container.file_scope,
15371539 };
15381540 }
......@@ -1665,6 +1667,8 @@ pub const SrcLoc = struct {
16651667 return token_starts[tok_index];
16661668 },
16671669 .node_offset_switch_operand => @panic("TODO"),
1670 .node_offset_switch_special_prong => @panic("TODO"),
1671 .node_offset_switch_range => @panic("TODO"),
16681672 }
16691673 }
16701674};
......@@ -1802,6 +1806,17 @@ pub const LazySrcLoc = union(enum) {
18021806 /// which points to a switch expression AST node. Next, nagivate to the operand.
18031807 /// The Decl is determined contextually.
18041808 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
18061821 /// Upgrade to a `SrcLoc` based on the `Decl` or file in the provided scope.
18071822 pub fn toSrcLoc(lazy: LazySrcLoc, scope: *Scope) SrcLoc {
......@@ -1836,6 +1851,8 @@ pub const LazySrcLoc = union(enum) {
18361851 .node_offset_bin_lhs,
18371852 .node_offset_bin_rhs,
18381853 .node_offset_switch_operand,
1854 .node_offset_switch_special_prong,
1855 .node_offset_switch_range,
18391856 => .{
18401857 .container = .{ .decl = scope.srcDecl().? },
18411858 .lazy = lazy,
......@@ -1876,6 +1893,8 @@ pub const LazySrcLoc = union(enum) {
18761893 .node_offset_bin_lhs,
18771894 .node_offset_bin_rhs,
18781895 .node_offset_switch_operand,
1896 .node_offset_switch_special_prong,
1897 .node_offset_switch_range,
18791898 => .{
18801899 .container = .{ .decl = decl },
18811900 .lazy = lazy,
src/RangeSet.zig+3
......@@ -44,6 +44,9 @@ fn lessThan(_: void, a: Range, b: Range) bool {
4444}
4545
4646pub fn spans(self: *RangeSet, start: Value, end: Value) !bool {
47 if (self.ranges.items.len == 0)
48 return false;
49
4750 std.sort.sort(Range, self.ranges.items, {}, lessThan);
4851
4952 if (!self.ranges.items[0].start.eql(start) or
src/Sema.zig+561-182
......@@ -59,6 +59,9 @@ const Scope = Module.Scope;
5959const InnerError = Module.InnerError;
6060const Decl = Module.Decl;
6161const LazySrcLoc = Module.LazySrcLoc;
62const RangeSet = @import("RangeSet.zig");
63
64const ValueSrcMap = std.HashMap(Value, LazySrcLoc, Value.hash, Value.eql, std.hash_map.DefaultMaxLoadPercentage);
6265
6366pub fn root(sema: *Sema, root_block: *Scope.Block) !zir.Inst.Index {
6467 const inst_data = sema.code.instructions.items(.data)[0].pl_node;
......@@ -242,18 +245,18 @@ pub fn analyzeBody(
242245 .ret_tok => return sema.zirRetTok(block, inst, false),
243246 .@"unreachable" => return sema.zirUnreachable(block, inst),
244247 .repeat => return sema.zirRepeat(block, inst),
245 .switch_br => return sema.zirSwitchBr(block, inst, false, .full),
246 .switch_br_range => return sema.zirSwitchBrRange(block, inst, false, .full),
248 .switch_br => return sema.zirSwitchBr(block, inst, false, .none),
249 .switch_br_multi => return sema.zirSwitchBrMulti(block, inst, false, .none),
247250 .switch_br_else => return sema.zirSwitchBr(block, inst, false, .@"else"),
248 .switch_br_else_range => return sema.zirSwitchBrRange(block, inst, false, .@"else"),
249 .switch_br_underscore => return sema.zirSwitchBr(block, inst, false, .under),
250 .switch_br_underscore_range => return sema.zirSwitchBrRange(block, inst, false, .under),
251 .switch_br_ref => return sema.zirSwitchBr(block, inst, true, .full),
252 .switch_br_ref_range => return sema.zirSwitchBrRange(block, inst, true, .full),
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),
253256 .switch_br_ref_else => return sema.zirSwitchBr(block, inst, true, .@"else"),
254 .switch_br_ref_else_range => return sema.zirSwitchBrRange(block, inst, true, .@"else"),
255 .switch_br_ref_underscore => return sema.zirSwitchBr(block, inst, true, .under),
256 .switch_br_ref_underscore_range => return sema.zirSwitchBrRange(block, inst, true, .under),
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),
257260
258261 // Instructions that we know can *never* be noreturn based solely on
259262 // 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
22052208 return sema.analyzeSlice(block, src, array_ptr, start, end, sentinel, sentinel_src);
22062209}
22072210
2208const ElseProng = enum { full, @"else", under };
2211const SpecialProng = enum { none, @"else", under };
22092212
22102213fn zirSwitchBr(
22112214 sema: *Sema,
22122215 block: *Scope.Block,
22132216 inst: zir.Inst.Index,
22142217 is_ref: bool,
2215 else_prong: ElseProng,
2218 special_prong: SpecialProng,
22162219) InnerError!zir.Inst.Index {
22172220 const tracy = trace(@src());
22182221 defer tracy.end();
......@@ -2228,15 +2231,23 @@ fn zirSwitchBr(
22282231 else
22292232 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 );
22322243}
22332244
2234fn zirSwitchBrRange(
2245fn zirSwitchBrMulti(
22352246 sema: *Sema,
22362247 block: *Scope.Block,
22372248 inst: zir.Inst.Index,
22382249 is_ref: bool,
2239 else_prong: ElseProng,
2250 special_prong: SpecialProng,
22402251) InnerError!zir.Inst.Index {
22412252 const tracy = trace(@src());
22422253 defer tracy.end();
......@@ -2244,7 +2255,7 @@ fn zirSwitchBrRange(
22442255 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
22452256 const src = inst_data.src();
22462257 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
22492260 const operand_ptr = try sema.resolveInst(extra.data.operand);
22502261 const operand = if (is_ref)
......@@ -2256,196 +2267,285 @@ fn zirSwitchBrRange(
22562267 block,
22572268 operand,
22582269 extra.end,
2259 else_prong,
2270 special_prong,
22602271 extra.data.scalar_cases_len,
22612272 extra.data.multi_cases_len,
2262 extra.data.range_cases_len,
2273 inst_data.src_node,
22632274 );
22642275}
22652276
22662277fn analyzeSwitch(
22672278 sema: *Sema,
2268 parent_block: *Scope.Block,
2279 block: *Scope.Block,
22692280 operand: *Inst,
22702281 extra_end: usize,
2271 else_prong: ElseProng,
2282 special_prong: SpecialProng,
22722283 scalar_cases_len: usize,
22732284 multi_cases_len: usize,
2274 range_cases_len: usize,
2285 src_node_offset: i32,
22752286) InnerError!zir.Inst.Index {
2276 if (true) @panic("TODO rework for zir-memory-layout branch");
2277
2278 try sema.validateSwitch(parent_block, operand, inst);
2279
2280 if (try sema.resolveDefinedValue(parent_block, inst.base.src, operand)) |target_val| {
2281 for (inst.positionals.cases) |case| {
2282 const resolved = try sema.resolveInst(case.item);
2283 const casted = try sema.coerce(block, operand.ty, resolved, resolved_src);
2284 const item = try sema.resolveConstValue(parent_block, case_src, casted);
2285
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,
2287 const special: struct { body: []const zir.Inst.Index, end: usize } = switch (special_prong) {
2288 .none => .{ .body = &.{}, .end = extra_end },
2289 .under, .@"else" => blk: {
2290 const body_len = sema.code.extra[extra_end];
2291 const extra_body_start = extra_end + 1;
2292 break :blk .{
2293 .body = sema.code.extra[extra_body_start..][0..body_len],
2294 .end = extra_body_start + body_len,
2295 };
2296 },
23112297 };
2312 defer case_block.instructions.deinit(sema.gpa);
23132298
2314 for (inst.positionals.cases) |case, i| {
2315 // Reset without freeing.
2316 case_block.instructions.items.len = 0;
2299 const src: LazySrcLoc = .{ .node_offset = src_node_offset };
2300 const special_prong_src: LazySrcLoc = .{ .node_offset_switch_special_prong = src_node_offset };
2301 const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = src_node_offset };
23172302
2318 const resolved = try sema.resolveInst(case.item);
2319 const casted = try sema.coerce(block, operand.ty, resolved, resolved_src);
2320 const item = try sema.resolveConstValue(parent_block, case_src, casted);
2321
2322 _ = try sema.analyzeBody(&case_block, case.body);
2323
2324 cases[i] = .{
2325 .item = item,
2326 .body = .{ .instructions = try sema.arena.dupe(*Inst, case_block.instructions.items) },
2303 // Validate usage of '_' prongs.
2304 if (special_prong == .under and !operand.ty.isExhaustiveEnum()) {
2305 const msg = msg: {
2306 const msg = try sema.mod.errMsg(
2307 &block.base,
2308 src,
2309 "'_' prong only allowed when switching on non-exhaustive enums",
2310 .{},
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;
23272321 };
2322 return sema.mod.failWithOwnedErrorMsg(&block.base, msg);
23282323 }
23292324
2330 case_block.instructions.items.len = 0;
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
2325 // Validate for duplicate items, missing else prong, and invalid range.
23592326 switch (operand.ty.zigTypeTag()) {
2360 .Enum => return sema.mod.fail(&block.base, inst.base.src, "TODO validateSwitch .Enum", .{}),
2361 .ErrorSet => return sema.mod.fail(&block.base, inst.base.src, "TODO validateSwitch .ErrorSet", .{}),
2362 .Union => return sema.mod.fail(&block.base, inst.base.src, "TODO validateSwitch .Union", .{}),
2327 .Enum => return sema.mod.fail(&block.base, src, "TODO validate switch .Enum", .{}),
2328 .ErrorSet => return sema.mod.fail(&block.base, src, "TODO validate switch .ErrorSet", .{}),
2329 .Union => return sema.mod.fail(&block.base, src, "TODO validate switch .Union", .{}),
23632330 .Int, .ComptimeInt => {
2364 var range_set = @import("RangeSet.zig").init(sema.gpa);
2331 var range_set = RangeSet.init(sema.gpa);
23652332 defer range_set.deinit();
23662333
2367 for (inst.positionals.items) |item| {
2368 const maybe_src = if (item.castTag(.switch_range)) |range| blk: {
2369 const start_resolved = try sema.resolveInst(range.positionals.lhs);
2370 const start_casted = try sema.coerce(block, operand.ty, start_resolved);
2371 const end_resolved = try sema.resolveInst(range.positionals.rhs);
2372 const end_casted = try sema.coerce(block, operand.ty, end_resolved);
2373
2374 break :blk try range_set.add(
2375 try sema.resolveConstValue(block, range_start_src, start_casted),
2376 try sema.resolveConstValue(block, range_end_src, end_casted),
2377 item.src,
2334 var extra_index: usize = special.end;
2335 {
2336 var scalar_i: usize = 0;
2337 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
2338 const item_ref = @intToEnum(zir.Inst.Ref, sema.code.extra[extra_index]);
2339 extra_index += 1;
2340 const body_len = sema.code.extra[extra_index];
2341 extra_index += 1;
2342 const body = sema.code.extra[extra_index..][0..body_len];
2343 extra_index += body_len;
2344
2345 try sema.validateSwitchItem(
2346 block,
2347 &range_set,
2348 item_ref,
2349 src_node_offset,
23782350 );
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
23892351 }
23902352 }
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) {
2393 var arena = std.heap.ArenaAllocator.init(sema.gpa);
2394 defer arena.deinit();
2395
2396 const start = try operand.ty.minInt(&arena, mod.getTarget());
2397 const end = try operand.ty.maxInt(&arena, mod.getTarget());
2398 if (try range_set.spans(start, end)) {
2399 if (inst.positionals.special_prong == .@"else") {
2400 return sema.mod.fail(&block.base, inst.base.src, "unreachable else prong, all cases already handled", .{});
2374 var range_i: usize = 0;
2375 while (range_i < ranges_len) : (range_i += 1) {
2376 const item_first = @intToEnum(zir.Inst.Ref, sema.code.extra[extra_index]);
2377 extra_index += 1;
2378 const item_last = @intToEnum(zir.Inst.Ref, sema.code.extra[extra_index]);
2379 extra_index += 1;
2380
2381 try sema.validateSwitchRange(
2382 block,
2383 &range_set,
2384 item_first,
2385 item_last,
2386 src_node_offset,
2387 );
24012388 }
2402 return;
2389
2390 extra_index += body_len;
24032391 }
24042392 }
24052393
2406 if (inst.positionals.special_prong != .@"else") {
2407 return sema.mod.fail(&block.base, inst.base.src, "switch must handle all possibilities", .{});
2394 check_range: {
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 }
24082421 }
24092422 },
24102423 .Bool => {
24112424 var true_count: u8 = 0;
24122425 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) {
2423 return sema.mod.fail(&block.base, item.src, "duplicate switch value", .{});
2427 var extra_index: usize = special.end;
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 );
24242445 }
24252446 }
2426 if ((true_count + false_count < 2) and inst.positionals.special_prong != .@"else") {
2427 return sema.mod.fail(&block.base, inst.base.src, "switch must handle all possibilities", .{});
2447 {
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 }
24282471 }
2429 if ((true_count + false_count == 2) and inst.positionals.special_prong == .@"else") {
2430 return sema.mod.fail(&block.base, inst.base.src, "unreachable else prong, all cases already handled", .{});
2472 switch (special_prong) {
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 },
24312493 }
24322494 },
24332495 .EnumLiteral, .Void, .Fn, .Pointer, .Type => {
2434 if (inst.positionals.special_prong != .@"else") {
2435 return sema.mod.fail(&block.base, inst.base.src, "else prong required when switching on type '{}'", .{operand.ty});
2496 if (special_prong != .@"else") {
2497 return sema.mod.fail(
2498 &block.base,
2499 src,
2500 "else prong required when switching on type '{}'",
2501 .{operand.ty},
2502 );
24362503 }
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);
24392506 defer seen_values.deinit();
24402507
2441 for (inst.positionals.items) |item| {
2442 const resolved = try sema.resolveInst(item);
2443 const casted = try sema.coerce(block, operand.ty, resolved);
2444 const val = try sema.resolveConstValue(block, item_src, casted);
2508 var extra_index: usize = special.end;
2509 {
2510 var scalar_i: usize = 0;
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| {
2447 return sema.mod.fail(&block.base, item.src, "duplicate switch value", .{});
2448 // TODO notes "previous value here" prev.value
2548 try sema.validateSwitchNoRange(block, ranges_len, operand.ty, src_node_offset);
24492549 }
24502550 }
24512551 },
......@@ -2464,10 +2564,298 @@ fn validateSwitch(sema: *Sema, block: *Scope.Block, operand: *Inst, inst: zir.In
24642564 .AnyFrame,
24652565 .ComptimeFloat,
24662566 .Float,
2467 => {
2468 return sema.mod.fail(&block.base, operand.src, "invalid switch operand type '{}'", .{operand.ty});
2469 },
2567 => return sema.mod.fail(&block.base, operand_src, "invalid switch operand type '{}'", .{
2568 operand.ty,
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;
24702769 }
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);
24712859}
24722860
24732861fn zirImport(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
......@@ -3095,30 +3483,21 @@ fn zirCondbr(
30953483 return always_noreturn;
30963484 }
30973485
3098 var true_block: Scope.Block = .{
3099 .parent = parent_block,
3100 .sema = sema,
3101 .src_decl = parent_block.src_decl,
3102 .instructions = .{},
3103 .inlining = parent_block.inlining,
3104 .is_comptime = parent_block.is_comptime,
3486 var sub_block = parent_block.makeSubBlock();
3487 defer sub_block.instructions.deinit(sema.gpa);
3488
3489 _ = try sema.analyzeBody(&sub_block, then_body);
3490 const tzir_then_body: ir.Body = .{
3491 .instructions = try sema.arena.dupe(*Inst, sub_block.instructions.items),
31053492 };
3106 defer true_block.instructions.deinit(sema.gpa);
3107 _ = try sema.analyzeBody(&true_block, then_body);
31083493
3109 var false_block: Scope.Block = .{
3110 .parent = parent_block,
3111 .sema = sema,
3112 .src_decl = parent_block.src_decl,
3113 .instructions = .{},
3114 .inlining = parent_block.inlining,
3115 .is_comptime = parent_block.is_comptime,
3494 sub_block.instructions.shrinkRetainingCapacity(0);
3495
3496 _ = try sema.analyzeBody(&sub_block, else_body);
3497 const tzir_else_body: ir.Body = .{
3498 .instructions = try sema.arena.dupe(*Inst, sub_block.instructions.items),
31163499 };
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) };
31223501 _ = try parent_block.addCondBr(src, cond, tzir_then_body, tzir_else_body);
31233502 return always_noreturn;
31243503}
src/type.zig+4
......@@ -3303,6 +3303,10 @@ pub const Type = extern union {
33033303 }
33043304 }
33053305
3306 pub fn isExhaustiveEnum(ty: Type) bool {
3307 return false; // TODO
3308 }
3309
33063310 /// This enum does not directly correspond to `std.builtin.TypeId` because
33073311 /// it has extra enum tags in it, as a way of using less memory. For example,
33083312 /// 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 {
589589 /// AST node is the switch, payload is `SwitchBr`.
590590 /// All prongs of target handled.
591591 switch_br,
592 /// Same as switch_br, except has a range field.
593 switch_br_range,
592 /// Same as switch_br, except one or more prongs have multiple items.
593 switch_br_multi,
594594 /// Same as switch_br, except has an else prong.
595595 switch_br_else,
596 /// Same as switch_br_else, except has a range field.
597 switch_br_else_range,
596 /// Same as switch_br_else, except one or more prongs have multiple items.
597 switch_br_else_multi,
598598 /// Same as switch_br, except has an underscore prong.
599 switch_br_underscore,
600 /// Same as switch_br, except has a range field.
601 switch_br_underscore_range,
599 switch_br_under,
600 /// Same as switch_br, except one or more prongs have multiple items.
601 switch_br_under_multi,
602602 /// Same as `switch_br` but the target is a pointer to the value being switched on.
603603 switch_br_ref,
604 /// Same as `switch_br_range` but the target is a pointer to the value being switched on.
605 switch_br_ref_range,
604 /// Same as `switch_br_multi` but the target is a pointer to the value being switched on.
605 switch_br_ref_multi,
606606 /// Same as `switch_br_else` but the target is a pointer to the value being switched on.
607607 switch_br_ref_else,
608 /// Same as `switch_br_else_range` but the target is a pointer to the
608 /// Same as `switch_br_else_multi` but the target is a pointer to the
609609 /// value being switched on.
610 switch_br_ref_else_range,
611 /// Same as `switch_br_underscore` but the target is a pointer to the value
610 switch_br_ref_else_multi,
611 /// Same as `switch_br_under` but the target is a pointer to the value
612612 /// being switched on.
613 switch_br_ref_underscore,
614 /// Same as `switch_br_underscore_range` but the target is a pointer to
613 switch_br_ref_under,
614 /// Same as `switch_br_under_multi` but the target is a pointer to
615615 /// the value being switched on.
616 switch_br_ref_underscore_range,
616 switch_br_ref_under_multi,
617617
618618 /// Returns whether the instruction is one of the control flow "noreturn" types.
619619 /// Function calls do not count.
......@@ -757,17 +757,17 @@ pub const Inst = struct {
757757 .repeat,
758758 .repeat_inline,
759759 .switch_br,
760 .switch_br_range,
760 .switch_br_multi,
761761 .switch_br_else,
762 .switch_br_else_range,
763 .switch_br_underscore,
764 .switch_br_underscore_range,
762 .switch_br_else_multi,
763 .switch_br_under,
764 .switch_br_under_multi,
765765 .switch_br_ref,
766 .switch_br_ref_range,
766 .switch_br_ref_multi,
767767 .switch_br_ref_else,
768 .switch_br_ref_else_range,
769 .switch_br_ref_underscore,
770 .switch_br_ref_underscore_range,
768 .switch_br_ref_else_multi,
769 .switch_br_ref_under,
770 .switch_br_ref_under_multi,
771771 => true,
772772 };
773773 }
......@@ -1333,7 +1333,7 @@ pub const Inst = struct {
13331333 /// This form is supported when there are no ranges, and exactly 1 item per block.
13341334 /// Depending on zir tag and len fields, extra fields trail
13351335 /// 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.
13371337 /// body_len: u32,
13381338 /// body member Index for every body_len
13391339 /// }
......@@ -1351,7 +1351,7 @@ pub const Inst = struct {
13511351 /// or a range.
13521352 /// Depending on zir tag and len fields, extra fields trail
13531353 /// 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.
13551355 /// body_len: u32,
13561356 /// body member Index for every body_len
13571357 /// }
......@@ -1362,19 +1362,19 @@ pub const Inst = struct {
13621362 /// }
13631363 /// 2. multi_cases: { // for every multi_cases_len
13641364 /// items_len: u32,
1365 /// item: Ref for every items_len
1366 /// block_index: u32, // index in extra to a `Block`
1367 /// }
1368 /// 3. range_cases: { // for every range_cases_len
1369 /// item_start: Ref,
1370 /// item_end: Ref,
1371 /// block_index: u32, // index in extra to a `Block`
1365 /// ranges_len: u32,
1366 /// body_len: u32,
1367 /// item: Ref // for every items_len
1368 /// ranges: { // for every ranges_len
1369 /// item_first: Ref,
1370 /// item_last: Ref,
1371 /// }
1372 /// body member Index for every body_len
13721373 /// }
1373 pub const SwitchBrRange = struct {
1374 pub const SwitchBrMulti = struct {
13741375 operand: Ref,
13751376 scalar_cases_len: u32,
13761377 multi_cases_len: u32,
1377 range_cases_len: u32,
13781378 };
13791379
13801380 pub const Field = struct {
......@@ -1544,19 +1544,19 @@ const Writer = struct {
15441544
15451545 .switch_br,
15461546 .switch_br_else,
1547 .switch_br_underscore,
1547 .switch_br_under,
15481548 .switch_br_ref,
15491549 .switch_br_ref_else,
1550 .switch_br_ref_underscore,
1550 .switch_br_ref_under,
15511551 => try self.writePlNodeSwitchBr(stream, inst),
15521552
1553 .switch_br_range,
1554 .switch_br_else_range,
1555 .switch_br_underscore_range,
1556 .switch_br_ref_range,
1557 .switch_br_ref_else_range,
1558 .switch_br_ref_underscore_range,
1559 => try self.writePlNodeSwitchBrRange(stream, inst),
1553 .switch_br_multi,
1554 .switch_br_else_multi,
1555 .switch_br_under_multi,
1556 .switch_br_ref_multi,
1557 .switch_br_ref_else_multi,
1558 .switch_br_ref_under_multi,
1559 => try self.writePlNodeSwitchBrMulti(stream, inst),
15601560
15611561 .compile_log,
15621562 .typeof_peer,
......@@ -1766,17 +1766,98 @@ const Writer = struct {
17661766 fn writePlNodeSwitchBr(self: *Writer, stream: anytype, inst: Inst.Index) !void {
17671767 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
17681768 const extra = self.code.extraData(Inst.SwitchBr, inst_data.payload_index);
1769
17701769 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(") ");
17721792 try self.writeSrc(stream, inst_data.src());
17731793 }
17741794
1775 fn writePlNodeSwitchBrRange(self: *Writer, stream: anytype, inst: Inst.Index) !void {
1795 fn writePlNodeSwitchBrMulti(self: *Writer, stream: anytype, inst: Inst.Index) !void {
17761796 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);
17781798 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(") ");
17801861 try self.writeSrc(stream, inst_data.src());
17811862 }
17821863