authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-03-30 23:57:22-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-03-30 23:57:22-07:00
log549af582e722c4798076acef7948a1f0d2896289
treee53fbf6b58f22943bc241f9149ba29260396954e
parent2a1dd174cdb3a084eef295613b03e94be4d843b9

AstGen: switch expressions properly handle result locations


3 files changed, 129 insertions(+), 17 deletions(-)

src/AstGen.zig+122-10
......@@ -2679,7 +2679,7 @@ fn switchExpr(
26792679 const operand_ty_inst = try parent_gz.addUnNode(typeof_tag, operand, operand_node);
26802680 const item_rl: ResultLoc = .{ .ty = operand_ty_inst };
26812681
2682 // Contains the data that goes into the `extra` array for the SwitchBr/SwitchBrMulti.
2682 // Contains the data that goes into the `extra` array for the SwitchBlock/SwitchBlockMulti.
26832683 // This is the header as well as the optional else prong body, as well as all the
26842684 // scalar cases.
26852685 // At the end we will memcpy this into place.
......@@ -2757,7 +2757,7 @@ fn switchExpr(
27572757 if (!astgen.refIsNoReturn(case_result)) {
27582758 _ = try case_scope.addBreak(.@"break", switch_block, case_result);
27592759 }
2760 // Documentation for this: `zir.Inst.SwitchBr` and `zir.Inst.SwitchBrMulti`.
2760 // Documentation for this: `zir.Inst.SwitchBlock` and `zir.Inst.SwitchBlockMulti`.
27612761 try scalar_cases_payload.ensureCapacity(gpa, scalar_cases_payload.items.len +
27622762 3 + // operand, scalar_cases_len, else body len
27632763 @boolToInt(multi_cases_len != 0) +
......@@ -2770,7 +2770,7 @@ fn switchExpr(
27702770 scalar_cases_payload.appendAssumeCapacity(@intCast(u32, case_scope.instructions.items.len));
27712771 scalar_cases_payload.appendSliceAssumeCapacity(case_scope.instructions.items);
27722772 } else {
2773 // Documentation for this: `zir.Inst.SwitchBr` and `zir.Inst.SwitchBrMulti`.
2773 // Documentation for this: `zir.Inst.SwitchBlock` and `zir.Inst.SwitchBlockMulti`.
27742774 try scalar_cases_payload.ensureCapacity(gpa, scalar_cases_payload.items.len +
27752775 2 + // operand, scalar_cases_len
27762776 @boolToInt(multi_cases_len != 0));
......@@ -2782,6 +2782,8 @@ fn switchExpr(
27822782 }
27832783
27842784 // In this pass we generate all the item and prong expressions except the special case.
2785 var multi_case_index: u32 = 0;
2786 var scalar_case_index: u32 = 0;
27852787 for (case_nodes) |case_node| {
27862788 if (case_node == special_node)
27872789 continue;
......@@ -2818,7 +2820,13 @@ fn switchExpr(
28182820 0b10 => .switch_capture_multi,
28192821 0b11 => .switch_capture_multi_ref,
28202822 };
2821 const capture_index = if (is_multi_case) multi_cases_len else scalar_cases_len;
2823 const capture_index = if (is_multi_case) ci: {
2824 multi_case_index += 1;
2825 break :ci multi_case_index - 1;
2826 } else ci: {
2827 scalar_case_index += 1;
2828 break :ci scalar_case_index - 1;
2829 };
28222830 const capture = try case_scope.add(.{
28232831 .tag = capture_tag,
28242832 .data = .{ .switch_capture = .{
......@@ -2918,17 +2926,121 @@ fn switchExpr(
29182926 else => unreachable,
29192927 };
29202928 const zir_datas = astgen.instructions.items(.data);
2921 zir_datas[switch_block].pl_node.payload_index = @intCast(u32, astgen.extra.items.len);
2929 const payload_index = astgen.extra.items.len;
2930 zir_datas[switch_block].pl_node.payload_index = @intCast(u32, payload_index);
29222931 try astgen.extra.ensureCapacity(gpa, astgen.extra.items.len +
29232932 scalar_cases_payload.items.len + multi_cases_payload.items.len);
29242933 astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items);
29252934 astgen.extra.appendSliceAssumeCapacity(multi_cases_payload.items);
29262935 const strat = rl.strategy(&block_scope);
2927 assert(strat.tag == .break_operand); // TODO
2928 assert(!strat.elide_store_to_block_ptr_instructions); // TODO
2929 assert(rl != .ref); // TODO
2930 const switch_block_ref = astgen.indexToRef(switch_block);
2931 return rvalue(parent_gz, scope, rl, switch_block_ref, switch_node);
2936 switch (strat.tag) {
2937 .break_operand => {
2938 // Switch expressions return `true` for `nodeMayNeedMemoryLocation` thus
2939 // this is always true.
2940 assert(strat.elide_store_to_block_ptr_instructions);
2941
2942 // Elide all the `store_to_block_ptr` instructions.
2943 var extra_index: usize = payload_index;
2944 extra_index += 2;
2945 extra_index += @boolToInt(multi_cases_len != 0);
2946 if (special_prong != .none) {
2947 const body_len = astgen.extra.items[extra_index];
2948 extra_index += 1;
2949 const body = astgen.extra.items[extra_index..][0..body_len];
2950 extra_index += body_len;
2951 const store_inst = body[body.len - 2];
2952 assert(zir_tags[store_inst] == .store_to_block_ptr);
2953 assert(zir_datas[store_inst].bin.lhs == block_scope.rl_ptr);
2954 zir_tags[store_inst] = .elided;
2955 zir_datas[store_inst] = undefined;
2956 }
2957 var scalar_i: u32 = 0;
2958 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
2959 extra_index += 1;
2960 const body_len = astgen.extra.items[extra_index];
2961 extra_index += 1;
2962 const body = astgen.extra.items[extra_index..][0..body_len];
2963 extra_index += body_len;
2964 const store_inst = body[body.len - 2];
2965 assert(zir_tags[store_inst] == .store_to_block_ptr);
2966 assert(zir_datas[store_inst].bin.lhs == block_scope.rl_ptr);
2967 zir_tags[store_inst] = .elided;
2968 zir_datas[store_inst] = undefined;
2969 }
2970 var multi_i: u32 = 0;
2971 while (multi_i < multi_cases_len) : (multi_i += 1) {
2972 const items_len = astgen.extra.items[extra_index];
2973 extra_index += 1;
2974 const ranges_len = astgen.extra.items[extra_index];
2975 extra_index += 1;
2976 const body_len = astgen.extra.items[extra_index];
2977 extra_index += 1;
2978 extra_index += items_len;
2979 extra_index += 2 * ranges_len;
2980 const body = astgen.extra.items[extra_index..][0..body_len];
2981 extra_index += body_len;
2982 const store_inst = body[body.len - 2];
2983 assert(zir_tags[store_inst] == .store_to_block_ptr);
2984 assert(zir_datas[store_inst].bin.lhs == block_scope.rl_ptr);
2985 zir_tags[store_inst] = .elided;
2986 zir_datas[store_inst] = undefined;
2987 }
2988
2989 const block_ref = astgen.indexToRef(switch_block);
2990 switch (rl) {
2991 .ref => return block_ref,
2992 else => return rvalue(parent_gz, scope, rl, block_ref, switch_node),
2993 }
2994 },
2995 .break_void => {
2996 assert(!strat.elide_store_to_block_ptr_instructions);
2997 // Modify all the terminating instruction tags to become `break` variants.
2998 var extra_index: usize = payload_index;
2999 extra_index += 2;
3000 extra_index += @boolToInt(multi_cases_len != 0);
3001 if (special_prong != .none) {
3002 const body_len = astgen.extra.items[extra_index];
3003 extra_index += 1;
3004 const body = astgen.extra.items[extra_index..][0..body_len];
3005 extra_index += body_len;
3006 const last = body[body.len - 1];
3007 assert(zir_tags[last] == .@"break");
3008 assert(zir_datas[last].@"break".block_inst == switch_block);
3009 zir_datas[last].@"break".operand = .void_value;
3010 }
3011 var scalar_i: u32 = 0;
3012 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
3013 extra_index += 1;
3014 const body_len = astgen.extra.items[extra_index];
3015 extra_index += 1;
3016 const body = astgen.extra.items[extra_index..][0..body_len];
3017 extra_index += body_len;
3018 const last = body[body.len - 1];
3019 assert(zir_tags[last] == .@"break");
3020 assert(zir_datas[last].@"break".block_inst == switch_block);
3021 zir_datas[last].@"break".operand = .void_value;
3022 }
3023 var multi_i: u32 = 0;
3024 while (multi_i < multi_cases_len) : (multi_i += 1) {
3025 const items_len = astgen.extra.items[extra_index];
3026 extra_index += 1;
3027 const ranges_len = astgen.extra.items[extra_index];
3028 extra_index += 1;
3029 const body_len = astgen.extra.items[extra_index];
3030 extra_index += 1;
3031 extra_index += items_len;
3032 extra_index += 2 * ranges_len;
3033 const body = astgen.extra.items[extra_index..][0..body_len];
3034 extra_index += body_len;
3035 const last = body[body.len - 1];
3036 assert(zir_tags[last] == .@"break");
3037 assert(zir_datas[last].@"break".block_inst == switch_block);
3038 zir_datas[last].@"break".operand = .void_value;
3039 }
3040
3041 return astgen.indexToRef(switch_block);
3042 },
3043 }
29323044}
29333045
29343046fn ret(gz: *GenZir, scope: *Scope, node: ast.Node.Index) InnerError!zir.Inst.Ref {
src/Sema.zig+2-2
......@@ -2255,7 +2255,7 @@ fn zirSwitchBlock(
22552255 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
22562256 const src = inst_data.src();
22572257 const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = inst_data.src_node };
2258 const extra = sema.code.extraData(zir.Inst.SwitchBr, inst_data.payload_index);
2258 const extra = sema.code.extraData(zir.Inst.SwitchBlock, inst_data.payload_index);
22592259
22602260 const operand_ptr = try sema.resolveInst(extra.data.operand);
22612261 const operand = if (is_ref)
......@@ -2288,7 +2288,7 @@ fn zirSwitchBlockMulti(
22882288 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
22892289 const src = inst_data.src();
22902290 const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = inst_data.src_node };
2291 const extra = sema.code.extraData(zir.Inst.SwitchBrMulti, inst_data.payload_index);
2291 const extra = sema.code.extraData(zir.Inst.SwitchBlockMulti, inst_data.payload_index);
22922292
22932293 const operand_ptr = try sema.resolveInst(extra.data.operand);
22942294 const operand = if (is_ref)
src/zir.zig+5-5
......@@ -589,7 +589,7 @@ pub const Inst = struct {
589589 /// Uses the `small_str` field.
590590 enum_literal_small,
591591 /// A switch expression. Uses the `pl_node` union field.
592 /// AST node is the switch, payload is `SwitchBr`.
592 /// AST node is the switch, payload is `SwitchBlock`.
593593 /// All prongs of target handled.
594594 switch_block,
595595 /// Same as switch_block, except one or more prongs have multiple items.
......@@ -1379,7 +1379,7 @@ pub const Inst = struct {
13791379 /// body_len: u32,
13801380 /// body member Index for every body_len
13811381 /// } for every cases_len
1382 pub const SwitchBr = struct {
1382 pub const SwitchBlock = struct {
13831383 operand: Ref,
13841384 cases_len: u32,
13851385 };
......@@ -1408,7 +1408,7 @@ pub const Inst = struct {
14081408 /// }
14091409 /// body member Index for every body_len
14101410 /// }
1411 pub const SwitchBrMulti = struct {
1411 pub const SwitchBlockMulti = struct {
14121412 operand: Ref,
14131413 scalar_cases_len: u32,
14141414 multi_cases_len: u32,
......@@ -1817,7 +1817,7 @@ const Writer = struct {
18171817 special_prong: SpecialProng,
18181818 ) !void {
18191819 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
1820 const extra = self.code.extraData(Inst.SwitchBr, inst_data.payload_index);
1820 const extra = self.code.extraData(Inst.SwitchBlock, inst_data.payload_index);
18211821 const special: struct {
18221822 body: []const Inst.Index,
18231823 end: usize,
......@@ -1881,7 +1881,7 @@ const Writer = struct {
18811881 special_prong: SpecialProng,
18821882 ) !void {
18831883 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
1884 const extra = self.code.extraData(Inst.SwitchBrMulti, inst_data.payload_index);
1884 const extra = self.code.extraData(Inst.SwitchBlockMulti, inst_data.payload_index);
18851885 const special: struct {
18861886 body: []const Inst.Index,
18871887 end: usize,