From eef92753c7cf677191adc40a7cdf7561ceb43bdb Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Mon, 22 May 2023 13:23:21 +0300 Subject: [PATCH] Sema: improve error message when calling optional function Co-authored-by: wrongnull --- src/Sema.zig | 70 ++++++++++--------- .../compile_errors/call_optional_function.zig | 17 +++++ 2 files changed, 54 insertions(+), 33 deletions(-) create mode 100644 test/cases/compile_errors/call_optional_function.zig diff --git a/src/Sema.zig b/src/Sema.zig index 6a1c889a6e..78f7a0bce6 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -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); diff --git a/test/cases/compile_errors/call_optional_function.zig b/test/cases/compile_errors/call_optional_function.zig new file mode 100644 index 0000000000..ce8c3a4d23 --- /dev/null +++ b/test/cases/compile_errors/call_optional_function.zig @@ -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'