authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-01-29 06:39:29+00:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-01-29 18:43:24+00:00
log107b65ec5dccd360410b4a693bb0caf0015312f3
tree7cd66974406b4f3423e746f1ca24c0e7f0cabb94
parent484f72311eae288ed5c7a648ec016b02f4547f48

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.

8 files changed, 77 insertions(+), 12 deletions(-)

lib/std/zig.zig-2
...@@ -749,7 +749,6 @@ pub const SimpleComptimeReason = enum(u32) {...@@ -749,7 +749,6 @@ pub const SimpleComptimeReason = enum(u32) {
749 atomic_order,749 atomic_order,
750 array_mul_factor,750 array_mul_factor,
751 slice_cat_operand,751 slice_cat_operand,
752 comptime_call_target,
753 inline_call_target,752 inline_call_target,
754 generic_call_target,753 generic_call_target,
755 wasm_memory_index,754 wasm_memory_index,
...@@ -830,7 +829,6 @@ pub const SimpleComptimeReason = enum(u32) {...@@ -830,7 +829,6 @@ pub const SimpleComptimeReason = enum(u32) {
830 .atomic_order => "atomic order must be comptime-known",829 .atomic_order => "atomic order must be comptime-known",
831 .array_mul_factor => "array multiplication factor must be comptime-known",830 .array_mul_factor => "array multiplication factor must be comptime-known",
832 .slice_cat_operand => "slice being concatenated must be comptime-known",831 .slice_cat_operand => "slice being concatenated must be comptime-known",
833 .comptime_call_target => "function being called at comptime must be comptime-known",
834 .inline_call_target => "function being called inline must be comptime-known",832 .inline_call_target => "function being called inline must be comptime-known",
835 .generic_call_target => "generic function being called must be comptime-known",833 .generic_call_target => "generic function being called must be comptime-known",
836 .wasm_memory_index => "wasm memory index must be comptime-known",834 .wasm_memory_index => "wasm memory index must be comptime-known",
src/Sema.zig+32-9
...@@ -8021,26 +8021,49 @@ fn analyzeCall(...@@ -8021,26 +8021,49 @@ fn analyzeCall(
80218021
8022 // This is an inline call. The function must be comptime-known. We will analyze its body directly using this `Sema`.8022 // This is an inline call. The function must be comptime-known. We will analyze its body directly using this `Sema`.
80238023
8024 const call_type: []const u8 = if (block.isComptime()) "comptime" else "inline";8024 if (func_ty_info.is_noinline and !block.isComptime()) {
8025 return sema.fail(block, call_src, "inline call of noinline function", .{});
8026 }
80258027
8028 const call_type: []const u8 = if (block.isComptime()) "comptime" else "inline";
8026 if (modifier == .never_inline) {8029 if (modifier == .never_inline) {
8027 return sema.fail(block, call_src, "cannot perform {s} call with 'never_inline' modifier", .{call_type});8030 const msg, const fail_block = msg: {
8028 }8031 const msg = try sema.errMsg(call_src, "cannot perform {s} call with 'never_inline' modifier", .{call_type});
8029 if (func_ty_info.is_noinline and !block.isComptime()) {8032 errdefer msg.destroy(gpa);
8030 return sema.fail(block, call_src, "{s} call of noinline function", .{call_type});8033 const fail_block = if (block.isComptime()) b: {
8034 break :b try block.explainWhyBlockIsComptime(msg);
8035 } else block;
8036 break :msg .{ msg, fail_block };
8037 };
8038 return sema.failWithOwnedErrorMsg(fail_block, msg);
8031 }8039 }
8032 if (func_ty_info.is_var_args) {8040 if (func_ty_info.is_var_args) {
8033 return sema.fail(block, call_src, "{s} call of variadic function", .{call_type});8041 const msg, const fail_block = msg: {
8042 const msg = try sema.errMsg(call_src, "{s} call of variadic function", .{call_type});
8043 errdefer msg.destroy(gpa);
8044 const fail_block = if (block.isComptime()) b: {
8045 break :b try block.explainWhyBlockIsComptime(msg);
8046 } else block;
8047 break :msg .{ msg, fail_block };
8048 };
8049 return sema.failWithOwnedErrorMsg(fail_block, msg);
8034 }8050 }
8035
8036 if (func_val == null) {8051 if (func_val == null) {
8037 if (func_is_extern) {8052 if (func_is_extern) {
8038 return sema.fail(block, call_src, "{s} call of extern function", .{call_type});8053 const msg, const fail_block = msg: {
8054 const msg = try sema.errMsg(call_src, "{s} call of extern function", .{call_type});
8055 errdefer msg.destroy(gpa);
8056 const fail_block = if (block.isComptime()) b: {
8057 break :b try block.explainWhyBlockIsComptime(msg);
8058 } else block;
8059 break :msg .{ msg, fail_block };
8060 };
8061 return sema.failWithOwnedErrorMsg(fail_block, msg);
8039 }8062 }
8040 return sema.failWithNeededComptime(8063 return sema.failWithNeededComptime(
8041 block,8064 block,
8042 func_src,8065 func_src,
8043 .{ .simple = if (block.isComptime()) .comptime_call_target else .inline_call_target },8066 if (block.isComptime()) null else .{ .simple = .inline_call_target },
8044 );8067 );
8045 }8068 }
80468069
test/cases/compile_errors/complex_comptime_call_of_extern_function.zig created+40
...@@ -0,0 +1,40 @@
1extern fn next_id() u32;
2
3const Foo = struct {
4 bar: Bar,
5
6 fn init() Foo {
7 return .{ .bar = .init() };
8 }
9};
10const Bar = struct {
11 qux: ?Qux,
12 id: u32,
13
14 fn init() Bar {
15 return .{
16 .qux = null,
17 .id = next_id(),
18 };
19 }
20};
21const Qux = struct {
22 handleThing: fn () void,
23};
24
25export fn entry() void {
26 const foo: Foo = .init();
27 _ = foo;
28}
29
30// error
31//
32// :17:26: error: comptime call of extern function
33// :7:31: note: called at comptime from here
34// :26:27: note: called at comptime from here
35// :26:27: note: call to function with comptime-only return type 'tmp.Foo' is evaluated at comptime
36// :6:15: note: return type declared here
37// :4:10: note: struct requires comptime because of this field
38// :11:10: note: struct requires comptime because of this field
39// :22:18: note: struct requires comptime because of this field
40// :22:18: note: use '*const fn () void' for a function pointer type
test/cases/compile_errors/comptime_call_of_function_pointer.zig+1-1
...@@ -6,4 +6,4 @@ export fn entry() void {...@@ -6,4 +6,4 @@ export fn entry() void {
6// error6// error
7//7//
8// :3:14: error: unable to resolve comptime value8// :3:14: error: unable to resolve comptime value
9// :3:14: note: function being called at comptime must be comptime-known9// :3:5: note: 'comptime' keyword forces comptime evaluation
test/cases/compile_errors/global_variable_initializer_must_be_constant_expression.zig+1
...@@ -7,3 +7,4 @@ export fn entry() i32 {...@@ -7,3 +7,4 @@ export fn entry() i32 {
7// error7// error
8//8//
9// :2:14: error: comptime call of extern function9// :2:14: error: comptime call of extern function
10// :2:14: note: initializer of container-level variable must be comptime-known
test/cases/compile_errors/invalid_extern_function_call.zig+1
...@@ -11,4 +11,5 @@ export fn entry1() void {...@@ -11,4 +11,5 @@ export fn entry1() void {
11// error11// error
12//12//
13// :4:15: error: comptime call of extern function13// :4:15: error: comptime call of extern function
14// :4:5: note: 'comptime' keyword forces comptime evaluation
14// :8:5: error: inline call of extern function15// :8:5: error: inline call of extern function
test/cases/compile_errors/invalid_pointer_for_var_type.zig+1
...@@ -9,3 +9,4 @@ export fn f() void {...@@ -9,3 +9,4 @@ export fn f() void {
9// error9// error
10//10//
11// :2:16: error: comptime call of extern function11// :2:16: error: comptime call of extern function
12// :2:12: note: types must be comptime-known
test/cases/compile_errors/non-const_expression_in_struct_literal_outside_function.zig+1
...@@ -11,3 +11,4 @@ export fn entry() usize {...@@ -11,3 +11,4 @@ export fn entry() usize {
11// error11// error
12//12//
13// :4:27: error: comptime call of extern function13// :4:27: error: comptime call of extern function
14// :4:14: note: initializer of container-level variable must be comptime-known