| author | |
| committer | |
| log | 107b65ec5dccd360410b4a693bb0caf0015312f3 |
| tree | 7cd66974406b4f3423e746f1ca24c0e7f0cabb94 |
| parent | 484f72311eae288ed5c7a648ec016b02f4547f48 |
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( |
| 8021 | 8021 | ||
| 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`. |
| 8023 | 8023 | ||
| 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 | } | ||
| 8025 | 8027 | ||
| 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 | } |
| 8046 | 8069 |
test/cases/compile_errors/complex_comptime_call_of_extern_function.zig created+40| ... | @@ -0,0 +1,40 @@ | ||
| 1 | extern fn next_id() u32; | ||
| 2 | |||
| 3 | const Foo = struct { | ||
| 4 | bar: Bar, | ||
| 5 | |||
| 6 | fn init() Foo { | ||
| 7 | return .{ .bar = .init() }; | ||
| 8 | } | ||
| 9 | }; | ||
| 10 | const 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 | }; | ||
| 21 | const Qux = struct { | ||
| 22 | handleThing: fn () void, | ||
| 23 | }; | ||
| 24 | |||
| 25 | export 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 | // error | 6 | // error |
| 7 | // | 7 | // |
| 8 | // :3:14: error: unable to resolve comptime value | 8 | // :3:14: error: unable to resolve comptime value |
| 9 | // :3:14: note: function being called at comptime must be comptime-known | 9 | // :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 | // error | 7 | // error |
| 8 | // | 8 | // |
| 9 | // :2:14: error: comptime call of extern function | 9 | // :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 | // error | 11 | // error |
| 12 | // | 12 | // |
| 13 | // :4:15: error: comptime call of extern function | 13 | // :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 function | 15 | // :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 | // error | 9 | // error |
| 10 | // | 10 | // |
| 11 | // :2:16: error: comptime call of extern function | 11 | // :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 | // error | 11 | // error |
| 12 | // | 12 | // |
| 13 | // :4:27: error: comptime call of extern function | 13 | // :4:27: error: comptime call of extern function |
| 14 | // :4:14: note: initializer of container-level variable must be comptime-known |