| author | |
| committer | |
| log | 55f5cee86b39bb2127a316f9b5d0abf532580cac |
| tree | a9bce15a8899632cf527b980c8fc27280bd9dca0 |
| parent | 13b5a4bf8ca65c569e6b28ca0e41d101d12d0ff1 |
| signature | Commit is signed but in an unrecognized format. |
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,6 +3819,11 @@ static void add_async_error_notes(CodeGen *g, ErrorMsg *msg, ZigFn *fn) { |
| 3819 | } else if (fn->inferred_async_node->type == NodeTypeAwaitExpr) { | 3819 | } else if (fn->inferred_async_node->type == NodeTypeAwaitExpr) { |
| 3820 | add_error_note(g, msg, fn->inferred_async_node, | 3820 | add_error_note(g, msg, fn->inferred_async_node, |
| 3821 | buf_sprintf("await is a suspend point")); | 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 | } else { | 3827 | } else { |
| 3823 | add_error_note(g, msg, fn->inferred_async_node, | 3828 | add_error_note(g, msg, fn->inferred_async_node, |
| 3824 | buf_sprintf("suspends here")); | 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,6 +3760,23 @@ static LLVMValueRef gen_frame_size(CodeGen *g, LLVMValueRef fn_val) { |
| 3760 | return LLVMBuildLoad(g->builder, prefix_ptr, ""); | 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 | static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstructionCallGen *instruction) { | 3780 | static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstructionCallGen *instruction) { |
| 3764 | LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->llvm_type; | 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,9 +3917,24 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr |
| 3900 | if (first_arg_ret) { | 3917 | if (first_arg_ret) { |
| 3901 | gen_param_values.append(ret_ptr); | 3918 | gen_param_values.append(ret_ptr); |
| 3902 | } | 3919 | } |
| 3903 | } | 3920 | if (prefix_arg_err_ret_stack) { |
| 3904 | if (prefix_arg_err_ret_stack) { | 3921 | // Set up the callee stack trace pointer pointing into the frame. |
| 3905 | gen_param_values.append(get_cur_err_ret_trace_val(g, instruction->base.scope)); | 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 | } else { | 3939 | } else { |
| 3908 | if (first_arg_ret) { | 3940 | if (first_arg_ret) { |
| ... | @@ -7126,20 +7158,10 @@ static void do_code_gen(CodeGen *g) { | ... | @@ -7126,20 +7158,10 @@ static void do_code_gen(CodeGen *g) { |
| 7126 | 7158 | ||
| 7127 | LLVMValueRef trace_field_ptr = LLVMBuildStructGEP(g->builder, g->cur_frame_ptr, | 7159 | LLVMValueRef trace_field_ptr = LLVMBuildStructGEP(g->builder, g->cur_frame_ptr, |
| 7128 | trace_field_index_stack, ""); | 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 | trace_field_index_stack + 1, ""); | 7162 | trace_field_index_stack + 1, ""); |
| 7131 | 7163 | ||
| 7132 | LLVMValueRef index_ptr = LLVMBuildStructGEP(g->builder, trace_field_ptr, 0, ""); | 7164 | gen_init_stack_trace(g, trace_field_ptr, addrs_field_ptr); |
| 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); | ||
| 7143 | } | 7165 | } |
| 7144 | render_async_var_decls(g, entry_block->instruction_list.at(0)->scope); | 7166 | render_async_var_decls(g, entry_block->instruction_list.at(0)->scope); |
| 7145 | } else { | 7167 | } else { |
src/ir.cpp+4| ... | @@ -22078,6 +22078,10 @@ static IrInstruction *ir_analyze_instruction_frame_handle(IrAnalyze *ira, IrInst | ... | @@ -22078,6 +22078,10 @@ static IrInstruction *ir_analyze_instruction_frame_handle(IrAnalyze *ira, IrInst |
| 22078 | ZigFn *fn = exec_fn_entry(ira->new_irb.exec); | 22078 | ZigFn *fn = exec_fn_entry(ira->new_irb.exec); |
| 22079 | ir_assert(fn != nullptr, &instruction->base); | 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 | ZigType *frame_type = get_fn_frame_type(ira->codegen, fn); | 22085 | ZigType *frame_type = get_fn_frame_type(ira->codegen, fn); |
| 22082 | ZigType *ptr_frame_type = get_pointer_to_type(ira->codegen, frame_type, false); | 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,6 +2,18 @@ const tests = @import("tests.zig"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | 3 | ||
| 4 | pub fn addCases(cases: *tests.CompileErrorContext) void { | 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 | cases.add( | 17 | cases.add( |
| 6 | "invalid suspend in exported function", | 18 | "invalid suspend in exported function", |
| 7 | \\export fn entry() void { | 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,17 +634,30 @@ test "returning a const error from async function" { |
| 634 | test "async/await typical usage" { | 634 | test "async/await typical usage" { |
| 635 | inline for ([_]bool{false, true}) |b1| { | 635 | inline for ([_]bool{false, true}) |b1| { |
| 636 | inline for ([_]bool{false, true}) |b2| { | 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 | return struct { | 652 | return struct { |
| 644 | fn doTheTest() void { | 653 | fn doTheTest() void { |
| 645 | _ = async amainWrap(); | 654 | _ = async amainWrap(); |
| 646 | resume global_file_frame; | 655 | if (suspend_file) { |
| 647 | resume global_download_frame; | 656 | resume global_file_frame; |
| 657 | } | ||
| 658 | if (suspend_download) { | ||
| 659 | resume global_download_frame; | ||
| 660 | } | ||
| 648 | } | 661 | } |
| 649 | fn amainWrap() void { | 662 | fn amainWrap() void { |
| 650 | if (amain()) |_| { | 663 | if (amain()) |_| { |
| ... | @@ -685,20 +698,26 @@ fn testAsyncAwaitTypicalUsage(comptime simulate_fail_download: bool, comptime si | ... | @@ -685,20 +698,26 @@ fn testAsyncAwaitTypicalUsage(comptime simulate_fail_download: bool, comptime si |
| 685 | 698 | ||
| 686 | var global_download_frame: anyframe = undefined; | 699 | var global_download_frame: anyframe = undefined; |
| 687 | fn fetchUrl(allocator: *std.mem.Allocator, url: []const u8) anyerror![]u8 { | 700 | fn fetchUrl(allocator: *std.mem.Allocator, url: []const u8) anyerror![]u8 { |
| 688 | global_download_frame = @frame(); | ||
| 689 | const result = try std.mem.dupe(allocator, u8, "expected download text"); | 701 | const result = try std.mem.dupe(allocator, u8, "expected download text"); |
| 690 | errdefer allocator.free(result); | 702 | errdefer allocator.free(result); |
| 691 | suspend; | 703 | if (suspend_download) { |
| 704 | suspend { | ||
| 705 | global_download_frame = @frame(); | ||
| 706 | } | ||
| 707 | } | ||
| 692 | if (simulate_fail_download) return error.NoResponse; | 708 | if (simulate_fail_download) return error.NoResponse; |
| 693 | return result; | 709 | return result; |
| 694 | } | 710 | } |
| 695 | 711 | ||
| 696 | var global_file_frame: anyframe = undefined; | 712 | var global_file_frame: anyframe = undefined; |
| 697 | fn readFile(allocator: *std.mem.Allocator, filename: []const u8) anyerror![]u8 { | 713 | fn readFile(allocator: *std.mem.Allocator, filename: []const u8) anyerror![]u8 { |
| 698 | global_file_frame = @frame(); | ||
| 699 | const result = try std.mem.dupe(allocator, u8, "expected file text"); | 714 | const result = try std.mem.dupe(allocator, u8, "expected file text"); |
| 700 | errdefer allocator.free(result); | 715 | errdefer allocator.free(result); |
| 701 | suspend; | 716 | if (suspend_file) { |
| 717 | suspend { | ||
| 718 | global_file_frame = @frame(); | ||
| 719 | } | ||
| 720 | } | ||
| 702 | if (simulate_fail_file) return error.FileNotFound; | 721 | if (simulate_fail_file) return error.FileNotFound; |
| 703 | return result; | 722 | return result; |
| 704 | } | 723 | } |