Sema: improve error message when calling optional function

Co-authored-by: wrongnull <wrongnull@gmail.com>
This commit is contained in:
Veikka Tuominen 2023-05-22 13:23:21 +03:00
parent 957f269a42
commit eef92753c7
2 changed files with 54 additions and 33 deletions

View File

@ -6308,22 +6308,8 @@ fn zirCall(
}
const callee_ty = sema.typeOf(func);
const func_ty = func_ty: {
switch (callee_ty.zigTypeTag()) {
.Fn => break :func_ty callee_ty,
.Pointer => {
const ptr_info = callee_ty.ptrInfo().data;
if (ptr_info.size == .One and ptr_info.pointee_type.zigTypeTag() == .Fn) {
break :func_ty ptr_info.pointee_type;
}
},
else => {},
}
return sema.fail(block, callee_src, "type '{}' not a function", .{callee_ty.fmt(sema.mod)});
};
const total_args = args_len + @boolToInt(bound_arg_src != null);
try sema.checkCallArgumentCount(block, func, callee_src, func_ty, total_args, bound_arg_src != null);
const func_ty = try sema.checkCallArgumentCount(block, func, callee_src, callee_ty, total_args, bound_arg_src != null);
const args_body = sema.code.extra[extra.end..];
@ -6423,18 +6409,49 @@ fn checkCallArgumentCount(
block: *Block,
func: Air.Inst.Ref,
func_src: LazySrcLoc,
func_ty: Type,
callee_ty: Type,
total_args: usize,
member_fn: bool,
) !void {
) !Type {
const func_ty = func_ty: {
switch (callee_ty.zigTypeTag()) {
.Fn => break :func_ty callee_ty,
.Pointer => {
const ptr_info = callee_ty.ptrInfo().data;
if (ptr_info.size == .One and ptr_info.pointee_type.zigTypeTag() == .Fn) {
break :func_ty ptr_info.pointee_type;
}
},
.Optional => {
var buf: Type.Payload.ElemType = undefined;
const opt_child = callee_ty.optionalChild(&buf);
if (opt_child.zigTypeTag() == .Fn or (opt_child.isSinglePointer() and
opt_child.childType().zigTypeTag() == .Fn))
{
const msg = msg: {
const msg = try sema.errMsg(block, func_src, "cannot call optional type '{}'", .{
callee_ty.fmt(sema.mod),
});
errdefer msg.destroy(sema.gpa);
try sema.errNote(block, func_src, msg, "consider using '.?', 'orelse' or 'if'", .{});
break :msg msg;
};
return sema.failWithOwnedErrorMsg(msg);
}
},
else => {},
}
return sema.fail(block, func_src, "type '{}' not a function", .{callee_ty.fmt(sema.mod)});
};
const func_ty_info = func_ty.fnInfo();
const fn_params_len = func_ty_info.param_types.len;
const args_len = total_args - @boolToInt(member_fn);
if (func_ty_info.is_var_args) {
assert(func_ty_info.cc == .C);
if (total_args >= fn_params_len) return;
if (total_args >= fn_params_len) return func_ty;
} else if (fn_params_len == total_args) {
return;
return func_ty;
}
const maybe_decl = try sema.funcDeclSrc(func);
@ -21666,20 +21683,7 @@ fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
}
const callee_ty = sema.typeOf(func);
const func_ty = func_ty: {
switch (callee_ty.zigTypeTag()) {
.Fn => break :func_ty callee_ty,
.Pointer => {
const ptr_info = callee_ty.ptrInfo().data;
if (ptr_info.size == .One and ptr_info.pointee_type.zigTypeTag() == .Fn) {
break :func_ty ptr_info.pointee_type;
}
},
else => {},
}
return sema.fail(block, func_src, "type '{}' not a function", .{callee_ty.fmt(sema.mod)});
};
try sema.checkCallArgumentCount(block, func, func_src, func_ty, resolved_args.len, false);
const func_ty = try sema.checkCallArgumentCount(block, func, func_src, callee_ty, resolved_args.len, false);
const ensure_result_used = extra.flags.ensure_result_used;
return sema.analyzeCall(block, func, func_ty, func_src, call_src, modifier, ensure_result_used, resolved_args, null, null);

View File

@ -0,0 +1,17 @@
pub export fn entry1() void {
const optional_fn: ?fn () void = null;
_ = optional_fn();
}
pub export fn entry2() void {
const optional_fn_ptr: ?*const fn () void = null;
_ = optional_fn_ptr();
}
// error
// backend=stage2
// target=native
//
// :3:9: error: cannot call optional type '?fn() void'
// :3:9: note: consider using '.?', 'orelse' or 'if'
// :7:9: error: cannot call optional type '?*const fn() void'
// :7:9: note: consider using '.?', 'orelse' or 'if'