diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2019-08-17 19:47:49 -0400 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2019-08-17 19:47:49 -0400 |
| commit | ea1734773ba9913e32318aba963cdcb9f51128d4 (patch) | |
| tree | f828d07b7c5b9e4cc550ddf7aab21c9ac166b466 | |
| parent | 57b90d2d98154e382c58f1b385de2bcef132f7d9 (diff) | |
| download | zig-ea1734773ba9913e32318aba963cdcb9f51128d4.tar.gz zig-ea1734773ba9913e32318aba963cdcb9f51128d4.zip | |
add compile error for async frames depending on themselves
| -rw-r--r-- | src/all_types.hpp | 6 | ||||
| -rw-r--r-- | src/analyze.cpp | 45 | ||||
| -rw-r--r-- | test/compile_errors.zig | 36 |
3 files changed, 85 insertions, 2 deletions
diff --git a/src/all_types.hpp b/src/all_types.hpp index f075dd7c58..559ebe8cda 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1279,6 +1279,12 @@ struct ZigTypeOpaque { struct ZigTypeFnFrame { ZigFn *fn; ZigType *locals_struct; + + // This is set to the type that resolving the frame currently depends on, null if none. + // It's for generating a helpful error message. + ZigType *resolve_loop_type; + AstNode *resolve_loop_src_node; + bool reported_loop_err; }; struct ZigTypeAnyFrame { diff --git a/src/analyze.cpp b/src/analyze.cpp index 16e66a5906..ab994d07e8 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -5197,6 +5197,27 @@ static ZigType *get_async_fn_type(CodeGen *g, ZigType *orig_fn_type) { return fn_type; } +static void emit_error_notes_for_type_loop(CodeGen *g, ErrorMsg *msg, ZigType *stop_type, + ZigType *ty, AstNode *src_node) +{ + ErrorMsg *note = add_error_note(g, msg, src_node, + buf_sprintf("when analyzing type '%s' here", buf_ptr(&ty->name))); + if (ty == stop_type) + return; + switch (ty->id) { + case ZigTypeIdFnFrame: { + ty->data.frame.reported_loop_err = true; + ZigType *depending_type = ty->data.frame.resolve_loop_type; + if (depending_type == nullptr) + return; + emit_error_notes_for_type_loop(g, note, stop_type, + depending_type, ty->data.frame.resolve_loop_src_node); + } + default: + return; + } +} + static Error resolve_async_frame(CodeGen *g, ZigType *frame_type) { Error err; @@ -5206,6 +5227,20 @@ static Error resolve_async_frame(CodeGen *g, ZigType *frame_type) { ZigFn *fn = frame_type->data.frame.fn; assert(!fn->type_entry->data.fn.is_generic); + if (frame_type->data.frame.resolve_loop_type != nullptr) { + if (!frame_type->data.frame.reported_loop_err) { + frame_type->data.frame.reported_loop_err = true; + ErrorMsg *msg = add_node_error(g, fn->proto_node, + buf_sprintf("'%s' depends on itself", buf_ptr(&frame_type->name))); + emit_error_notes_for_type_loop(g, msg, + frame_type, + frame_type->data.frame.resolve_loop_type, + frame_type->data.frame.resolve_loop_src_node); + emit_error_notes_for_ref_stack(g, msg); + } + return ErrorSemanticAnalyzeFail; + } + switch (fn->anal_state) { case FnAnalStateInvalid: return ErrorSemanticAnalyzeFail; @@ -5299,6 +5334,10 @@ static Error resolve_async_frame(CodeGen *g, ZigType *frame_type) { return ErrorSemanticAnalyzeFail; } + ZigType *callee_frame_type = get_fn_frame_type(g, callee); + frame_type->data.frame.resolve_loop_type = callee_frame_type; + frame_type->data.frame.resolve_loop_src_node = call->base.source_node; + analyze_fn_body(g, callee); if (callee->anal_state == FnAnalStateInvalid) { frame_type->data.frame.locals_struct = g->builtin_types.entry_invalid; @@ -5308,8 +5347,6 @@ static Error resolve_async_frame(CodeGen *g, ZigType *frame_type) { if (!fn_is_async(callee)) continue; - ZigType *callee_frame_type = get_fn_frame_type(g, callee); - IrInstructionAllocaGen *alloca_gen = allocate<IrInstructionAllocaGen>(1); alloca_gen->base.id = IrInstructionIdAllocaGen; alloca_gen->base.source_node = call->base.source_node; @@ -5378,9 +5415,13 @@ static Error resolve_async_frame(CodeGen *g, ZigType *frame_type) { continue; } } + + frame_type->data.frame.resolve_loop_type = child_type; + frame_type->data.frame.resolve_loop_src_node = instruction->base.source_node; 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)); diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 654171f553..cecb37620c 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -3,6 +3,42 @@ const builtin = @import("builtin"); pub fn addCases(cases: *tests.CompileErrorContext) void { cases.add( + "indirect recursion of async functions detected", + \\var frame: ?anyframe = null; + \\ + \\export fn a() void { + \\ _ = async rangeSum(10); + \\ while (frame) |f| resume f; + \\} + \\ + \\fn rangeSum(x: i32) i32 { + \\ suspend { + \\ frame = @frame(); + \\ } + \\ frame = null; + \\ + \\ if (x == 0) return 0; + \\ var child = rangeSumIndirect(x - 1); + \\ return child + 1; + \\} + \\ + \\fn rangeSumIndirect(x: i32) i32 { + \\ suspend { + \\ frame = @frame(); + \\ } + \\ frame = null; + \\ + \\ if (x == 0) return 0; + \\ var child = rangeSum(x - 1); + \\ return child + 1; + \\} + , + "tmp.zig:8:1: error: '@Frame(rangeSum)' depends on itself", + "tmp.zig:15:33: note: when analyzing type '@Frame(rangeSumIndirect)' here", + "tmp.zig:26:25: note: when analyzing type '@Frame(rangeSum)' here", + ); + + cases.add( "non-async function pointer eventually is inferred to become async", \\export fn a() void { \\ var non_async_fn: fn () void = undefined; |
