| author | |
| committer | |
| log | ae8d26a6a00a528bdf555689c2a93cb35a3287f2 |
| tree | d72b651523e62b81d0b1733cf2f1a4eced7265db |
| parent | da95da438ed5c934a9f42811651f5ffa524e48a8 |
Adds error for taking a non comptime parameter in a function returning a
comptime-only type but not when that type is dependent on a parameter.
Co-authored-by: Veikka Tuominen <git@vexu.eu>10 files changed, 95 insertions(+), 26 deletions(-)
lib/std/c.zig+1-1| ... | @@ -20,7 +20,7 @@ pub const Tokenizer = tokenizer.Tokenizer; | ... | @@ -20,7 +20,7 @@ pub const Tokenizer = tokenizer.Tokenizer; |
| 20 | /// If linking gnu libc (glibc), the `ok` value will be true if the target | 20 | /// If linking gnu libc (glibc), the `ok` value will be true if the target |
| 21 | /// version is greater than or equal to `glibc_version`. | 21 | /// version is greater than or equal to `glibc_version`. |
| 22 | /// If linking a libc other than these, returns `false`. | 22 | /// If linking a libc other than these, returns `false`. |
| 23 | pub fn versionCheck(glibc_version: std.builtin.Version) type { | 23 | pub fn versionCheck(comptime glibc_version: std.builtin.Version) type { |
| 24 | return struct { | 24 | return struct { |
| 25 | pub const ok = blk: { | 25 | pub const ok = blk: { |
| 26 | if (!builtin.link_libc) break :blk false; | 26 | if (!builtin.link_libc) break :blk false; |
lib/std/elf.zig+2-2| ... | @@ -406,7 +406,7 @@ pub const Header = struct { | ... | @@ -406,7 +406,7 @@ pub const Header = struct { |
| 406 | } | 406 | } |
| 407 | }; | 407 | }; |
| 408 | 408 | ||
| 409 | pub fn ProgramHeaderIterator(ParseSource: anytype) type { | 409 | pub fn ProgramHeaderIterator(comptime ParseSource: anytype) type { |
| 410 | return struct { | 410 | return struct { |
| 411 | elf_header: Header, | 411 | elf_header: Header, |
| 412 | parse_source: ParseSource, | 412 | parse_source: ParseSource, |
| ... | @@ -456,7 +456,7 @@ pub fn ProgramHeaderIterator(ParseSource: anytype) type { | ... | @@ -456,7 +456,7 @@ pub fn ProgramHeaderIterator(ParseSource: anytype) type { |
| 456 | }; | 456 | }; |
| 457 | } | 457 | } |
| 458 | 458 | ||
| 459 | pub fn SectionHeaderIterator(ParseSource: anytype) type { | 459 | pub fn SectionHeaderIterator(comptime ParseSource: anytype) type { |
| 460 | return struct { | 460 | return struct { |
| 461 | elf_header: Header, | 461 | elf_header: Header, |
| 462 | parse_source: ParseSource, | 462 | parse_source: ParseSource, |
lib/std/io/bit_reader.zig+1-1| ... | @@ -7,7 +7,7 @@ const meta = std.meta; | ... | @@ -7,7 +7,7 @@ const meta = std.meta; |
| 7 | const math = std.math; | 7 | const math = std.math; |
| 8 | 8 | ||
| 9 | /// Creates a stream which allows for reading bit fields from another stream | 9 | /// Creates a stream which allows for reading bit fields from another stream |
| 10 | pub fn BitReader(endian: std.builtin.Endian, comptime ReaderType: type) type { | 10 | pub fn BitReader(comptime endian: std.builtin.Endian, comptime ReaderType: type) type { |
| 11 | return struct { | 11 | return struct { |
| 12 | forward_reader: ReaderType, | 12 | forward_reader: ReaderType, |
| 13 | bit_buffer: u7, | 13 | bit_buffer: u7, |
lib/std/io/bit_writer.zig+1-1| ... | @@ -7,7 +7,7 @@ const meta = std.meta; | ... | @@ -7,7 +7,7 @@ const meta = std.meta; |
| 7 | const math = std.math; | 7 | const math = std.math; |
| 8 | 8 | ||
| 9 | /// Creates a stream which allows for writing bit fields to another stream | 9 | /// Creates a stream which allows for writing bit fields to another stream |
| 10 | pub fn BitWriter(endian: std.builtin.Endian, comptime WriterType: type) type { | 10 | pub fn BitWriter(comptime endian: std.builtin.Endian, comptime WriterType: type) type { |
| 11 | return struct { | 11 | return struct { |
| 12 | forward_writer: WriterType, | 12 | forward_writer: WriterType, |
| 13 | bit_buffer: u8, | 13 | bit_buffer: u8, |
lib/std/meta.zig+1-1| ... | @@ -764,7 +764,7 @@ const TagPayloadType = TagPayload; | ... | @@ -764,7 +764,7 @@ const TagPayloadType = TagPayload; |
| 764 | 764 | ||
| 765 | ///Given a tagged union type, and an enum, return the type of the union | 765 | ///Given a tagged union type, and an enum, return the type of the union |
| 766 | /// field corresponding to the enum tag. | 766 | /// field corresponding to the enum tag. |
| 767 | pub fn TagPayload(comptime U: type, tag: Tag(U)) type { | 767 | pub fn TagPayload(comptime U: type, comptime tag: Tag(U)) type { |
| 768 | comptime debug.assert(trait.is(.Union)(U)); | 768 | comptime debug.assert(trait.is(.Union)(U)); |
| 769 | 769 | ||
| 770 | const info = @typeInfo(U).Union; | 770 | const info = @typeInfo(U).Union; |
lib/std/multi_array_list.zig+1-1| ... | @@ -459,7 +459,7 @@ pub fn MultiArrayList(comptime S: type) type { | ... | @@ -459,7 +459,7 @@ pub fn MultiArrayList(comptime S: type) type { |
| 459 | return self.bytes[0..capacityInBytes(self.capacity)]; | 459 | return self.bytes[0..capacityInBytes(self.capacity)]; |
| 460 | } | 460 | } |
| 461 | 461 | ||
| 462 | fn FieldType(field: Field) type { | 462 | fn FieldType(comptime field: Field) type { |
| 463 | return meta.fieldInfo(S, field).field_type; | 463 | return meta.fieldInfo(S, field).field_type; |
| 464 | } | 464 | } |
| 465 | 465 |
src/Sema.zig+47-14| ... | @@ -7816,19 +7816,17 @@ fn funcCommon( | ... | @@ -7816,19 +7816,17 @@ fn funcCommon( |
| 7816 | }; | 7816 | }; |
| 7817 | } | 7817 | } |
| 7818 | 7818 | ||
| 7819 | var is_comptime_ret = false; | 7819 | var ret_ty_requires_comptime = false; |
| 7820 | const ret_poison = if (!is_generic) rp: { | 7820 | const ret_poison = if (sema.typeRequiresComptime(block, ret_ty_src, bare_return_type)) |ret_comptime| rp: { |
| 7821 | if (sema.typeRequiresComptime(block, ret_ty_src, bare_return_type)) |ret_comptime| { | 7821 | ret_ty_requires_comptime = ret_comptime; |
| 7822 | is_comptime_ret = ret_comptime; | 7822 | break :rp bare_return_type.tag() == .generic_poison; |
| 7823 | break :rp bare_return_type.tag() == .generic_poison; | 7823 | } else |err| switch (err) { |
| 7824 | } else |err| switch (err) { | 7824 | error.GenericPoison => rp: { |
| 7825 | error.GenericPoison => { | 7825 | is_generic = true; |
| 7826 | is_generic = true; | 7826 | break :rp true; |
| 7827 | break :rp true; | 7827 | }, |
| 7828 | }, | 7828 | else => |e| return e, |
| 7829 | else => |e| return e, | 7829 | }; |
| 7830 | } | ||
| 7831 | } else bare_return_type.tag() == .generic_poison; | ||
| 7832 | 7830 | ||
| 7833 | const return_type = if (!inferred_error_set or ret_poison) | 7831 | const return_type = if (!inferred_error_set or ret_poison) |
| 7834 | bare_return_type | 7832 | bare_return_type |
| ... | @@ -7873,6 +7871,41 @@ fn funcCommon( | ... | @@ -7873,6 +7871,41 @@ fn funcCommon( |
| 7873 | return sema.failWithOwnedErrorMsg(msg); | 7871 | return sema.failWithOwnedErrorMsg(msg); |
| 7874 | } | 7872 | } |
| 7875 | 7873 | ||
| 7874 | // If the return type is comptime only but not dependent on parameters then all parameter types also need to be comptime | ||
| 7875 | if (!sema.is_generic_instantiation and has_body and ret_ty_requires_comptime) comptime_check: { | ||
| 7876 | for (block.params.items) |param| { | ||
| 7877 | if (!param.is_comptime) break; | ||
| 7878 | } else break :comptime_check; | ||
| 7879 | |||
| 7880 | const msg = try sema.errMsg( | ||
| 7881 | block, | ||
| 7882 | ret_ty_src, | ||
| 7883 | "function with comptime only return type '{}' requires all parameters to be comptime", | ||
| 7884 | .{return_type.fmt(sema.mod)}, | ||
| 7885 | ); | ||
| 7886 | try sema.explainWhyTypeIsComptime(block, ret_ty_src, msg, ret_ty_src.toSrcLoc(sema.owner_decl), return_type); | ||
| 7887 | |||
| 7888 | const tags = sema.code.instructions.items(.tag); | ||
| 7889 | const data = sema.code.instructions.items(.data); | ||
| 7890 | const param_body = sema.code.getParamBody(func_inst); | ||
| 7891 | for (block.params.items) |param, i| { | ||
| 7892 | if (!param.is_comptime) { | ||
| 7893 | const param_index = param_body[i]; | ||
| 7894 | const param_src = switch (tags[param_index]) { | ||
| 7895 | .param => data[param_index].pl_tok.src(), | ||
| 7896 | .param_anytype => data[param_index].str_tok.src(), | ||
| 7897 | else => unreachable, | ||
| 7898 | }; | ||
| 7899 | if (param.name.len != 0) { | ||
| 7900 | try sema.errNote(block, param_src, msg, "param '{s}' is required to be comptime", .{param.name}); | ||
| 7901 | } else { | ||
| 7902 | try sema.errNote(block, param_src, msg, "param is required to be comptime", .{}); | ||
| 7903 | } | ||
| 7904 | } | ||
| 7905 | } | ||
| 7906 | return sema.failWithOwnedErrorMsg(msg); | ||
| 7907 | } | ||
| 7908 | |||
| 7876 | const arch = sema.mod.getTarget().cpu.arch; | 7909 | const arch = sema.mod.getTarget().cpu.arch; |
| 7877 | if (switch (cc_workaround) { | 7910 | if (switch (cc_workaround) { |
| 7878 | .Unspecified, .C, .Naked, .Async, .Inline => null, | 7911 | .Unspecified, .C, .Naked, .Async, .Inline => null, |
| ... | @@ -7917,7 +7950,7 @@ fn funcCommon( | ... | @@ -7917,7 +7950,7 @@ fn funcCommon( |
| 7917 | } | 7950 | } |
| 7918 | if (is_generic and sema.no_partial_func_ty) return error.GenericPoison; | 7951 | if (is_generic and sema.no_partial_func_ty) return error.GenericPoison; |
| 7919 | for (comptime_params) |ct| is_generic = is_generic or ct; | 7952 | for (comptime_params) |ct| is_generic = is_generic or ct; |
| 7920 | is_generic = is_generic or is_comptime_ret; | 7953 | is_generic = is_generic or ret_ty_requires_comptime; |
| 7921 | 7954 | ||
| 7922 | break :fn_ty try Type.Tag.function.create(sema.arena, .{ | 7955 | break :fn_ty try Type.Tag.function.create(sema.arena, .{ |
| 7923 | .param_types = param_types, | 7956 | .param_types = param_types, |
test/behavior/union.zig+1-1| ... | @@ -745,7 +745,7 @@ fn setAttribute(attr: Attribute) void { | ... | @@ -745,7 +745,7 @@ fn setAttribute(attr: Attribute) void { |
| 745 | _ = attr; | 745 | _ = attr; |
| 746 | } | 746 | } |
| 747 | 747 | ||
| 748 | fn Setter(attr: Attribute) type { | 748 | fn Setter(comptime attr: Attribute) type { |
| 749 | return struct { | 749 | return struct { |
| 750 | fn set() void { | 750 | fn set() void { |
| 751 | setAttribute(attr); | 751 | setAttribute(attr); |
test/cases/compile_errors/explain_why_fn_is_called_at_comptime.zig+4-4| ... | @@ -4,12 +4,12 @@ const S = struct { | ... | @@ -4,12 +4,12 @@ const S = struct { |
| 4 | }; | 4 | }; |
| 5 | fn bar() void {} | 5 | fn bar() void {} |
| 6 | 6 | ||
| 7 | fn foo(a: u8) S { | 7 | fn foo(comptime a: *u8) S { |
| 8 | return .{ .fnPtr = bar, .a = a }; | 8 | return .{ .fnPtr = bar, .a = a.* }; |
| 9 | } | 9 | } |
| 10 | pub export fn entry() void { | 10 | pub export fn entry() void { |
| 11 | var a: u8 = 1; | 11 | var a: u8 = 1; |
| 12 | _ = foo(a); | 12 | _ = foo(&a); |
| 13 | } | 13 | } |
| 14 | 14 | ||
| 15 | // error | 15 | // error |
| ... | @@ -18,6 +18,6 @@ pub export fn entry() void { | ... | @@ -18,6 +18,6 @@ pub export fn entry() void { |
| 18 | // | 18 | // |
| 19 | // :12:13: error: unable to resolve comptime value | 19 | // :12:13: error: unable to resolve comptime value |
| 20 | // :12:13: note: argument to function being called at comptime must be comptime known | 20 | // :12:13: note: argument to function being called at comptime must be comptime known |
| 21 | // :7:15: note: function is being called at comptime because it returns a comptime only type 'tmp.S' | 21 | // :7:25: note: function is being called at comptime because it returns a comptime only type 'tmp.S' |
| 22 | // :2:12: note: struct requires comptime because of this field | 22 | // :2:12: note: struct requires comptime because of this field |
| 23 | // :2:12: note: use '*const fn() void' for a function pointer type | 23 | // :2:12: note: use '*const fn() void' for a function pointer type |
test/cases/compile_errors/non_comptime_param_in_comptime_function.zig created+36| ... | @@ -0,0 +1,36 @@ | ||
| 1 | fn F(val: anytype) type { | ||
| 2 | _ = val; | ||
| 3 | return struct {}; | ||
| 4 | } | ||
| 5 | export fn entry() void { | ||
| 6 | _ = F(void{}); | ||
| 7 | } | ||
| 8 | const S = struct { | ||
| 9 | foo: fn () void, | ||
| 10 | }; | ||
| 11 | fn bar(_: u32) S { | ||
| 12 | return undefined; | ||
| 13 | } | ||
| 14 | export fn entry1() void { | ||
| 15 | _ = bar(); | ||
| 16 | } | ||
| 17 | // prioritize other return type errors | ||
| 18 | fn foo(a: u32) callconv(.C) comptime_int { | ||
| 19 | return a; | ||
| 20 | } | ||
| 21 | export fn entry2() void { | ||
| 22 | _ = foo(1); | ||
| 23 | } | ||
| 24 | |||
| 25 | // error | ||
| 26 | // backend=stage2 | ||
| 27 | // target=native | ||
| 28 | // | ||
| 29 | // :1:20: error: function with comptime only return type 'type' requires all parameters to be comptime | ||
| 30 | // :1:20: note: types are not available at runtime | ||
| 31 | // :1:6: note: param 'val' is required to be comptime | ||
| 32 | // :11:16: error: function with comptime only return type 'tmp.S' requires all parameters to be comptime | ||
| 33 | // :9:10: note: struct requires comptime because of this field | ||
| 34 | // :9:10: note: use '*const fn() void' for a function pointer type | ||
| 35 | // :11:8: note: param is required to be comptime | ||
| 36 | // :18:29: error: return type 'comptime_int' not allowed in function with calling convention 'C' | ||