Fix @call being too eager to resolve the fn argument

Closes #4020
This commit is contained in:
LemonBoy 2020-01-12 23:49:26 +01:00
parent c96131f30c
commit 34cdcb13c0
3 changed files with 60 additions and 17 deletions

View File

@ -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,

View File

@ -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 {

View File

@ -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" {