| 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 | 749 | atomic_order, |
| 750 | 750 | array_mul_factor, |
| 751 | 751 | slice_cat_operand, |
| 752 | comptime_call_target, | |
| 753 | 752 | inline_call_target, |
| 754 | 753 | generic_call_target, |
| 755 | 754 | wasm_memory_index, |
| ... | ... | @@ -830,7 +829,6 @@ pub const SimpleComptimeReason = enum(u32) { |
| 830 | 829 | .atomic_order => "atomic order must be comptime-known", |
| 831 | 830 | .array_mul_factor => "array multiplication factor must be comptime-known", |
| 832 | 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 | 832 | .inline_call_target => "function being called inline must be comptime-known", |
| 835 | 833 | .generic_call_target => "generic function being called must be comptime-known", |
| 836 | 834 | .wasm_memory_index => "wasm memory index must be comptime-known", |
src/Sema.zig+32-9| ... | ... | @@ -8021,26 +8021,49 @@ fn analyzeCall( |
| 8021 | 8021 | |
| 8022 | 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 | 8029 | if (modifier == .never_inline) { |
| 8027 | return sema.fail(block, call_src, "cannot perform {s} call with 'never_inline' modifier", .{call_type}); | |
| 8028 | } | |
| 8029 | if (func_ty_info.is_noinline and !block.isComptime()) { | |
| 8030 | return sema.fail(block, call_src, "{s} call of noinline function", .{call_type}); | |
| 8030 | const msg, const fail_block = msg: { | |
| 8031 | const msg = try sema.errMsg(call_src, "cannot perform {s} call with 'never_inline' modifier", .{call_type}); | |
| 8032 | errdefer msg.destroy(gpa); | |
| 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 | 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 | 8051 | if (func_val == null) { |
| 8037 | 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 | 8063 | return sema.failWithNeededComptime( |
| 8041 | 8064 | block, |
| 8042 | 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 | 6 | // error |
| 7 | 7 | // |
| 8 | 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 | 7 | // error |
| 8 | 8 | // |
| 9 | 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 | 11 | // error |
| 12 | 12 | // |
| 13 | 13 | // :4:15: error: comptime call of extern function |
| 14 | // :4:5: note: 'comptime' keyword forces comptime evaluation | |
| 14 | 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 | 9 | // error |
| 10 | 10 | // |
| 11 | 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 | 11 | // error |
| 12 | 12 | // |
| 13 | 13 | // :4:27: error: comptime call of extern function |
| 14 | // :4:14: note: initializer of container-level variable must be comptime-known |