Commit 17199b0879

Andrew Kelley <andrew@ziglang.org>
2019-08-07 00:29:56
passing the error return trace async function test
1 parent 400500a
Changed files (5)
src/all_types.hpp
@@ -1718,7 +1718,7 @@ struct CodeGen {
     LLVMTargetMachineRef target_machine;
     ZigLLVMDIFile *dummy_di_file;
     LLVMValueRef cur_ret_ptr;
-    LLVMValueRef cur_ret_ptr_ptr;
+    LLVMValueRef cur_frame_ptr;
     LLVMValueRef cur_fn_val;
     LLVMValueRef cur_async_switch_instr;
     LLVMValueRef cur_async_resume_index_ptr;
src/analyze.cpp
@@ -5160,6 +5160,8 @@ static ZigType *get_async_fn_type(CodeGen *g, ZigType *orig_fn_type) {
 }
 
 static Error resolve_coro_frame(CodeGen *g, ZigType *frame_type) {
+    Error err;
+
     if (frame_type->data.frame.locals_struct != nullptr)
         return ErrorNone;
 
@@ -5286,6 +5288,9 @@ static Error resolve_coro_frame(CodeGen *g, ZigType *frame_type) {
                 continue;
             }
         }
+        if ((err = type_resolve(g, child_type, ResolveStatusSizeKnown))) {
+            return err;
+        }
         const char *name;
         if (*instruction->name_hint == 0) {
             name = buf_ptr(buf_sprintf("@local%" ZIG_PRI_usize, alloca_i));
src/codegen.cpp
@@ -2088,17 +2088,19 @@ static LLVMValueRef gen_resume(CodeGen *g, LLVMValueRef fn_val, LLVMValueRef tar
 static LLVMValueRef ir_render_return_begin(CodeGen *g, IrExecutable *executable,
         IrInstructionReturnBegin *instruction)
 {
-    if (!fn_is_async(g->cur_fn)) return nullptr;
+    bool ret_type_has_bits = instruction->operand != nullptr &&
+        type_has_bits(instruction->operand->value.type);
+
+    if (!fn_is_async(g->cur_fn)) {
+        return ret_type_has_bits ? ir_llvm_value(g, instruction->operand) : nullptr;
+    }
 
     LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->llvm_type;
 
-    bool ret_type_has_bits = instruction->operand != nullptr &&
-        type_has_bits(instruction->operand->value.type);
     ZigType *ret_type = ret_type_has_bits ? instruction->operand->value.type : nullptr;
     if (ret_type_has_bits && !handle_is_ptr(ret_type)) {
         // It's a scalar, so it didn't get written to the result ptr. Do that before the atomic rmw.
-        LLVMValueRef result_ptr = LLVMBuildLoad(g->builder, g->cur_ret_ptr_ptr, "");
-        LLVMBuildStore(g->builder, ir_llvm_value(g, instruction->operand), result_ptr);
+        LLVMBuildStore(g->builder, ir_llvm_value(g, instruction->operand), g->cur_ret_ptr);
     }
 
     // Prepare to be suspended. We might end up not having to suspend though.
@@ -2147,7 +2149,11 @@ static LLVMValueRef ir_render_return_begin(CodeGen *g, IrExecutable *executable,
     LLVMBasicBlockRef incoming_blocks[] = { after_resume_block, switch_bb };
     LLVMAddIncoming(g->cur_async_prev_val, incoming_values, incoming_blocks, 2);
 
-    return nullptr;
+    if (!ret_type_has_bits) {
+        return nullptr;
+    }
+
+    return get_handle_value(g, g->cur_ret_ptr, ret_type, get_pointer_to_type(g, ret_type, true));
 }
 
 static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable, IrInstructionReturn *instruction) {
@@ -2166,17 +2172,16 @@ static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable, IrIns
             // If the awaiter result pointer is non-null, we need to copy the result to there.
             LLVMBasicBlockRef copy_block = LLVMAppendBasicBlock(g->cur_fn_val, "CopyResult");
             LLVMBasicBlockRef copy_end_block = LLVMAppendBasicBlock(g->cur_fn_val, "CopyResultEnd");
-            LLVMValueRef awaiter_ret_ptr_ptr = LLVMBuildStructGEP(g->builder, g->cur_ret_ptr, coro_ret_start + 1, "");
+            LLVMValueRef awaiter_ret_ptr_ptr = LLVMBuildStructGEP(g->builder, g->cur_frame_ptr, coro_ret_start + 1, "");
             LLVMValueRef awaiter_ret_ptr = LLVMBuildLoad(g->builder, awaiter_ret_ptr_ptr, "");
             LLVMValueRef zero_ptr = LLVMConstNull(LLVMTypeOf(awaiter_ret_ptr));
             LLVMValueRef need_copy_bit = LLVMBuildICmp(g->builder, LLVMIntNE, awaiter_ret_ptr, zero_ptr, "");
             LLVMBuildCondBr(g->builder, need_copy_bit, copy_block, copy_end_block);
 
             LLVMPositionBuilderAtEnd(g->builder, copy_block);
-            LLVMValueRef ret_ptr = LLVMBuildLoad(g->builder, g->cur_ret_ptr_ptr, "");
             LLVMTypeRef ptr_u8 = LLVMPointerType(LLVMInt8Type(), 0);
             LLVMValueRef dest_ptr_casted = LLVMBuildBitCast(g->builder, awaiter_ret_ptr, ptr_u8, "");
-            LLVMValueRef src_ptr_casted = LLVMBuildBitCast(g->builder, ret_ptr, ptr_u8, "");
+            LLVMValueRef src_ptr_casted = LLVMBuildBitCast(g->builder, g->cur_ret_ptr, ptr_u8, "");
             bool is_volatile = false;
             uint32_t abi_align = get_abi_alignment(g, ret_type);
             LLVMValueRef byte_count_val = LLVMConstInt(usize_type_ref, type_size(g, ret_type), false);
@@ -3385,10 +3390,6 @@ static LLVMValueRef ir_render_return_ptr(CodeGen *g, IrExecutable *executable,
     if (!type_has_bits(instruction->base.value.type))
         return nullptr;
     src_assert(g->cur_ret_ptr != nullptr, instruction->base.source_node);
-    if (fn_is_async(g->cur_fn)) {
-        LLVMValueRef ptr_ptr = LLVMBuildStructGEP(g->builder, g->cur_ret_ptr, coro_ret_start, "");
-        return LLVMBuildLoad(g->builder, ptr_ptr, "");
-    }
     return g->cur_ret_ptr;
 }
 
@@ -3547,7 +3548,7 @@ static void render_async_spills(CodeGen *g) {
             continue;
         }
 
-        var->value_ref = LLVMBuildStructGEP(g->builder, g->cur_ret_ptr, async_var_index,
+        var->value_ref = LLVMBuildStructGEP(g->builder, g->cur_frame_ptr, async_var_index,
                 buf_ptr(&var->name));
         async_var_index += 1;
         if (var->decl_node) {
@@ -3578,7 +3579,7 @@ static void render_async_spills(CodeGen *g) {
                 continue;
             }
         }
-        instruction->base.llvm_value = LLVMBuildStructGEP(g->builder, g->cur_ret_ptr, async_var_index,
+        instruction->base.llvm_value = LLVMBuildStructGEP(g->builder, g->cur_frame_ptr, async_var_index,
                 instruction->name_hint);
         async_var_index += 1;
     }
@@ -3697,7 +3698,7 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
         // initialization.
     } else if (callee_is_async) {
         frame_result_loc = ir_llvm_value(g, instruction->frame_result_loc);
-        awaiter_init_val = LLVMBuildPtrToInt(g->builder, g->cur_ret_ptr, usize_type_ref, ""); // caller's own frame pointer
+        awaiter_init_val = LLVMBuildPtrToInt(g->builder, g->cur_frame_ptr, usize_type_ref, ""); // caller's own frame pointer
         if (ret_has_bits) {
             if (result_loc == nullptr) {
                 // return type is a scalar, but we still need a pointer to it. Use the async fn frame.
@@ -4850,7 +4851,7 @@ static LLVMValueRef ir_render_frame_address(CodeGen *g, IrExecutable *executable
 }
 
 static LLVMValueRef ir_render_handle(CodeGen *g, IrExecutable *executable, IrInstructionFrameHandle *instruction) {
-    return g->cur_ret_ptr;
+    return g->cur_frame_ptr;
 }
 
 static LLVMValueRef render_shl_with_overflow(CodeGen *g, IrInstructionOverflowOp *instruction) {
@@ -5335,7 +5336,7 @@ static LLVMValueRef ir_render_await(CodeGen *g, IrExecutable *executable, IrInst
     }
 
     // caller's own frame pointer
-    LLVMValueRef awaiter_init_val = LLVMBuildPtrToInt(g->builder, g->cur_ret_ptr, usize_type_ref, "");
+    LLVMValueRef awaiter_init_val = LLVMBuildPtrToInt(g->builder, g->cur_frame_ptr, usize_type_ref, "");
     LLVMValueRef awaiter_ptr = LLVMBuildStructGEP(g->builder, target_frame_ptr, coro_awaiter_index, "");
     LLVMValueRef prev_val = LLVMBuildAtomicRMW(g->builder, LLVMAtomicRMWBinOpXchg, awaiter_ptr, awaiter_init_val,
             LLVMAtomicOrderingRelease, g->is_single_threaded);
@@ -6710,13 +6711,17 @@ static void do_code_gen(CodeGen *g) {
 
         bool is_async = fn_is_async(fn_table_entry);
 
-        if (want_sret || is_async) {
-            g->cur_ret_ptr = LLVMGetParam(fn, 0);
-        } else if (handle_is_ptr(fn_type_id->return_type)) {
-            g->cur_ret_ptr = build_alloca(g, fn_type_id->return_type, "result", 0);
-            // TODO add debug info variable for this
+        if (is_async) {
+            g->cur_frame_ptr = LLVMGetParam(fn, 0);
         } else {
-            g->cur_ret_ptr = nullptr;
+            if (want_sret) {
+                g->cur_ret_ptr = LLVMGetParam(fn, 0);
+            } else if (handle_is_ptr(fn_type_id->return_type)) {
+                g->cur_ret_ptr = build_alloca(g, fn_type_id->return_type, "result", 0);
+                // TODO add debug info variable for this
+            } else {
+                g->cur_ret_ptr = nullptr;
+            }
         }
 
         uint32_t err_ret_trace_arg_index = get_err_ret_trace_arg_index(g, fn_table_entry);
@@ -6870,21 +6875,22 @@ static void do_code_gen(CodeGen *g) {
 
             LLVMPositionBuilderAtEnd(g->builder, g->cur_preamble_llvm_block);
             render_async_spills(g);
-            g->cur_async_awaiter_ptr = LLVMBuildStructGEP(g->builder, g->cur_ret_ptr, coro_awaiter_index, "");
-            LLVMValueRef resume_index_ptr = LLVMBuildStructGEP(g->builder, g->cur_ret_ptr, coro_resume_index, "");
+            g->cur_async_awaiter_ptr = LLVMBuildStructGEP(g->builder, g->cur_frame_ptr, coro_awaiter_index, "");
+            LLVMValueRef resume_index_ptr = LLVMBuildStructGEP(g->builder, g->cur_frame_ptr, coro_resume_index, "");
             g->cur_async_resume_index_ptr = resume_index_ptr;
 
             if (type_has_bits(fn_type_id->return_type)) {
-                g->cur_ret_ptr_ptr = LLVMBuildStructGEP(g->builder, g->cur_ret_ptr, coro_ret_start, "");
+                LLVMValueRef cur_ret_ptr_ptr = LLVMBuildStructGEP(g->builder, g->cur_frame_ptr, coro_ret_start, "");
+                g->cur_ret_ptr = LLVMBuildLoad(g->builder, cur_ret_ptr_ptr, "");
             }
             if (codegen_fn_has_err_ret_tracing_arg(g, fn_type_id->return_type)) {
                 uint32_t trace_field_index = frame_index_trace_arg(g, fn_type_id->return_type);
-                g->cur_err_ret_trace_val_arg = LLVMBuildStructGEP(g->builder, g->cur_ret_ptr, trace_field_index, "");
+                g->cur_err_ret_trace_val_arg = LLVMBuildStructGEP(g->builder, g->cur_frame_ptr, trace_field_index, "");
             }
             uint32_t trace_field_index_stack = UINT32_MAX;
             if (codegen_fn_has_err_ret_tracing_stack(g, fn_table_entry, true)) {
                 trace_field_index_stack = frame_index_trace_stack(g, fn_type_id);
-                g->cur_err_ret_trace_val_stack = LLVMBuildStructGEP(g->builder, g->cur_ret_ptr,
+                g->cur_err_ret_trace_val_stack = LLVMBuildStructGEP(g->builder, g->cur_frame_ptr,
                         trace_field_index_stack, "");
             }
 
@@ -6898,9 +6904,9 @@ static void do_code_gen(CodeGen *g) {
             g->cur_resume_block_count += 1;
             LLVMPositionBuilderAtEnd(g->builder, entry_block->llvm_block);
             if (trace_field_index_stack != UINT32_MAX) {
-                LLVMValueRef trace_field_ptr = LLVMBuildStructGEP(g->builder, g->cur_ret_ptr,
+                LLVMValueRef trace_field_ptr = LLVMBuildStructGEP(g->builder, g->cur_frame_ptr,
                         trace_field_index_stack, "");
-                LLVMValueRef trace_field_addrs = LLVMBuildStructGEP(g->builder, g->cur_ret_ptr,
+                LLVMValueRef trace_field_addrs = LLVMBuildStructGEP(g->builder, g->cur_frame_ptr,
                         trace_field_index_stack + 1, "");
 
                 LLVMValueRef index_ptr = LLVMBuildStructGEP(g->builder, trace_field_ptr, 0, "");
src/ir.cpp
@@ -1129,8 +1129,6 @@ static IrInstruction *ir_build_return_begin(IrBuilder *irb, Scope *scope, AstNod
         IrInstruction *operand)
 {
     IrInstructionReturnBegin *return_instruction = ir_build_instruction<IrInstructionReturnBegin>(irb, scope, source_node);
-    return_instruction->base.value.type = irb->codegen->builtin_types.entry_void;
-    return_instruction->base.value.special = ConstValSpecialStatic;
     return_instruction->operand = operand;
 
     ir_ref_instruction(operand, irb->current_basic_block);
@@ -3480,7 +3478,8 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node,
                     return_value = ir_build_const_void(irb, scope, node);
                 }
 
-                ir_build_return_begin(irb, scope, node, return_value);
+                ir_mark_gen(ir_build_add_implicit_return_type(irb, scope, node, return_value));
+                return_value = ir_build_return_begin(irb, scope, node, return_value);
 
                 size_t defer_counts[2];
                 ir_count_defers(irb, scope, outer_scope, defer_counts);
@@ -3514,14 +3513,12 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node,
                     ir_build_br(irb, scope, node, ret_stmt_block, is_comptime);
 
                     ir_set_cursor_at_end_and_append_block(irb, ret_stmt_block);
-                    ir_mark_gen(ir_build_add_implicit_return_type(irb, scope, node, return_value));
                     IrInstruction *result = ir_build_return(irb, scope, node, return_value);
                     result_loc_ret->base.source_instruction = result;
                     return result;
                 } else {
                     // generate unconditional defers
                     ir_gen_defers_for_block(irb, scope, outer_scope, false);
-                    ir_mark_gen(ir_build_add_implicit_return_type(irb, scope, node, return_value));
                     IrInstruction *result = ir_build_return(irb, scope, node, return_value);
                     result_loc_ret->base.source_instruction = result;
                     return result;
@@ -3549,7 +3546,8 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node,
                 ir_set_cursor_at_end_and_append_block(irb, return_block);
                 IrInstruction *err_val_ptr = ir_build_unwrap_err_code(irb, scope, node, err_union_ptr);
                 IrInstruction *err_val = ir_build_load_ptr(irb, scope, node, err_val_ptr);
-                ir_build_return_begin(irb, scope, node, err_val);
+                ir_mark_gen(ir_build_add_implicit_return_type(irb, scope, node, err_val));
+                err_val = ir_build_return_begin(irb, scope, node, err_val);
                 if (!ir_gen_defers_for_block(irb, scope, outer_scope, true)) {
                     ResultLocReturn *result_loc_ret = allocate<ResultLocReturn>(1);
                     result_loc_ret->base.id = ResultLocIdReturn;
@@ -3559,7 +3557,6 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node,
                     if (irb->codegen->have_err_ret_tracing && !should_inline) {
                         ir_build_save_err_ret_addr(irb, scope, node);
                     }
-                    ir_mark_gen(ir_build_add_implicit_return_type(irb, scope, node, err_val));
                     IrInstruction *ret_inst = ir_build_return(irb, scope, node, err_val);
                     result_loc_ret->base.source_instruction = ret_inst;
                 }
@@ -4972,7 +4969,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
             return ir_lval_wrap(irb, scope, ir_build_frame_address(irb, scope, node), lval, result_loc);
         case BuiltinFnIdFrameHandle:
             if (!irb->exec->fn_entry) {
-                add_node_error(irb->codegen, node, buf_sprintf("@handle() called outside of function definition"));
+                add_node_error(irb->codegen, node, buf_sprintf("@frame() called outside of function definition"));
                 return irb->codegen->invalid_instruction;
             }
             return ir_lval_wrap(irb, scope, ir_build_handle(irb, scope, node), lval, result_loc);
@@ -8101,9 +8098,9 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec
         return false;
 
     if (!instr_is_unreachable(result)) {
-        ir_mark_gen(ir_build_return_begin(irb, scope, node, result));
-        // no need for save_err_ret_addr because this cannot return error
         ir_mark_gen(ir_build_add_implicit_return_type(irb, scope, result->source_node, result));
+        result = ir_mark_gen(ir_build_return_begin(irb, scope, node, result));
+        // no need for save_err_ret_addr because this cannot return error
         ir_mark_gen(ir_build_return(irb, scope, result->source_node, result));
     }
 
@@ -9789,6 +9786,8 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT
 
                 ZigType *prev_err_set_type = (err_set_type == nullptr) ? prev_type->data.error_union.err_set_type : err_set_type;
                 ZigType *cur_err_set_type = cur_type->data.error_union.err_set_type;
+                if (prev_err_set_type == cur_err_set_type)
+                    continue;
 
                 if (!resolve_inferred_error_set(ira->codegen, prev_err_set_type, cur_inst->source_node)) {
                     return ira->codegen->builtin_types.entry_invalid;
@@ -12614,6 +12613,14 @@ static IrInstruction *ir_analyze_instruction_return_begin(IrAnalyze *ira, IrInst
     if (type_is_invalid(operand->value.type))
         return ira->codegen->invalid_instruction;
 
+    if (!instr_is_comptime(operand) && handle_is_ptr(ira->explicit_return_type)) {
+        // result location mechanism took care of it.
+        IrInstruction *result = ir_build_return_begin(&ira->new_irb, instruction->base.scope,
+                instruction->base.source_node, operand);
+        copy_const_val(&result->value, &operand->value, true);
+        return result;
+    }
+
     IrInstruction *casted_operand = ir_implicit_cast(ira, operand, ira->explicit_return_type);
     if (type_is_invalid(casted_operand->value.type)) {
         AstNode *source_node = ira->explicit_return_type_source_node;
@@ -12625,8 +12632,18 @@ static IrInstruction *ir_analyze_instruction_return_begin(IrAnalyze *ira, IrInst
         return ir_unreach_error(ira);
     }
 
-    return ir_build_return_begin(&ira->new_irb, instruction->base.scope, instruction->base.source_node,
-            casted_operand);
+    if (casted_operand->value.special == ConstValSpecialRuntime &&
+        casted_operand->value.type->id == ZigTypeIdPointer &&
+        casted_operand->value.data.rh_ptr == RuntimeHintPtrStack)
+    {
+        ir_add_error(ira, casted_operand, buf_sprintf("function returns address of local variable"));
+        return ir_unreach_error(ira);
+    }
+
+    IrInstruction *result = ir_build_return_begin(&ira->new_irb, instruction->base.scope,
+            instruction->base.source_node, casted_operand);
+    copy_const_val(&result->value, &casted_operand->value, true);
+    return result;
 }
 
 static IrInstruction *ir_analyze_instruction_return(IrAnalyze *ira, IrInstructionReturn *instruction) {
@@ -12642,21 +12659,8 @@ static IrInstruction *ir_analyze_instruction_return(IrAnalyze *ira, IrInstructio
         return ir_finish_anal(ira, result);
     }
 
-    IrInstruction *casted_operand = ir_implicit_cast(ira, operand, ira->explicit_return_type);
-    if (type_is_invalid(casted_operand->value.type)) {
-        // error already reported by IrInstructionReturnBegin
-        return ir_unreach_error(ira);
-    }
-
-    if (casted_operand->value.special == ConstValSpecialRuntime &&
-        casted_operand->value.type->id == ZigTypeIdPointer &&
-        casted_operand->value.data.rh_ptr == RuntimeHintPtrStack)
-    {
-        ir_add_error(ira, casted_operand, buf_sprintf("function returns address of local variable"));
-        return ir_unreach_error(ira);
-    }
     IrInstruction *result = ir_build_return(&ira->new_irb, instruction->base.scope,
-            instruction->base.source_node, casted_operand);
+            instruction->base.source_node, operand);
     result->value.type = ira->codegen->builtin_types.entry_unreachable;
     return ir_finish_anal(ira, result);
 }
@@ -14612,8 +14616,12 @@ static IrInstruction *ir_resolve_result_raw(IrAnalyze *ira, IrInstruction *suspe
             if ((err = type_resolve(ira->codegen, ira->explicit_return_type, ResolveStatusZeroBitsKnown))) {
                 return ira->codegen->invalid_instruction;
             }
-            if (!type_has_bits(ira->explicit_return_type) || !handle_is_ptr(ira->explicit_return_type))
-                return nullptr;
+            if (!type_has_bits(ira->explicit_return_type) || !handle_is_ptr(ira->explicit_return_type)) {
+                ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec);
+                if (fn_entry == nullptr || fn_entry->inferred_async_node == nullptr) {
+                    return nullptr;
+                }
+            }
 
             ZigType *ptr_return_type = get_pointer_to_type(ira->codegen, ira->explicit_return_type, false);
             result_loc->written = true;
@@ -24510,7 +24518,7 @@ static IrInstruction *ir_analyze_instruction_await(IrAnalyze *ira, IrInstruction
     IrInstruction *result_loc;
     if (type_has_bits(result_type)) {
         result_loc = ir_resolve_result(ira, &instruction->base, instruction->result_loc,
-                result_type, nullptr, true, false, true);
+                result_type, nullptr, true, true, true);
         if (result_loc != nullptr && (type_is_invalid(result_loc->value.type) || instr_is_unreachable(result_loc)))
             return result_loc;
     } else {
test/stage1/behavior/coroutines.zig
@@ -334,40 +334,40 @@ test "async fn with inferred error set" {
     S.doTheTest();
 }
 
-//test "error return trace across suspend points - early return" {
-//    const p = nonFailing();
-//    resume p;
-//    const p2 = async printTrace(p);
-//}
-//
-//test "error return trace across suspend points - async return" {
-//    const p = nonFailing();
-//    const p2 = async printTrace(p);
-//    resume p;
-//}
-//
-//fn nonFailing() (anyframe->anyerror!void) {
-//    const Static = struct {
-//        var frame: @Frame(suspendThenFail) = undefined;
-//    };
-//    Static.frame = async suspendThenFail();
-//    return &Static.frame;
-//}
-//async fn suspendThenFail() anyerror!void {
-//    suspend;
-//    return error.Fail;
-//}
-//async fn printTrace(p: anyframe->(anyerror!void)) void {
-//    (await p) catch |e| {
-//        std.testing.expect(e == error.Fail);
-//        if (@errorReturnTrace()) |trace| {
-//            expect(trace.index == 1);
-//        } else switch (builtin.mode) {
-//            .Debug, .ReleaseSafe => @panic("expected return trace"),
-//            .ReleaseFast, .ReleaseSmall => {},
-//        }
-//    };
-//}
+test "error return trace across suspend points - early return" {
+    const p = nonFailing();
+    resume p;
+    const p2 = async printTrace(p);
+}
+
+test "error return trace across suspend points - async return" {
+    const p = nonFailing();
+    const p2 = async printTrace(p);
+    resume p;
+}
+
+fn nonFailing() (anyframe->anyerror!void) {
+    const Static = struct {
+        var frame: @Frame(suspendThenFail) = undefined;
+    };
+    Static.frame = async suspendThenFail();
+    return &Static.frame;
+}
+async fn suspendThenFail() anyerror!void {
+    suspend;
+    return error.Fail;
+}
+async fn printTrace(p: anyframe->(anyerror!void)) void {
+    (await p) catch |e| {
+        std.testing.expect(e == error.Fail);
+        if (@errorReturnTrace()) |trace| {
+            expect(trace.index == 1);
+        } else switch (builtin.mode) {
+            .Debug, .ReleaseSafe => @panic("expected return trace"),
+            .ReleaseFast, .ReleaseSmall => {},
+        }
+    };
+}
 
 test "break from suspend" {
     var my_result: i32 = 1;