| ... | @@ -1709,23 +1709,42 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn | ... | @@ -1709,23 +1709,42 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn |
| 1709 | return Zir.Inst.Ref.unreachable_value; | 1709 | return Zir.Inst.Ref.unreachable_value; |
| 1710 | } | 1710 | } |
| 1711 | block_gz.break_count += 1; | 1711 | block_gz.break_count += 1; |
| 1712 | const operand = try expr(parent_gz, parent_scope, block_gz.break_result_loc, rhs); | 1712 | |
| 1713 | // if list grew as much as rvalue_rl_count, then a break inside operand already saved the store_to_block_ptr | 1713 | // The loop scope has a mechanism to prevent rvalue() from emitting a |
| 1714 | const have_store_to_block = block_gz.rvalue_rl_count > block_gz.labeled_store_to_block_ptr_list.items.len; | 1714 | // store to the result location for the loop body (since it is continues |
| 1715 | | 1715 | // rather than returning a result from the loop) but here is a `break` |
| 1716 | const br = try parent_gz.addBreak(.@"break", block_inst, operand); | 1716 | // which needs to override this behavior. |
| 1717 | | 1717 | const prev_rvalue_noresult = parent_gz.rvalue_noresult; |
| 1718 | if (block_gz.break_result_loc == .block_ptr) { | 1718 | parent_gz.rvalue_noresult = .none; |
| 1719 | try block_gz.labeled_breaks.append(astgen.gpa, br); | 1719 | const operand = try reachableExpr(parent_gz, parent_scope, block_gz.break_result_loc, rhs, node); |
| 1720 | | 1720 | parent_gz.rvalue_noresult = prev_rvalue_noresult; |
| 1721 | if (have_store_to_block) { | 1721 | |
| 1722 | const zir_tags = parent_gz.astgen.instructions.items(.tag); | 1722 | switch (block_gz.break_result_loc) { |
| 1723 | const zir_datas = parent_gz.astgen.instructions.items(.data); | 1723 | .block_ptr => { |
| 1724 | const store_inst = @intCast(u32, zir_tags.len - 2); | 1724 | const br = try parent_gz.addBreak(.@"break", block_inst, operand); |
| 1725 | assert(zir_tags[store_inst] == .store_to_block_ptr); | 1725 | try block_gz.labeled_breaks.append(astgen.gpa, br); |
| 1726 | assert(zir_datas[store_inst].bin.lhs == block_gz.rl_ptr); | 1726 | |
| 1727 | try block_gz.labeled_store_to_block_ptr_list.append(astgen.gpa, store_inst); | 1727 | // if list grew as much as rvalue_rl_count, then a break |
| 1728 | } | 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 | return Zir.Inst.Ref.unreachable_value; | 1749 | return Zir.Inst.Ref.unreachable_value; |
| 1731 | }, | 1750 | }, |
| ... | @@ -4776,7 +4795,7 @@ fn arrayAccess( | ... | @@ -4776,7 +4795,7 @@ fn arrayAccess( |
| 4776 | else => return rvalue(gz, rl, try gz.addBin( | 4795 | else => return rvalue(gz, rl, try gz.addBin( |
| 4777 | .elem_val, | 4796 | .elem_val, |
| 4778 | try expr(gz, scope, .none, node_datas[node].lhs), | 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 | ), node), | 4799 | ), node), |
| 4781 | } | 4800 | } |
| 4782 | } | 4801 | } |
| ... | @@ -5183,6 +5202,7 @@ fn whileExpr( | ... | @@ -5183,6 +5202,7 @@ fn whileExpr( |
| 5183 | // make scope now but don't stack on parent_gz until loop_scope | 5202 | // make scope now but don't stack on parent_gz until loop_scope |
| 5184 | // gets unstacked after cont_expr is emitted and added below | 5203 | // gets unstacked after cont_expr is emitted and added below |
| 5185 | var then_scope = parent_gz.makeSubBlock(&continue_scope.base); | 5204 | var then_scope = parent_gz.makeSubBlock(&continue_scope.base); |
| | 5205 | then_scope.markAsLoopBody(loop_scope); |
| 5186 | then_scope.instructions_top = GenZir.unstacked_top; | 5206 | then_scope.instructions_top = GenZir.unstacked_top; |
| 5187 | defer then_scope.unstack(); | 5207 | defer then_scope.unstack(); |
| 5188 | | 5208 | |
| ... | @@ -5267,9 +5287,6 @@ fn whileExpr( | ... | @@ -5267,9 +5287,6 @@ fn whileExpr( |
| 5267 | then_scope.instructions_top = then_scope.instructions.items.len; | 5287 | then_scope.instructions_top = then_scope.instructions.items.len; |
| 5268 | if (payload_inst != 0) try then_scope.instructions.append(astgen.gpa, payload_inst); | 5288 | if (payload_inst != 0) try then_scope.instructions.append(astgen.gpa, payload_inst); |
| 5269 | const then_result = try expr(&then_scope, then_sub_scope, loop_scope.break_result_loc, while_full.ast.then_expr); | 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 | try checkUsed(parent_gz, &then_scope.base, then_sub_scope); | 5290 | try checkUsed(parent_gz, &then_scope.base, then_sub_scope); |
| 5274 | | 5291 | |
| 5275 | var else_scope = parent_gz.makeSubBlock(&continue_scope.base); | 5292 | var else_scope = parent_gz.makeSubBlock(&continue_scope.base); |
| ... | @@ -5426,6 +5443,7 @@ fn forExpr( | ... | @@ -5426,6 +5443,7 @@ fn forExpr( |
| 5426 | } | 5443 | } |
| 5427 | | 5444 | |
| 5428 | var then_scope = parent_gz.makeSubBlock(&cond_scope.base); | 5445 | var then_scope = parent_gz.makeSubBlock(&cond_scope.base); |
| | 5446 | then_scope.markAsLoopBody(loop_scope); |
| 5429 | defer then_scope.unstack(); | 5447 | defer then_scope.unstack(); |
| 5430 | | 5448 | |
| 5431 | var payload_val_scope: Scope.LocalVal = undefined; | 5449 | var payload_val_scope: Scope.LocalVal = undefined; |
| ... | @@ -5482,9 +5500,6 @@ fn forExpr( | ... | @@ -5482,9 +5500,6 @@ fn forExpr( |
| 5482 | }; | 5500 | }; |
| 5483 | | 5501 | |
| 5484 | const then_result = try expr(&then_scope, then_sub_scope, loop_scope.break_result_loc, for_full.ast.then_expr); | 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 | try checkUsed(parent_gz, &then_scope.base, then_sub_scope); | 5503 | try checkUsed(parent_gz, &then_scope.base, then_sub_scope); |
| 5489 | | 5504 | |
| 5490 | var else_scope = parent_gz.makeSubBlock(&cond_scope.base); | 5505 | var else_scope = parent_gz.makeSubBlock(&cond_scope.base); |
| ... | @@ -8369,19 +8384,25 @@ fn rvalue( | ... | @@ -8369,19 +8384,25 @@ fn rvalue( |
| 8369 | } | 8384 | } |
| 8370 | }, | 8385 | }, |
| 8371 | .ptr => |ptr_inst| { | 8386 | .ptr => |ptr_inst| { |
| 8372 | _ = try gz.addPlNode(.store_node, src_node, Zir.Inst.Bin{ | 8387 | if (gz.rvalue_noresult != ptr_inst) { |
| 8373 | .lhs = ptr_inst, | 8388 | _ = try gz.addPlNode(.store_node, src_node, Zir.Inst.Bin{ |
| 8374 | .rhs = result, | 8389 | .lhs = ptr_inst, |
| 8375 | }); | 8390 | .rhs = result, |
| | 8391 | }); |
| | 8392 | } |
| 8376 | return result; | 8393 | return result; |
| 8377 | }, | 8394 | }, |
| 8378 | .inferred_ptr => |alloc| { | 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 | return result; | 8399 | return result; |
| 8381 | }, | 8400 | }, |
| 8382 | .block_ptr => |block_scope| { | 8401 | .block_ptr => |block_scope| { |
| 8383 | block_scope.rvalue_rl_count += 1; | 8402 | if (gz.rvalue_noresult != block_scope.rl_ptr) { |
| 8384 | _ = try gz.addBin(.store_to_block_ptr, block_scope.rl_ptr, result); | 8403 | block_scope.rvalue_rl_count += 1; |
| | 8404 | _ = try gz.addBin(.store_to_block_ptr, block_scope.rl_ptr, result); |
| | 8405 | } |
| 8385 | return result; | 8406 | return result; |
| 8386 | }, | 8407 | }, |
| 8387 | } | 8408 | } |
| ... | @@ -8890,6 +8911,7 @@ const GenZir = struct { | ... | @@ -8890,6 +8911,7 @@ const GenZir = struct { |
| 8890 | rl_ptr: Zir.Inst.Ref = .none, | 8911 | rl_ptr: Zir.Inst.Ref = .none, |
| 8891 | /// When a block has a type result location, here it is. | 8912 | /// When a block has a type result location, here it is. |
| 8892 | rl_ty_inst: Zir.Inst.Ref = .none, | 8913 | rl_ty_inst: Zir.Inst.Ref = .none, |
| | 8914 | rvalue_noresult: Zir.Inst.Ref = .none, |
| 8893 | /// Keeps track of how many branches of a block did not actually | 8915 | /// Keeps track of how many branches of a block did not actually |
| 8894 | /// consume the result location. astgen uses this to figure out | 8916 | /// consume the result location. astgen uses this to figure out |
| 8895 | /// whether to rely on break instructions or writing to the result | 8917 | /// whether to rely on break instructions or writing to the result |
| ... | @@ -10096,6 +10118,17 @@ const GenZir = struct { | ... | @@ -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 | /// This can only be for short-lived references; the memory becomes invalidated | 10134 | /// This can only be for short-lived references; the memory becomes invalidated |