diff --git a/src/ir.cpp b/src/ir.cpp index 631e268f55..fb7613cf90 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -8111,6 +8111,7 @@ static IrInstSrc *ir_gen_while_expr(IrBuilderSrc *irb, Scope *scope, AstNode *no } else { payload_scope = subexpr_scope; } + ScopeExpr *spill_scope = create_expr_scope(irb->codegen, node, payload_scope); IrInstSrc *err_val_ptr = ir_gen_node_extra(irb, node->data.while_expr.condition, subexpr_scope, LValPtr, nullptr); if (err_val_ptr == irb->codegen->invalid_inst_src) @@ -8134,10 +8135,10 @@ static IrInstSrc *ir_gen_while_expr(IrBuilderSrc *irb, Scope *scope, AstNode *no ir_set_cursor_at_end_and_append_block(irb, body_block); if (var_symbol) { - IrInstSrc *payload_ptr = ir_build_unwrap_err_payload_src(irb, payload_scope, symbol_node, + IrInstSrc *payload_ptr = ir_build_unwrap_err_payload_src(irb, &spill_scope->base, symbol_node, err_val_ptr, false, false); IrInstSrc *var_ptr = node->data.while_expr.var_is_ptr ? - ir_build_ref_src(irb, payload_scope, symbol_node, payload_ptr, true, false) : payload_ptr; + ir_build_ref_src(irb, &spill_scope->base, symbol_node, payload_ptr, true, false) : payload_ptr; ir_build_var_decl_src(irb, payload_scope, symbol_node, payload_var, nullptr, var_ptr); } @@ -8152,6 +8153,7 @@ static IrInstSrc *ir_gen_while_expr(IrBuilderSrc *irb, Scope *scope, AstNode *no loop_scope->incoming_values = &incoming_values; loop_scope->lval = lval; loop_scope->peer_parent = peer_parent; + loop_scope->spill_scope = spill_scope; // Note the body block of the loop is not the place that lval and result_loc are used - // it's actually in break statements, handled similarly to return statements. @@ -8222,6 +8224,7 @@ static IrInstSrc *ir_gen_while_expr(IrBuilderSrc *irb, Scope *scope, AstNode *no ZigVar *payload_var = ir_create_var(irb, symbol_node, subexpr_scope, var_symbol, true, false, false, is_comptime); Scope *child_scope = payload_var->child_scope; + ScopeExpr *spill_scope = create_expr_scope(irb->codegen, node, child_scope); IrInstSrc *maybe_val_ptr = ir_gen_node_extra(irb, node->data.while_expr.condition, subexpr_scope, LValPtr, nullptr); if (maybe_val_ptr == irb->codegen->invalid_inst_src) @@ -8244,9 +8247,9 @@ static IrInstSrc *ir_gen_while_expr(IrBuilderSrc *irb, Scope *scope, AstNode *no is_comptime); ir_set_cursor_at_end_and_append_block(irb, body_block); - IrInstSrc *payload_ptr = ir_build_optional_unwrap_ptr(irb, child_scope, symbol_node, maybe_val_ptr, false, false); + IrInstSrc *payload_ptr = ir_build_optional_unwrap_ptr(irb, &spill_scope->base, symbol_node, maybe_val_ptr, false, false); IrInstSrc *var_ptr = node->data.while_expr.var_is_ptr ? - ir_build_ref_src(irb, child_scope, symbol_node, payload_ptr, true, false) : payload_ptr; + ir_build_ref_src(irb, &spill_scope->base, symbol_node, payload_ptr, true, false) : payload_ptr; ir_build_var_decl_src(irb, child_scope, symbol_node, payload_var, nullptr, var_ptr); ZigList incoming_values = {0}; @@ -8260,6 +8263,7 @@ static IrInstSrc *ir_gen_while_expr(IrBuilderSrc *irb, Scope *scope, AstNode *no loop_scope->incoming_values = &incoming_values; loop_scope->lval = lval; loop_scope->peer_parent = peer_parent; + loop_scope->spill_scope = spill_scope; // Note the body block of the loop is not the place that lval and result_loc are used - // it's actually in break statements, handled similarly to return statements. diff --git a/test/stage1/behavior/async_fn.zig b/test/stage1/behavior/async_fn.zig index 1b21ba7eff..87e7fca383 100644 --- a/test/stage1/behavior/async_fn.zig +++ b/test/stage1/behavior/async_fn.zig @@ -1182,6 +1182,42 @@ test "suspend in for loop" { S.doTheTest(); } +test "suspend in while loop" { + const S = struct { + var global_frame: ?anyframe = null; + + fn doTheTest() void { + _ = async atest(); + while (global_frame) |f| resume f; + } + + fn atest() void { + expect(optional(6) == 6); + expect(errunion(6) == 6); + } + fn optional(stuff: ?u32) u32 { + global_frame = @frame(); + defer global_frame = null; + while (stuff) |val| { + suspend; + return val; + } + return 0; + } + fn errunion(stuff: anyerror!u32) u32 { + global_frame = @frame(); + defer global_frame = null; + while (stuff) |val| { + suspend; + return val; + } else |err| { + return 0; + } + } + }; + S.doTheTest(); +} + test "correctly spill when returning the error union result of another async fn" { const S = struct { var global_frame: anyframe = undefined;