From 107b65ec5dccd360410b4a693bb0caf0015312f3 Mon Sep 17 00:00:00 2001 From: mlugg Date: Wed, 29 Jan 2025 06:39:29 +0000 Subject: [PATCH] Sema: explain why we tried to call an `extern fn` at comptime I recently saw a user hit the "comptime call of extern function" error, and get confused because they didn't know why the scope was `comptime`. So, use `explainWhyBlockIsComptime` on this and related errors to add all the relevant notes. The added test case shows the motivating situation. --- lib/std/zig.zig | 2 - src/Sema.zig | 41 +++++++++++++++---- ...mplex_comptime_call_of_extern_function.zig | 40 ++++++++++++++++++ .../comptime_call_of_function_pointer.zig | 2 +- ...nitializer_must_be_constant_expression.zig | 1 + .../invalid_extern_function_call.zig | 1 + .../invalid_pointer_for_var_type.zig | 1 + ...ion_in_struct_literal_outside_function.zig | 1 + 8 files changed, 77 insertions(+), 12 deletions(-) create mode 100644 test/cases/compile_errors/complex_comptime_call_of_extern_function.zig diff --git a/lib/std/zig.zig b/lib/std/zig.zig index 292e06982029fde8a6107eaee6754e6972ed0a6a..7897c341b9d3c13ffc7bde93e15d29258435b235 100644 --- a/lib/std/zig.zig +++ b/lib/std/zig.zig @@ -749,7 +749,6 @@ pub const SimpleComptimeReason = enum(u32) { atomic_order, array_mul_factor, slice_cat_operand, - comptime_call_target, inline_call_target, generic_call_target, wasm_memory_index, @@ -830,7 +829,6 @@ pub const SimpleComptimeReason = enum(u32) { .atomic_order => "atomic order must be comptime-known", .array_mul_factor => "array multiplication factor must be comptime-known", .slice_cat_operand => "slice being concatenated must be comptime-known", - .comptime_call_target => "function being called at comptime must be comptime-known", .inline_call_target => "function being called inline must be comptime-known", .generic_call_target => "generic function being called must be comptime-known", .wasm_memory_index => "wasm memory index must be comptime-known", diff --git a/src/Sema.zig b/src/Sema.zig index f38a82659c803f3935cb9b02a9e3f7a6469b7675..2fe4aac581d90cf856162889475f14a037decae0 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -8021,26 +8021,49 @@ fn analyzeCall( // This is an inline call. The function must be comptime-known. We will analyze its body directly using this `Sema`. + if (func_ty_info.is_noinline and !block.isComptime()) { + return sema.fail(block, call_src, "inline call of noinline function", .{}); + } + const call_type: []const u8 = if (block.isComptime()) "comptime" else "inline"; - if (modifier == .never_inline) { - return sema.fail(block, call_src, "cannot perform {s} call with 'never_inline' modifier", .{call_type}); - } - if (func_ty_info.is_noinline and !block.isComptime()) { - return sema.fail(block, call_src, "{s} call of noinline function", .{call_type}); + const msg, const fail_block = msg: { + const msg = try sema.errMsg(call_src, "cannot perform {s} call with 'never_inline' modifier", .{call_type}); + errdefer msg.destroy(gpa); + const fail_block = if (block.isComptime()) b: { + break :b try block.explainWhyBlockIsComptime(msg); + } else block; + break :msg .{ msg, fail_block }; + }; + return sema.failWithOwnedErrorMsg(fail_block, msg); } if (func_ty_info.is_var_args) { - return sema.fail(block, call_src, "{s} call of variadic function", .{call_type}); + const msg, const fail_block = msg: { + const msg = try sema.errMsg(call_src, "{s} call of variadic function", .{call_type}); + errdefer msg.destroy(gpa); + const fail_block = if (block.isComptime()) b: { + break :b try block.explainWhyBlockIsComptime(msg); + } else block; + break :msg .{ msg, fail_block }; + }; + return sema.failWithOwnedErrorMsg(fail_block, msg); } - if (func_val == null) { if (func_is_extern) { - return sema.fail(block, call_src, "{s} call of extern function", .{call_type}); + const msg, const fail_block = msg: { + const msg = try sema.errMsg(call_src, "{s} call of extern function", .{call_type}); + errdefer msg.destroy(gpa); + const fail_block = if (block.isComptime()) b: { + break :b try block.explainWhyBlockIsComptime(msg); + } else block; + break :msg .{ msg, fail_block }; + }; + return sema.failWithOwnedErrorMsg(fail_block, msg); } return sema.failWithNeededComptime( block, func_src, - .{ .simple = if (block.isComptime()) .comptime_call_target else .inline_call_target }, + if (block.isComptime()) null else .{ .simple = .inline_call_target }, ); } diff --git a/test/cases/compile_errors/complex_comptime_call_of_extern_function.zig b/test/cases/compile_errors/complex_comptime_call_of_extern_function.zig new file mode 100644 index 0000000000000000000000000000000000000000..fc0065cb24caf8b4642e6ef2efddfa2598086de8 --- /dev/null +++ b/test/cases/compile_errors/complex_comptime_call_of_extern_function.zig @@ -0,0 +1,40 @@ +extern fn next_id() u32; + +const Foo = struct { + bar: Bar, + + fn init() Foo { + return .{ .bar = .init() }; + } +}; +const Bar = struct { + qux: ?Qux, + id: u32, + + fn init() Bar { + return .{ + .qux = null, + .id = next_id(), + }; + } +}; +const Qux = struct { + handleThing: fn () void, +}; + +export fn entry() void { + const foo: Foo = .init(); + _ = foo; +} + +// error +// +// :17:26: error: comptime call of extern function +// :7:31: note: called at comptime from here +// :26:27: note: called at comptime from here +// :26:27: note: call to function with comptime-only return type 'tmp.Foo' is evaluated at comptime +// :6:15: note: return type declared here +// :4:10: note: struct requires comptime because of this field +// :11:10: note: struct requires comptime because of this field +// :22:18: note: struct requires comptime because of this field +// :22:18: note: use '*const fn () void' for a function pointer type diff --git a/test/cases/compile_errors/comptime_call_of_function_pointer.zig b/test/cases/compile_errors/comptime_call_of_function_pointer.zig index a73f541b5ea37e2d3ce278d007586f6c21873f75..d419d5fe86e65305aa834522bb947b68cf2d7efb 100644 --- a/test/cases/compile_errors/comptime_call_of_function_pointer.zig +++ b/test/cases/compile_errors/comptime_call_of_function_pointer.zig @@ -6,4 +6,4 @@ export fn entry() void { // error // // :3:14: error: unable to resolve comptime value -// :3:14: note: function being called at comptime must be comptime-known +// :3:5: note: 'comptime' keyword forces comptime evaluation diff --git a/test/cases/compile_errors/global_variable_initializer_must_be_constant_expression.zig b/test/cases/compile_errors/global_variable_initializer_must_be_constant_expression.zig index b06767111d2c83167e949cd2657cea47020ac5e0..1f3ab93c96c7c9b825044a156a13e2be32bc2f25 100644 --- a/test/cases/compile_errors/global_variable_initializer_must_be_constant_expression.zig +++ b/test/cases/compile_errors/global_variable_initializer_must_be_constant_expression.zig @@ -7,3 +7,4 @@ export fn entry() i32 { // error // // :2:14: error: comptime call of extern function +// :2:14: note: initializer of container-level variable must be comptime-known diff --git a/test/cases/compile_errors/invalid_extern_function_call.zig b/test/cases/compile_errors/invalid_extern_function_call.zig index 36a8ac3fd7de05b7d77ca9dee2ac38965fe101e8..887d2e1da01f7dc48177c628349161be8a50cd83 100644 --- a/test/cases/compile_errors/invalid_extern_function_call.zig +++ b/test/cases/compile_errors/invalid_extern_function_call.zig @@ -11,4 +11,5 @@ export fn entry1() void { // error // // :4:15: error: comptime call of extern function +// :4:5: note: 'comptime' keyword forces comptime evaluation // :8:5: error: inline call of extern function diff --git a/test/cases/compile_errors/invalid_pointer_for_var_type.zig b/test/cases/compile_errors/invalid_pointer_for_var_type.zig index 2d80718b4ff60102b20eac6a36f8aa237706e16b..01e049dcd015fb2f897fdaf185a8426ddbf36e05 100644 --- a/test/cases/compile_errors/invalid_pointer_for_var_type.zig +++ b/test/cases/compile_errors/invalid_pointer_for_var_type.zig @@ -9,3 +9,4 @@ export fn f() void { // error // // :2:16: error: comptime call of extern function +// :2:12: note: types must be comptime-known diff --git a/test/cases/compile_errors/non-const_expression_in_struct_literal_outside_function.zig b/test/cases/compile_errors/non-const_expression_in_struct_literal_outside_function.zig index 808f94d9bfc03751ed21af213b8b3898150463e0..7750e325b80ac6abdbcd95832759bf0bc348c7fa 100644 --- a/test/cases/compile_errors/non-const_expression_in_struct_literal_outside_function.zig +++ b/test/cases/compile_errors/non-const_expression_in_struct_literal_outside_function.zig @@ -11,3 +11,4 @@ export fn entry() usize { // error // // :4:27: error: comptime call of extern function +// :4:14: note: initializer of container-level variable must be comptime-known -- 2.54.0