Commit 57b90d2d98

Andrew Kelley <andrew@ziglang.org>
2019-08-17 23:22:20
allow implicit cast of fn to async fn
it forces the fn to be async. closes #3079
1 parent 66a490c
Changed files (2)
src
test
stage1
behavior
src/ir.cpp
@@ -9485,10 +9485,6 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted
             result.id = ConstCastResultIdFnAlign;
             return result;
         }
-        if (wanted_type->data.fn.fn_type_id.cc != actual_type->data.fn.fn_type_id.cc) {
-            result.id = ConstCastResultIdFnCC;
-            return result;
-        }
         if (wanted_type->data.fn.fn_type_id.is_var_args != actual_type->data.fn.fn_type_id.is_var_args) {
             result.id = ConstCastResultIdFnVarArgs;
             return result;
@@ -9546,6 +9542,11 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted
                 return result;
             }
         }
+        if (wanted_type->data.fn.fn_type_id.cc != actual_type->data.fn.fn_type_id.cc) {
+            // ConstCastResultIdFnCC is guaranteed to be the last one reported, meaning everything else is ok.
+            result.id = ConstCastResultIdFnCC;
+            return result;
+        }
         return result;
     }
 
@@ -11780,8 +11781,11 @@ static void report_recursive_error(IrAnalyze *ira, AstNode *source_node, ConstCa
             add_error_note(ira->codegen, parent_msg, source_node,
                     buf_sprintf("only one of the functions is generic"));
             break;
+        case ConstCastResultIdFnCC:
+            add_error_note(ira->codegen, parent_msg, source_node,
+                    buf_sprintf("calling convention mismatch"));
+            break;
         case ConstCastResultIdFnAlign: // TODO
-        case ConstCastResultIdFnCC: // TODO
         case ConstCastResultIdFnVarArgs: // TODO
         case ConstCastResultIdFnReturnType: // TODO
         case ConstCastResultIdFnArgCount: // TODO
@@ -11891,6 +11895,21 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
         return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpNoop);
     }
 
+    if (const_cast_result.id == ConstCastResultIdFnCC) {
+        ir_assert(value->value.type->id == ZigTypeIdFn, source_instr);
+        // ConstCastResultIdFnCC is guaranteed to be the last one reported, meaning everything else is ok.
+        if (wanted_type->data.fn.fn_type_id.cc == CallingConventionAsync &&
+            actual_type->data.fn.fn_type_id.cc == CallingConventionUnspecified)
+        {
+            ir_assert(value->value.data.x_ptr.special == ConstPtrSpecialFunction, source_instr);
+            ZigFn *fn = value->value.data.x_ptr.data.fn.fn_entry;
+            if (fn->inferred_async_node == nullptr) {
+                fn->inferred_async_node = source_instr->source_node;
+            }
+            return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpNoop);
+        }
+    }
+
     // cast from T to ?T
     // note that the *T to ?*T case is handled via the "ConstCastOnly" mechanism
     if (wanted_type->id == ZigTypeIdOptional) {
test/stage1/behavior/async_fn.zig
@@ -817,3 +817,30 @@ test "struct parameter to async function is copied to the frame" {
     };
     S.doTheTest();
 }
+
+test "cast fn to async fn when it is inferred to be async" {
+    const S = struct {
+        var frame: anyframe = undefined;
+        var ok = false;
+
+        fn doTheTest() void {
+            var ptr: async fn () i32 = undefined;
+            ptr = func;
+            var buf: [100]u8 align(16) = undefined;
+            var result: i32 = undefined;
+            _ = await @asyncCall(&buf, &result, ptr);
+            expect(result == 1234);
+            ok = true;
+        }
+
+        fn func() i32 {
+            suspend {
+                frame = @frame();
+            }
+            return 1234;
+        }
+    };
+    _ = async S.doTheTest();
+    resume S.frame;
+    expect(S.ok);
+}