aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2019-08-17 19:47:49 -0400
committerAndrew Kelley <andrew@ziglang.org>2019-08-17 19:47:49 -0400
commitea1734773ba9913e32318aba963cdcb9f51128d4 (patch)
treef828d07b7c5b9e4cc550ddf7aab21c9ac166b466
parent57b90d2d98154e382c58f1b385de2bcef132f7d9 (diff)
downloadzig-ea1734773ba9913e32318aba963cdcb9f51128d4.tar.gz
zig-ea1734773ba9913e32318aba963cdcb9f51128d4.zip
add compile error for async frames depending on themselves
-rw-r--r--src/all_types.hpp6
-rw-r--r--src/analyze.cpp45
-rw-r--r--test/compile_errors.zig36
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;