authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-03-28 18:28:08-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-03-28 18:28:08-07:00
log4dd65316b7d5756809394710078718b3ee536a9b
treee058370c2dc1a01693134a15a5fd8ca6cb7b550d
parent857743473ced48a493a713c0b86340ee110ce909

AstGen: coerce break operands of labeled blocks

Similar code was already in place for conditional branches. This updates AstGen to do the same for labeled blocks. It takes advantage of the `store_to_block_ptr` instructions by mutating them in place to become `as` instructions, coercing the break operands before they are returned from the block.

2 files changed, 39 insertions(+), 0 deletions(-)

src/AstGen.zig+27
...@@ -2015,6 +2015,7 @@ fn labeledBlockExpr(...@@ -2015,6 +2015,7 @@ fn labeledBlockExpr(
2015 }2015 }
20162016
2017 const zir_datas = gz.astgen.instructions.items(.data);2017 const zir_datas = gz.astgen.instructions.items(.data);
2018 const zir_tags = gz.astgen.instructions.items(.tag);
2018 const strat = rl.strategy(&block_scope);2019 const strat = rl.strategy(&block_scope);
2019 switch (strat.tag) {2020 switch (strat.tag) {
2020 .break_void => {2021 .break_void => {
...@@ -2029,6 +2030,31 @@ fn labeledBlockExpr(...@@ -2029,6 +2030,31 @@ fn labeledBlockExpr(
2029 },2030 },
2030 .break_operand => {2031 .break_operand => {
2031 // All break operands are values that did not use the result location pointer.2032 // All break operands are values that did not use the result location pointer.
2033 // The break instructions need to have their operands coerced if the
2034 // block's result location is a `ty`. In this case we overwrite the
2035 // `store_to_block_ptr` instruction with an `as` instruction and repurpose
2036 // it as the break operand.
2037 // This corresponds to similar code in `setCondBrPayloadElideBlockStorePtr`.
2038 if (block_scope.rl_ty_inst != .none) {
2039 for (block_scope.labeled_breaks.items) |br| {
2040 // We expect the `store_to_block_ptr` to be created between 1-3 instructions
2041 // prior to the break.
2042 var search_index = br -| 3;
2043 while (search_index < br) : (search_index += 1) {
2044 if (zir_tags[search_index] == .store_to_block_ptr and
2045 zir_datas[search_index].bin.lhs == block_scope.rl_ptr)
2046 {
2047 zir_tags[search_index] = .as;
2048 zir_datas[search_index].bin = .{
2049 .lhs = block_scope.rl_ty_inst,
2050 .rhs = zir_datas[br].@"break".operand,
2051 };
2052 zir_datas[br].@"break".operand = indexToRef(search_index);
2053 break;
2054 }
2055 } else unreachable;
2056 }
2057 }
2032 try block_scope.setBlockBody(block_inst);2058 try block_scope.setBlockBody(block_inst);
2033 const block_ref = indexToRef(block_inst);2059 const block_ref = indexToRef(block_inst);
2034 switch (rl) {2060 switch (rl) {
...@@ -5366,6 +5392,7 @@ fn setCondBrPayloadElideBlockStorePtr(...@@ -5366,6 +5392,7 @@ fn setCondBrPayloadElideBlockStorePtr(
5366 // switch's result location is a `ty`. In this case we overwrite the5392 // switch's result location is a `ty`. In this case we overwrite the
5367 // `store_to_block_ptr` instruction with an `as` instruction and repurpose5393 // `store_to_block_ptr` instruction with an `as` instruction and repurpose
5368 // it as the break operand.5394 // it as the break operand.
5395 // This corresponds to similar code in `labeledBlockExpr`.
5369 for (then_body) |src_inst| {5396 for (then_body) |src_inst| {
5370 if (zir_tags[src_inst] == .store_to_block_ptr and5397 if (zir_tags[src_inst] == .store_to_block_ptr and
5371 zir_datas[src_inst].bin.lhs == block_ptr)5398 zir_datas[src_inst].bin.lhs == block_ptr)
test/behavior/basic.zig+12
...@@ -875,3 +875,15 @@ test "catch in block has correct result location" {...@@ -875,3 +875,15 @@ test "catch in block has correct result location" {
875 };875 };
876 try expect(config_h_text == 1);876 try expect(config_h_text == 1);
877}877}
878
879test "labeled block with runtime branch forwards its result location type to break statements" {
880 const E = enum { a, b };
881 var a = false;
882 const e: E = blk: {
883 if (a) {
884 break :blk .a;
885 }
886 break :blk .b;
887 };
888 try expect(e == .b);
889}