authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2023-08-12 01:56:39-04:00
committergravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2023-08-12 02:22:26-04:00
log41575fa868f4b75b79bf5f774b66bb9a7b1ab142
treef7038b14c37cf43074f52001c2a151ace5796ee9
parentce7acf1296f7d38e6ee1b24e7aa85864198dd8fa

AstGen: fix src loc for invalid switch expression rls coercions


2 files changed, 148 insertions(+), 75 deletions(-)

src/AstGen.zig+102-75
......@@ -7178,7 +7178,8 @@ fn switchExpr(
71787178 try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.SwitchBlock).Struct.fields.len +
71797179 @intFromBool(multi_cases_len != 0) +
71807180 @intFromBool(any_has_tag_capture) +
7181 payloads.items.len - case_table_end);
7181 payloads.items.len - case_table_end +
7182 (case_table_end - case_table_start) * @typeInfo(Zir.Inst.As).Struct.fields.len);
71827183
71837184 const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.SwitchBlock{
71847185 .operand = raw_operand,
......@@ -7205,82 +7206,108 @@ fn switchExpr(
72057206 zir_datas[switch_block].pl_node.payload_index = payload_index;
72067207
72077208 const strat = ri.rl.strategy(&block_scope);
7208 for (payloads.items[case_table_start..case_table_end], 0..) |start_index, i| {
7209 var body_len_index = start_index;
7210 var end_index = start_index;
7211 const table_index = case_table_start + i;
7212 if (table_index < scalar_case_table) {
7213 end_index += 1;
7214 } else if (table_index < multi_case_table) {
7215 body_len_index += 1;
7216 end_index += 2;
7217 } else {
7218 body_len_index += 2;
7219 const items_len = payloads.items[start_index];
7220 const ranges_len = payloads.items[start_index + 1];
7221 end_index += 3 + items_len + 2 * ranges_len;
7222 }
7223
7224 const body_len = @as(Zir.Inst.SwitchBlock.ProngInfo, @bitCast(payloads.items[body_len_index])).body_len;
7225 end_index += body_len;
7226
7227 switch (strat.tag) {
7228 .break_operand => blk: {
7229 // Switch expressions return `true` for `nodeMayNeedMemoryLocation` thus
7230 // `elide_store_to_block_ptr_instructions` will either be true,
7231 // or all prongs are noreturn.
7232 if (!strat.elide_store_to_block_ptr_instructions)
7233 break :blk;
7234
7235 // There will necessarily be a store_to_block_ptr for
7236 // all prongs, except for prongs that ended with a noreturn instruction.
7237 // Elide all the `store_to_block_ptr` instructions.
7238
7239 // The break instructions need to have their operands coerced if the
7240 // switch's result location is a `ty`. In this case we overwrite the
7241 // `store_to_block_ptr` instruction with an `as` instruction and repurpose
7242 // it as the break operand.
7243 if (body_len < 2)
7244 break :blk;
7245
7246 var store_index = end_index - 2;
7247 while (true) : (store_index -= 1) switch (zir_tags[payloads.items[store_index]]) {
7248 .dbg_block_end, .dbg_block_begin, .dbg_stmt, .dbg_var_val, .dbg_var_ptr => {},
7249 else => break,
7250 };
7251 const store_inst = payloads.items[store_index];
7252 if (zir_tags[store_inst] != .store_to_block_ptr or
7253 zir_datas[store_inst].bin.lhs != block_scope.rl_ptr)
7254 break :blk;
7255 const break_inst = payloads.items[end_index - 1];
7256 if (block_scope.rl_ty_inst != .none) {
7257 zir_tags[store_inst] = .as;
7258 zir_datas[store_inst].bin = .{
7259 .lhs = block_scope.rl_ty_inst,
7260 .rhs = zir_datas[break_inst].@"break".operand,
7209 inline for (.{ .body, .breaks }) |pass| {
7210 for (payloads.items[case_table_start..case_table_end], 0..) |start_index, i| {
7211 var body_len_index = start_index;
7212 var end_index = start_index;
7213 const table_index = case_table_start + i;
7214 if (table_index < scalar_case_table) {
7215 end_index += 1;
7216 } else if (table_index < multi_case_table) {
7217 body_len_index += 1;
7218 end_index += 2;
7219 } else {
7220 body_len_index += 2;
7221 const items_len = payloads.items[start_index];
7222 const ranges_len = payloads.items[start_index + 1];
7223 end_index += 3 + items_len + 2 * ranges_len;
7224 }
7225
7226 const prong_info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(payloads.items[body_len_index]);
7227 end_index += prong_info.body_len;
7228
7229 switch (strat.tag) {
7230 .break_operand => blk: {
7231 // Switch expressions return `true` for `nodeMayNeedMemoryLocation` thus
7232 // `elide_store_to_block_ptr_instructions` will either be true,
7233 // or all prongs are noreturn.
7234 if (!strat.elide_store_to_block_ptr_instructions)
7235 break :blk;
7236
7237 // There will necessarily be a store_to_block_ptr for
7238 // all prongs, except for prongs that ended with a noreturn instruction.
7239 // Elide all the `store_to_block_ptr` instructions.
7240
7241 // The break instructions need to have their operands coerced if the
7242 // switch's result location is a `ty`. In this case we overwrite the
7243 // `store_to_block_ptr` instruction with an `as` instruction and repurpose
7244 // it as the break operand.
7245 if (prong_info.body_len < 2)
7246 break :blk;
7247
7248 var store_index = end_index - 2;
7249 while (true) : (store_index -= 1) switch (zir_tags[payloads.items[store_index]]) {
7250 .dbg_block_end, .dbg_block_begin, .dbg_stmt, .dbg_var_val, .dbg_var_ptr => {},
7251 else => break,
72617252 };
7262 zir_datas[break_inst].@"break".operand = indexToRef(store_inst);
7263 } else {
7264 payloads.items[body_len_index] -= 1;
7265 astgen.extra.appendSliceAssumeCapacity(payloads.items[start_index .. end_index - 2]);
7266 astgen.extra.appendAssumeCapacity(break_inst);
7267 continue;
7268 }
7269 },
7270 .break_void => {
7271 assert(!strat.elide_store_to_block_ptr_instructions);
7272 const last_inst = payloads.items[end_index - 1];
7273 if (zir_tags[last_inst] == .@"break") {
7274 const break_data = &zir_datas[last_inst].@"break";
7275 const block_inst = astgen.extra.items[
7276 break_data.payload_index + std.meta.fieldIndex(Zir.Inst.Break, "block_inst").?
7277 ];
7278 if (block_inst == switch_block) break_data.operand = .void_value;
7279 }
7280 },
7281 }
7253 const store_inst = payloads.items[store_index];
7254 if (zir_tags[store_inst] != .store_to_block_ptr or
7255 zir_datas[store_inst].bin.lhs != block_scope.rl_ptr)
7256 break :blk;
7257 const break_inst = payloads.items[end_index - 1];
7258 if (block_scope.rl_ty_inst != .none) {
7259 if (pass == .breaks) {
7260 const break_data = &zir_datas[break_inst].@"break";
7261 const break_src: i32 = @bitCast(astgen.extra.items[
7262 break_data.payload_index +
7263 std.meta.fieldIndex(Zir.Inst.Break, "operand_src_node").?
7264 ]);
7265 if (break_src == Zir.Inst.Break.no_src_node) {
7266 zir_tags[store_inst] = .as;
7267 zir_datas[store_inst].bin = .{
7268 .lhs = block_scope.rl_ty_inst,
7269 .rhs = break_data.operand,
7270 };
7271 } else {
7272 zir_tags[store_inst] = .as_node;
7273 zir_datas[store_inst] = .{ .pl_node = .{
7274 .src_node = break_src,
7275 .payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.As{
7276 .dest_type = block_scope.rl_ty_inst,
7277 .operand = break_data.operand,
7278 }),
7279 } };
7280 }
7281 break_data.operand = indexToRef(store_inst);
7282 }
7283 } else {
7284 if (pass == .body) {
7285 payloads.items[body_len_index] -= 1;
7286 astgen.extra.appendSliceAssumeCapacity(
7287 payloads.items[start_index .. end_index - 2],
7288 );
7289 astgen.extra.appendAssumeCapacity(break_inst);
7290 }
7291 continue;
7292 }
7293 },
7294 .break_void => if (pass == .breaks) {
7295 assert(!strat.elide_store_to_block_ptr_instructions);
7296 const last_inst = payloads.items[end_index - 1];
7297 if (zir_tags[last_inst] == .@"break") {
7298 const break_data = &zir_datas[last_inst].@"break";
7299 const block_inst = astgen.extra.items[
7300 break_data.payload_index +
7301 std.meta.fieldIndex(Zir.Inst.Break, "block_inst").?
7302 ];
7303 if (block_inst == switch_block) break_data.operand = .void_value;
7304 }
7305 },
7306 }
72827307
7283 astgen.extra.appendSliceAssumeCapacity(payloads.items[start_index..end_index]);
7308 if (pass == .body)
7309 astgen.extra.appendSliceAssumeCapacity(payloads.items[start_index..end_index]);
7310 }
72847311 }
72857312
72867313 const block_ref = indexToRef(switch_block);
test/cases/compile_errors/invalid_switch_expr_result_location_coercion.zig created+46
......@@ -0,0 +1,46 @@
1const Enum = enum(u8) { first, second, _ };
2
3export fn invalidFirstProng(enum_value: Enum) u8 {
4 const result: u8 = switch (enum_value) {
5 .first => 256,
6 .second => 0,
7 else => 0,
8 };
9 return result;
10}
11
12export fn invalidSecondProng(enum_value: Enum) u8 {
13 const result: u8 = switch (enum_value) {
14 .first => 0,
15 .second => 256,
16 _ => 0,
17 };
18 return result;
19}
20
21export fn invalidElseProng(enum_value: Enum) u8 {
22 const result: u8 = switch (enum_value) {
23 .first => 0,
24 .second => 0,
25 else => 256,
26 };
27 return result;
28}
29
30export fn invalidNonExhaustiveProng(enum_value: Enum) u8 {
31 const result: u8 = switch (enum_value) {
32 .first => 0,
33 .second => 0,
34 _ => 256,
35 };
36 return result;
37}
38
39// error
40// backend=stage2
41// target=native
42//
43// :5:19: error: type 'u8' cannot represent integer value '256'
44// :15:20: error: type 'u8' cannot represent integer value '256'
45// :25:17: error: type 'u8' cannot represent integer value '256'
46// :34:14: error: type 'u8' cannot represent integer value '256'