| ... | ... | @@ -1709,23 +1709,42 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn |
| 1709 | 1709 | return Zir.Inst.Ref.unreachable_value; |
| 1710 | 1710 | } |
| 1711 | 1711 | block_gz.break_count += 1; |
| 1712 | | const operand = try expr(parent_gz, parent_scope, block_gz.break_result_loc, rhs); |
| 1713 | | // if list grew as much as rvalue_rl_count, then a break inside operand already saved the store_to_block_ptr |
| 1714 | | const have_store_to_block = block_gz.rvalue_rl_count > block_gz.labeled_store_to_block_ptr_list.items.len; |
| 1715 | | |
| 1716 | | const br = try parent_gz.addBreak(.@"break", block_inst, operand); |
| 1717 | | |
| 1718 | | if (block_gz.break_result_loc == .block_ptr) { |
| 1719 | | try block_gz.labeled_breaks.append(astgen.gpa, br); |
| 1720 | | |
| 1721 | | if (have_store_to_block) { |
| 1722 | | const zir_tags = parent_gz.astgen.instructions.items(.tag); |
| 1723 | | const zir_datas = parent_gz.astgen.instructions.items(.data); |
| 1724 | | const store_inst = @intCast(u32, zir_tags.len - 2); |
| 1725 | | assert(zir_tags[store_inst] == .store_to_block_ptr); |
| 1726 | | assert(zir_datas[store_inst].bin.lhs == block_gz.rl_ptr); |
| 1727 | | try block_gz.labeled_store_to_block_ptr_list.append(astgen.gpa, store_inst); |
| 1728 | | } |
| 1712 | |
| 1713 | // The loop scope has a mechanism to prevent rvalue() from emitting a |
| 1714 | // store to the result location for the loop body (since it is continues |
| 1715 | // rather than returning a result from the loop) but here is a `break` |
| 1716 | // which needs to override this behavior. |
| 1717 | const prev_rvalue_noresult = parent_gz.rvalue_noresult; |
| 1718 | parent_gz.rvalue_noresult = .none; |
| 1719 | const operand = try reachableExpr(parent_gz, parent_scope, block_gz.break_result_loc, rhs, node); |
| 1720 | parent_gz.rvalue_noresult = prev_rvalue_noresult; |
| 1721 | |
| 1722 | switch (block_gz.break_result_loc) { |
| 1723 | .block_ptr => { |
| 1724 | const br = try parent_gz.addBreak(.@"break", block_inst, operand); |
| 1725 | try block_gz.labeled_breaks.append(astgen.gpa, br); |
| 1726 | |
| 1727 | // if list grew as much as rvalue_rl_count, then a break |
| 1728 | // inside operand already saved the store_to_block_ptr |
| 1729 | const have_store_to_block = block_gz.rvalue_rl_count > |
| 1730 | block_gz.labeled_store_to_block_ptr_list.items.len; |
| 1731 | if (have_store_to_block) { |
| 1732 | const zir_tags = parent_gz.astgen.instructions.items(.tag); |
| 1733 | const zir_datas = parent_gz.astgen.instructions.items(.data); |
| 1734 | const store_inst = @intCast(u32, zir_tags.len - 2); |
| 1735 | assert(zir_tags[store_inst] == .store_to_block_ptr); |
| 1736 | assert(zir_datas[store_inst].bin.lhs == block_gz.rl_ptr); |
| 1737 | try block_gz.labeled_store_to_block_ptr_list.append(astgen.gpa, store_inst); |
| 1738 | } |
| 1739 | }, |
| 1740 | .ptr => { |
| 1741 | // In this case we don't have any mechanism to intercept it; |
| 1742 | // we assume the result location is written, and we break with void. |
| 1743 | _ = try parent_gz.addBreak(.@"break", block_inst, .void_value); |
| 1744 | }, |
| 1745 | else => { |
| 1746 | _ = try parent_gz.addBreak(.@"break", block_inst, operand); |
| 1747 | }, |
| 1729 | 1748 | } |
| 1730 | 1749 | return Zir.Inst.Ref.unreachable_value; |
| 1731 | 1750 | }, |
| ... | ... | @@ -4776,7 +4795,7 @@ fn arrayAccess( |
| 4776 | 4795 | else => return rvalue(gz, rl, try gz.addBin( |
| 4777 | 4796 | .elem_val, |
| 4778 | 4797 | try expr(gz, scope, .none, node_datas[node].lhs), |
| 4779 | | try expr(gz, scope, .{ .ty = .usize_type }, node_datas[node].rhs), |
| 4798 | try expr(gz, scope, .{ .coerced_ty = .usize_type }, node_datas[node].rhs), |
| 4780 | 4799 | ), node), |
| 4781 | 4800 | } |
| 4782 | 4801 | } |
| ... | ... | @@ -5183,6 +5202,7 @@ fn whileExpr( |
| 5183 | 5202 | // make scope now but don't stack on parent_gz until loop_scope |
| 5184 | 5203 | // gets unstacked after cont_expr is emitted and added below |
| 5185 | 5204 | var then_scope = parent_gz.makeSubBlock(&continue_scope.base); |
| 5205 | then_scope.markAsLoopBody(loop_scope); |
| 5186 | 5206 | then_scope.instructions_top = GenZir.unstacked_top; |
| 5187 | 5207 | defer then_scope.unstack(); |
| 5188 | 5208 | |
| ... | ... | @@ -5267,9 +5287,6 @@ fn whileExpr( |
| 5267 | 5287 | then_scope.instructions_top = then_scope.instructions.items.len; |
| 5268 | 5288 | if (payload_inst != 0) try then_scope.instructions.append(astgen.gpa, payload_inst); |
| 5269 | 5289 | const then_result = try expr(&then_scope, then_sub_scope, loop_scope.break_result_loc, while_full.ast.then_expr); |
| 5270 | | if (!then_scope.endsWithNoReturn()) { |
| 5271 | | loop_scope.break_count += 1; |
| 5272 | | } |
| 5273 | 5290 | try checkUsed(parent_gz, &then_scope.base, then_sub_scope); |
| 5274 | 5291 | |
| 5275 | 5292 | var else_scope = parent_gz.makeSubBlock(&continue_scope.base); |
| ... | ... | @@ -5426,6 +5443,7 @@ fn forExpr( |
| 5426 | 5443 | } |
| 5427 | 5444 | |
| 5428 | 5445 | var then_scope = parent_gz.makeSubBlock(&cond_scope.base); |
| 5446 | then_scope.markAsLoopBody(loop_scope); |
| 5429 | 5447 | defer then_scope.unstack(); |
| 5430 | 5448 | |
| 5431 | 5449 | var payload_val_scope: Scope.LocalVal = undefined; |
| ... | ... | @@ -5482,9 +5500,6 @@ fn forExpr( |
| 5482 | 5500 | }; |
| 5483 | 5501 | |
| 5484 | 5502 | const then_result = try expr(&then_scope, then_sub_scope, loop_scope.break_result_loc, for_full.ast.then_expr); |
| 5485 | | if (!then_scope.endsWithNoReturn()) { |
| 5486 | | loop_scope.break_count += 1; |
| 5487 | | } |
| 5488 | 5503 | try checkUsed(parent_gz, &then_scope.base, then_sub_scope); |
| 5489 | 5504 | |
| 5490 | 5505 | var else_scope = parent_gz.makeSubBlock(&cond_scope.base); |
| ... | ... | @@ -8369,19 +8384,25 @@ fn rvalue( |
| 8369 | 8384 | } |
| 8370 | 8385 | }, |
| 8371 | 8386 | .ptr => |ptr_inst| { |
| 8372 | | _ = try gz.addPlNode(.store_node, src_node, Zir.Inst.Bin{ |
| 8373 | | .lhs = ptr_inst, |
| 8374 | | .rhs = result, |
| 8375 | | }); |
| 8387 | if (gz.rvalue_noresult != ptr_inst) { |
| 8388 | _ = try gz.addPlNode(.store_node, src_node, Zir.Inst.Bin{ |
| 8389 | .lhs = ptr_inst, |
| 8390 | .rhs = result, |
| 8391 | }); |
| 8392 | } |
| 8376 | 8393 | return result; |
| 8377 | 8394 | }, |
| 8378 | 8395 | .inferred_ptr => |alloc| { |
| 8379 | | _ = try gz.addBin(.store_to_inferred_ptr, alloc, result); |
| 8396 | if (gz.rvalue_noresult != alloc) { |
| 8397 | _ = try gz.addBin(.store_to_inferred_ptr, alloc, result); |
| 8398 | } |
| 8380 | 8399 | return result; |
| 8381 | 8400 | }, |
| 8382 | 8401 | .block_ptr => |block_scope| { |
| 8383 | | block_scope.rvalue_rl_count += 1; |
| 8384 | | _ = try gz.addBin(.store_to_block_ptr, block_scope.rl_ptr, result); |
| 8402 | if (gz.rvalue_noresult != block_scope.rl_ptr) { |
| 8403 | block_scope.rvalue_rl_count += 1; |
| 8404 | _ = try gz.addBin(.store_to_block_ptr, block_scope.rl_ptr, result); |
| 8405 | } |
| 8385 | 8406 | return result; |
| 8386 | 8407 | }, |
| 8387 | 8408 | } |
| ... | ... | @@ -8890,6 +8911,7 @@ const GenZir = struct { |
| 8890 | 8911 | rl_ptr: Zir.Inst.Ref = .none, |
| 8891 | 8912 | /// When a block has a type result location, here it is. |
| 8892 | 8913 | rl_ty_inst: Zir.Inst.Ref = .none, |
| 8914 | rvalue_noresult: Zir.Inst.Ref = .none, |
| 8893 | 8915 | /// Keeps track of how many branches of a block did not actually |
| 8894 | 8916 | /// consume the result location. astgen uses this to figure out |
| 8895 | 8917 | /// whether to rely on break instructions or writing to the result |
| ... | ... | @@ -10096,6 +10118,17 @@ const GenZir = struct { |
| 10096 | 10118 | } |
| 10097 | 10119 | } |
| 10098 | 10120 | } |
| 10121 | |
| 10122 | /// Control flow does not fall through the "then" block of a loop; it continues |
| 10123 | /// back to the while condition. This prevents `rvalue` from |
| 10124 | /// adding an invalid store to the result location of `then_scope`. |
| 10125 | fn markAsLoopBody(gz: *GenZir, loop_scope: GenZir) void { |
| 10126 | gz.rvalue_noresult = switch (loop_scope.break_result_loc) { |
| 10127 | .ptr, .inferred_ptr => |ptr| ptr, |
| 10128 | .block_ptr => |block| block.rl_ptr, |
| 10129 | else => .none, |
| 10130 | }; |
| 10131 | } |
| 10099 | 10132 | }; |
| 10100 | 10133 | |
| 10101 | 10134 | /// This can only be for short-lived references; the memory becomes invalidated |