| author | |
| committer | |
| log | 55f5cee86b39bb2127a316f9b5d0abf532580cac |
| tree | a9bce15a8899632cf527b980c8fc27280bd9dca0 |
| parent | 13b5a4bf8ca65c569e6b28ca0e41d101d12d0ff1 |
| signature |
5 files changed, 85 insertions(+), 23 deletions(-)
src/analyze.cpp+5| ... | ... | @@ -3819,6 +3819,11 @@ static void add_async_error_notes(CodeGen *g, ErrorMsg *msg, ZigFn *fn) { |
| 3819 | 3819 | } else if (fn->inferred_async_node->type == NodeTypeAwaitExpr) { |
| 3820 | 3820 | add_error_note(g, msg, fn->inferred_async_node, |
| 3821 | 3821 | buf_sprintf("await is a suspend point")); |
| 3822 | } else if (fn->inferred_async_node->type == NodeTypeFnCallExpr && | |
| 3823 | fn->inferred_async_node->data.fn_call_expr.is_builtin) | |
| 3824 | { | |
| 3825 | add_error_note(g, msg, fn->inferred_async_node, | |
| 3826 | buf_sprintf("@frame() causes function to be async")); | |
| 3822 | 3827 | } else { |
| 3823 | 3828 | add_error_note(g, msg, fn->inferred_async_node, |
| 3824 | 3829 | buf_sprintf("suspends here")); |
src/codegen.cpp+37-15| ... | ... | @@ -3760,6 +3760,23 @@ static LLVMValueRef gen_frame_size(CodeGen *g, LLVMValueRef fn_val) { |
| 3760 | 3760 | return LLVMBuildLoad(g->builder, prefix_ptr, ""); |
| 3761 | 3761 | } |
| 3762 | 3762 | |
| 3763 | static void gen_init_stack_trace(CodeGen *g, LLVMValueRef trace_field_ptr, LLVMValueRef addrs_field_ptr) { | |
| 3764 | LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->llvm_type; | |
| 3765 | LLVMValueRef zero = LLVMConstNull(usize_type_ref); | |
| 3766 | ||
| 3767 | LLVMValueRef index_ptr = LLVMBuildStructGEP(g->builder, trace_field_ptr, 0, ""); | |
| 3768 | LLVMBuildStore(g->builder, zero, index_ptr); | |
| 3769 | ||
| 3770 | LLVMValueRef addrs_slice_ptr = LLVMBuildStructGEP(g->builder, trace_field_ptr, 1, ""); | |
| 3771 | LLVMValueRef addrs_ptr_ptr = LLVMBuildStructGEP(g->builder, addrs_slice_ptr, slice_ptr_index, ""); | |
| 3772 | LLVMValueRef indices[] = { LLVMConstNull(usize_type_ref), LLVMConstNull(usize_type_ref) }; | |
| 3773 | LLVMValueRef trace_field_addrs_as_ptr = LLVMBuildInBoundsGEP(g->builder, addrs_field_ptr, indices, 2, ""); | |
| 3774 | LLVMBuildStore(g->builder, trace_field_addrs_as_ptr, addrs_ptr_ptr); | |
| 3775 | ||
| 3776 | LLVMValueRef addrs_len_ptr = LLVMBuildStructGEP(g->builder, addrs_slice_ptr, slice_len_index, ""); | |
| 3777 | LLVMBuildStore(g->builder, LLVMConstInt(usize_type_ref, stack_trace_ptr_count, false), addrs_len_ptr); | |
| 3778 | } | |
| 3779 | ||
| 3763 | 3780 | static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstructionCallGen *instruction) { |
| 3764 | 3781 | LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->llvm_type; |
| 3765 | 3782 | |
| ... | ... | @@ -3900,9 +3917,24 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr |
| 3900 | 3917 | if (first_arg_ret) { |
| 3901 | 3918 | gen_param_values.append(ret_ptr); |
| 3902 | 3919 | } |
| 3903 | } | |
| 3904 | if (prefix_arg_err_ret_stack) { | |
| 3905 | gen_param_values.append(get_cur_err_ret_trace_val(g, instruction->base.scope)); | |
| 3920 | if (prefix_arg_err_ret_stack) { | |
| 3921 | // Set up the callee stack trace pointer pointing into the frame. | |
| 3922 | // Then we have to wire up the StackTrace pointers. | |
| 3923 | // Await is responsible for merging error return traces. | |
| 3924 | uint32_t trace_field_index_start = frame_index_trace_arg(g, src_return_type); | |
| 3925 | LLVMValueRef callee_trace_ptr_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, | |
| 3926 | trace_field_index_start, ""); | |
| 3927 | LLVMValueRef trace_field_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, | |
| 3928 | trace_field_index_start + 2, ""); | |
| 3929 | LLVMValueRef addrs_field_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, | |
| 3930 | trace_field_index_start + 3, ""); | |
| 3931 | ||
| 3932 | LLVMBuildStore(g->builder, trace_field_ptr, callee_trace_ptr_ptr); | |
| 3933 | ||
| 3934 | gen_init_stack_trace(g, trace_field_ptr, addrs_field_ptr); | |
| 3935 | ||
| 3936 | gen_param_values.append(get_cur_err_ret_trace_val(g, instruction->base.scope)); | |
| 3937 | } | |
| 3906 | 3938 | } |
| 3907 | 3939 | } else { |
| 3908 | 3940 | if (first_arg_ret) { |
| ... | ... | @@ -7126,20 +7158,10 @@ static void do_code_gen(CodeGen *g) { |
| 7126 | 7158 | |
| 7127 | 7159 | LLVMValueRef trace_field_ptr = LLVMBuildStructGEP(g->builder, g->cur_frame_ptr, |
| 7128 | 7160 | trace_field_index_stack, ""); |
| 7129 | LLVMValueRef trace_field_addrs = LLVMBuildStructGEP(g->builder, g->cur_frame_ptr, | |
| 7161 | LLVMValueRef addrs_field_ptr = LLVMBuildStructGEP(g->builder, g->cur_frame_ptr, | |
| 7130 | 7162 | trace_field_index_stack + 1, ""); |
| 7131 | 7163 | |
| 7132 | LLVMValueRef index_ptr = LLVMBuildStructGEP(g->builder, trace_field_ptr, 0, ""); | |
| 7133 | LLVMBuildStore(g->builder, zero, index_ptr); | |
| 7134 | ||
| 7135 | LLVMValueRef addrs_slice_ptr = LLVMBuildStructGEP(g->builder, trace_field_ptr, 1, ""); | |
| 7136 | LLVMValueRef addrs_ptr_ptr = LLVMBuildStructGEP(g->builder, addrs_slice_ptr, slice_ptr_index, ""); | |
| 7137 | LLVMValueRef indices[] = { LLVMConstNull(usize_type_ref), LLVMConstNull(usize_type_ref) }; | |
| 7138 | LLVMValueRef trace_field_addrs_as_ptr = LLVMBuildInBoundsGEP(g->builder, trace_field_addrs, indices, 2, ""); | |
| 7139 | LLVMBuildStore(g->builder, trace_field_addrs_as_ptr, addrs_ptr_ptr); | |
| 7140 | ||
| 7141 | LLVMValueRef addrs_len_ptr = LLVMBuildStructGEP(g->builder, addrs_slice_ptr, slice_len_index, ""); | |
| 7142 | LLVMBuildStore(g->builder, LLVMConstInt(usize_type_ref, stack_trace_ptr_count, false), addrs_len_ptr); | |
| 7164 | gen_init_stack_trace(g, trace_field_ptr, addrs_field_ptr); | |
| 7143 | 7165 | } |
| 7144 | 7166 | render_async_var_decls(g, entry_block->instruction_list.at(0)->scope); |
| 7145 | 7167 | } else { |
src/ir.cpp+4| ... | ... | @@ -22078,6 +22078,10 @@ static IrInstruction *ir_analyze_instruction_frame_handle(IrAnalyze *ira, IrInst |
| 22078 | 22078 | ZigFn *fn = exec_fn_entry(ira->new_irb.exec); |
| 22079 | 22079 | ir_assert(fn != nullptr, &instruction->base); |
| 22080 | 22080 | |
| 22081 | if (fn->inferred_async_node == nullptr) { | |
| 22082 | fn->inferred_async_node = instruction->base.source_node; | |
| 22083 | } | |
| 22084 | ||
| 22081 | 22085 | ZigType *frame_type = get_fn_frame_type(ira->codegen, fn); |
| 22082 | 22086 | ZigType *ptr_frame_type = get_pointer_to_type(ira->codegen, frame_type, false); |
| 22083 | 22087 |
test/compile_errors.zig+12| ... | ... | @@ -2,6 +2,18 @@ const tests = @import("tests.zig"); |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | 3 | |
| 4 | 4 | pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5 | cases.add( | |
| 6 | "@frame() causes function to be async", | |
| 7 | \\export fn entry() void { | |
| 8 | \\ func(); | |
| 9 | \\} | |
| 10 | \\fn func() void { | |
| 11 | \\ _ = @frame(); | |
| 12 | \\} | |
| 13 | , | |
| 14 | "tmp.zig:1:1: error: function with calling convention 'ccc' cannot be async", | |
| 15 | "tmp.zig:5:9: note: @frame() causes function to be async", | |
| 16 | ); | |
| 5 | 17 | cases.add( |
| 6 | 18 | "invalid suspend in exported function", |
| 7 | 19 | \\export fn entry() void { |
test/stage1/behavior/async_fn.zig+27-8| ... | ... | @@ -634,17 +634,30 @@ test "returning a const error from async function" { |
| 634 | 634 | test "async/await typical usage" { |
| 635 | 635 | inline for ([_]bool{false, true}) |b1| { |
| 636 | 636 | inline for ([_]bool{false, true}) |b2| { |
| 637 | testAsyncAwaitTypicalUsage(b1, b2).doTheTest(); | |
| 637 | inline for ([_]bool{false, true}) |b3| { | |
| 638 | inline for ([_]bool{false, true}) |b4| { | |
| 639 | testAsyncAwaitTypicalUsage(b1, b2, b3, b4).doTheTest(); | |
| 640 | } | |
| 641 | } | |
| 638 | 642 | } |
| 639 | 643 | } |
| 640 | 644 | } |
| 641 | 645 | |
| 642 | fn testAsyncAwaitTypicalUsage(comptime simulate_fail_download: bool, comptime simulate_fail_file: bool) type { | |
| 646 | fn testAsyncAwaitTypicalUsage( | |
| 647 | comptime simulate_fail_download: bool, | |
| 648 | comptime simulate_fail_file: bool, | |
| 649 | comptime suspend_download: bool, | |
| 650 | comptime suspend_file: bool) type | |
| 651 | { | |
| 643 | 652 | return struct { |
| 644 | 653 | fn doTheTest() void { |
| 645 | 654 | _ = async amainWrap(); |
| 646 | resume global_file_frame; | |
| 647 | resume global_download_frame; | |
| 655 | if (suspend_file) { | |
| 656 | resume global_file_frame; | |
| 657 | } | |
| 658 | if (suspend_download) { | |
| 659 | resume global_download_frame; | |
| 660 | } | |
| 648 | 661 | } |
| 649 | 662 | fn amainWrap() void { |
| 650 | 663 | if (amain()) |_| { |
| ... | ... | @@ -685,20 +698,26 @@ fn testAsyncAwaitTypicalUsage(comptime simulate_fail_download: bool, comptime si |
| 685 | 698 | |
| 686 | 699 | var global_download_frame: anyframe = undefined; |
| 687 | 700 | fn fetchUrl(allocator: *std.mem.Allocator, url: []const u8) anyerror![]u8 { |
| 688 | global_download_frame = @frame(); | |
| 689 | 701 | const result = try std.mem.dupe(allocator, u8, "expected download text"); |
| 690 | 702 | errdefer allocator.free(result); |
| 691 | suspend; | |
| 703 | if (suspend_download) { | |
| 704 | suspend { | |
| 705 | global_download_frame = @frame(); | |
| 706 | } | |
| 707 | } | |
| 692 | 708 | if (simulate_fail_download) return error.NoResponse; |
| 693 | 709 | return result; |
| 694 | 710 | } |
| 695 | 711 | |
| 696 | 712 | var global_file_frame: anyframe = undefined; |
| 697 | 713 | fn readFile(allocator: *std.mem.Allocator, filename: []const u8) anyerror![]u8 { |
| 698 | global_file_frame = @frame(); | |
| 699 | 714 | const result = try std.mem.dupe(allocator, u8, "expected file text"); |
| 700 | 715 | errdefer allocator.free(result); |
| 701 | suspend; | |
| 716 | if (suspend_file) { | |
| 717 | suspend { | |
| 718 | global_file_frame = @frame(); | |
| 719 | } | |
| 720 | } | |
| 702 | 721 | if (simulate_fail_file) return error.FileNotFound; |
| 703 | 722 | return result; |
| 704 | 723 | } |