Commit 66a490c27c

Andrew Kelley <andrew@ziglang.org>
2019-08-17 22:49:23
detect non-async function pointer of inferred async function
closes #3075
1 parent 0ff396c
src/all_types.hpp
@@ -1396,6 +1396,7 @@ struct ZigFn {
     AstNode *set_cold_node;
     const AstNode *inferred_async_node;
     ZigFn *inferred_async_fn;
+    AstNode *non_async_node;
 
     ZigList<GlobalExport> export_list;
     ZigList<IrInstructionCallGen *> call_list;
src/analyze.cpp
@@ -4144,8 +4144,15 @@ void semantic_analyze(CodeGen *g) {
 
     // second pass over functions for detecting async
     for (g->fn_defs_index = 0; g->fn_defs_index < g->fn_defs.length; g->fn_defs_index += 1) {
-        ZigFn *fn_entry = g->fn_defs.at(g->fn_defs_index);
-        analyze_fn_async(g, fn_entry, true);
+        ZigFn *fn = g->fn_defs.at(g->fn_defs_index);
+        analyze_fn_async(g, fn, true);
+        if (fn_is_async(fn) && fn->non_async_node != nullptr) {
+            ErrorMsg *msg = add_node_error(g, fn->proto_node,
+                buf_sprintf("'%s' cannot be async", buf_ptr(&fn->symbol_name)));
+            add_error_note(g, msg, fn->non_async_node,
+                buf_sprintf("required to be non-async here"));
+            add_async_error_notes(g, msg, fn);
+        }
     }
 }
 
src/ir.cpp
@@ -15160,6 +15160,20 @@ no_mem_slot:
     return var_ptr_instruction;
 }
 
+// This function is called when a comptime value becomes accessible at runtime.
+static void mark_comptime_value_escape(IrAnalyze *ira, IrInstruction *source_instr, ConstExprValue *val) {
+    ir_assert(value_is_comptime(val), source_instr);
+    if (val->special == ConstValSpecialUndef)
+        return;
+
+    if (val->type->id == ZigTypeIdFn && val->type->data.fn.fn_type_id.cc == CallingConventionUnspecified) {
+        ir_assert(val->data.x_ptr.special == ConstPtrSpecialFunction, source_instr);
+        if (val->data.x_ptr.data.fn.fn_entry->non_async_node == nullptr) {
+            val->data.x_ptr.data.fn.fn_entry->non_async_node = source_instr->source_node;
+        }
+    }
+}
+
 static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source_instr,
         IrInstruction *ptr, IrInstruction *uncasted_value, bool allow_write_through_const)
 {
@@ -15256,6 +15270,10 @@ static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source
             break;
     }
 
+    if (instr_is_comptime(value)) {
+        mark_comptime_value_escape(ira, source_instr, &value->value);
+    }
+
     IrInstructionStorePtr *store_ptr = ir_build_store_ptr(&ira->new_irb, source_instr->scope,
             source_instr->source_node, ptr, value);
     return &store_ptr->base;
test/compile_errors.zig
@@ -2,6 +2,21 @@ const tests = @import("tests.zig");
 const builtin = @import("builtin");
 
 pub fn addCases(cases: *tests.CompileErrorContext) void {
+    cases.add(
+        "non-async function pointer eventually is inferred to become async",
+        \\export fn a() void {
+        \\    var non_async_fn: fn () void = undefined;
+        \\    non_async_fn = func;
+        \\}
+        \\fn func() void {
+        \\    suspend;
+        \\}
+    ,
+        "tmp.zig:5:1: error: 'func' cannot be async",
+        "tmp.zig:3:20: note: required to be non-async here",
+        "tmp.zig:6:5: note: suspends here",
+    );
+
     cases.add(
         "bad alignment in @asyncCall",
         \\export fn entry() void {