Commit 34cdcb13c0

LemonBoy <thatlemon@gmail.com>
2020-01-12 23:49:26
Fix @call being too eager to resolve the fn argument
Closes #4020
1 parent c96131f
Changed files (3)
src
test
src/ir.cpp
@@ -18687,18 +18687,6 @@ static IrInstruction *ir_analyze_call_extra(IrAnalyze *ira, IrInstruction *sourc
     IrInstruction *fn_ref = pass1_fn_ref->child;
     if (type_is_invalid(fn_ref->value->type))
         return ira->codegen->invalid_instruction;
-    IrInstruction *first_arg_ptr = nullptr;
-    ZigFn *fn = nullptr;
-    if (fn_ref->value->type->id == ZigTypeIdBoundFn) {
-        assert(fn_ref->value->special == ConstValSpecialStatic);
-        fn = fn_ref->value->data.x_bound_fn.fn;
-        first_arg_ptr = fn_ref->value->data.x_bound_fn.first_arg;
-        if (type_is_invalid(first_arg_ptr->value->type))
-            return ira->codegen->invalid_instruction;
-    } else {
-        fn = ir_resolve_fn(ira, fn_ref);
-    }
-    ZigType *fn_type = (fn != nullptr) ? fn->type_entry : fn_ref->value->type;
 
     TypeStructField *modifier_field = find_struct_type_field(options->value->type, buf_create_from_str("modifier"));
     ir_assert(modifier_field != nullptr, source_instr);
@@ -18733,22 +18721,52 @@ static IrInstruction *ir_analyze_call_extra(IrAnalyze *ira, IrInstruction *sourc
         }
     }
 
+    IrInstruction *first_arg_ptr = nullptr;
+    ZigFn *fn = nullptr;
+    if (instr_is_comptime(fn_ref)) {
+        if (fn_ref->value->type->id == ZigTypeIdBoundFn) {
+            assert(fn_ref->value->special == ConstValSpecialStatic);
+            fn = fn_ref->value->data.x_bound_fn.fn;
+            first_arg_ptr = fn_ref->value->data.x_bound_fn.first_arg;
+            if (type_is_invalid(first_arg_ptr->value->type))
+                return ira->codegen->invalid_instruction;
+        } else {
+            fn = ir_resolve_fn(ira, fn_ref);
+        }
+    }
+
+    // Some modifiers require the callee to be comptime-known
+    switch (modifier) {
+        case CallModifierCompileTime:
+        case CallModifierAlwaysInline:
+        case CallModifierAsync:
+            if (fn == nullptr) {
+                ir_add_error(ira, modifier_inst,
+                    buf_sprintf("the specified modifier requires a comptime-known function"));
+                return ira->codegen->invalid_instruction;
+            }
+        default:
+            break;
+    }
+
+    ZigType *fn_type = (fn != nullptr) ? fn->type_entry : fn_ref->value->type;
+
     TypeStructField *stack_field = find_struct_type_field(options->value->type, buf_create_from_str("stack"));
     ir_assert(stack_field != nullptr, source_instr);
     IrInstruction *opt_stack = ir_analyze_struct_value_field_value(ira, source_instr, options, stack_field);
     if (type_is_invalid(opt_stack->value->type))
         return ira->codegen->invalid_instruction;
+
     IrInstruction *stack_is_non_null_inst = ir_analyze_test_non_null(ira, source_instr, opt_stack);
     bool stack_is_non_null;
     if (!ir_resolve_bool(ira, stack_is_non_null_inst, &stack_is_non_null))
         return ira->codegen->invalid_instruction;
-    IrInstruction *stack;
+
+    IrInstruction *stack = nullptr;
     if (stack_is_non_null) {
         stack = ir_analyze_optional_value_payload_value(ira, source_instr, opt_stack, false);
-    if (type_is_invalid(stack->value->type))
-        return ira->codegen->invalid_instruction;
-    } else {
-        stack = nullptr;
+        if (type_is_invalid(stack->value->type))
+            return ira->codegen->invalid_instruction;
     }
 
     return ir_analyze_fn_call(ira, source_instr, fn, fn_type, fn_ref, first_arg_ptr,
test/stage1/behavior/call.zig
@@ -20,6 +20,13 @@ test "basic invocations" {
         const result = @call(.{ .modifier = .compile_time }, foo, .{}) == 1234;
         comptime expect(result);
     }
+    {
+        // call of non comptime-known function
+        var alias_foo = foo;
+        expect(@call(.{ .modifier = .no_async }, alias_foo, .{}) == 1234);
+        expect(@call(.{ .modifier = .never_tail }, alias_foo, .{}) == 1234);
+        expect(@call(.{ .modifier = .never_inline }, alias_foo, .{}) == 1234);
+    }
 }
 
 test "tuple parameters" {
test/compile_errors.zig
@@ -11,6 +11,24 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
         "tmp.zig:3:26: error: expected 2 arguments, found 1",
     });
 
+    cases.addTest("@call rejects non comptime-known fn - always_inline",
+        \\pub export fn entry() void {
+        \\    var call_me: fn () void = undefined;
+        \\    @call(.{ .modifier = .always_inline }, call_me, .{});
+        \\}
+    , &[_][]const u8{
+        "tmp.zig:3:5: error: the specified modifier requires a comptime-known function",
+    });
+
+    cases.addTest("@call rejects non comptime-known fn - compile_time",
+        \\pub export fn entry() void {
+        \\    var call_me: fn () void = undefined;
+        \\    @call(.{ .modifier = .compile_time }, call_me, .{});
+        \\}
+    , &[_][]const u8{
+        "tmp.zig:3:5: error: the specified modifier requires a comptime-known function",
+    });
+
     cases.addTest("error in struct initializer doesn't crash the compiler",
         \\pub export fn entry() void {
         \\    const bitfield = struct {