Commit ae8d26a6a0

antlilja <liljaanton2001@gmail.com>
2022-08-06 22:32:00
Sema: add error for non-comptime param in comptime func
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>
1 parent da95da4
lib/std/io/bit_reader.zig
@@ -7,7 +7,7 @@ const meta = std.meta;
 const math = std.math;
 
 /// Creates a stream which allows for reading bit fields from another stream
-pub fn BitReader(endian: std.builtin.Endian, comptime ReaderType: type) type {
+pub fn BitReader(comptime endian: std.builtin.Endian, comptime ReaderType: type) type {
     return struct {
         forward_reader: ReaderType,
         bit_buffer: u7,
lib/std/io/bit_writer.zig
@@ -7,7 +7,7 @@ const meta = std.meta;
 const math = std.math;
 
 /// Creates a stream which allows for writing bit fields to another stream
-pub fn BitWriter(endian: std.builtin.Endian, comptime WriterType: type) type {
+pub fn BitWriter(comptime endian: std.builtin.Endian, comptime WriterType: type) type {
     return struct {
         forward_writer: WriterType,
         bit_buffer: u8,
lib/std/c.zig
@@ -20,7 +20,7 @@ pub const Tokenizer = tokenizer.Tokenizer;
 /// If linking gnu libc (glibc), the `ok` value will be true if the target
 /// version is greater than or equal to `glibc_version`.
 /// If linking a libc other than these, returns `false`.
-pub fn versionCheck(glibc_version: std.builtin.Version) type {
+pub fn versionCheck(comptime glibc_version: std.builtin.Version) type {
     return struct {
         pub const ok = blk: {
             if (!builtin.link_libc) break :blk false;
lib/std/elf.zig
@@ -406,7 +406,7 @@ pub const Header = struct {
     }
 };
 
-pub fn ProgramHeaderIterator(ParseSource: anytype) type {
+pub fn ProgramHeaderIterator(comptime ParseSource: anytype) type {
     return struct {
         elf_header: Header,
         parse_source: ParseSource,
@@ -456,7 +456,7 @@ pub fn ProgramHeaderIterator(ParseSource: anytype) type {
     };
 }
 
-pub fn SectionHeaderIterator(ParseSource: anytype) type {
+pub fn SectionHeaderIterator(comptime ParseSource: anytype) type {
     return struct {
         elf_header: Header,
         parse_source: ParseSource,
lib/std/meta.zig
@@ -764,7 +764,7 @@ const TagPayloadType = TagPayload;
 
 ///Given a tagged union type, and an enum, return the type of the union
 /// field corresponding to the enum tag.
-pub fn TagPayload(comptime U: type, tag: Tag(U)) type {
+pub fn TagPayload(comptime U: type, comptime tag: Tag(U)) type {
     comptime debug.assert(trait.is(.Union)(U));
 
     const info = @typeInfo(U).Union;
lib/std/multi_array_list.zig
@@ -459,7 +459,7 @@ pub fn MultiArrayList(comptime S: type) type {
             return self.bytes[0..capacityInBytes(self.capacity)];
         }
 
-        fn FieldType(field: Field) type {
+        fn FieldType(comptime field: Field) type {
             return meta.fieldInfo(S, field).field_type;
         }
 
src/Sema.zig
@@ -7816,19 +7816,17 @@ fn funcCommon(
             };
         }
 
-        var is_comptime_ret = false;
-        const ret_poison = if (!is_generic) rp: {
-            if (sema.typeRequiresComptime(block, ret_ty_src, bare_return_type)) |ret_comptime| {
-                is_comptime_ret = ret_comptime;
-                break :rp bare_return_type.tag() == .generic_poison;
-            } else |err| switch (err) {
-                error.GenericPoison => {
-                    is_generic = true;
-                    break :rp true;
-                },
-                else => |e| return e,
-            }
-        } else bare_return_type.tag() == .generic_poison;
+        var ret_ty_requires_comptime = false;
+        const ret_poison = if (sema.typeRequiresComptime(block, ret_ty_src, bare_return_type)) |ret_comptime| rp: {
+            ret_ty_requires_comptime = ret_comptime;
+            break :rp bare_return_type.tag() == .generic_poison;
+        } else |err| switch (err) {
+            error.GenericPoison => rp: {
+                is_generic = true;
+                break :rp true;
+            },
+            else => |e| return e,
+        };
 
         const return_type = if (!inferred_error_set or ret_poison)
             bare_return_type
@@ -7873,6 +7871,41 @@ fn funcCommon(
             return sema.failWithOwnedErrorMsg(msg);
         }
 
+        // If the return type is comptime only but not dependent on parameters then all parameter types also need to be comptime
+        if (!sema.is_generic_instantiation and has_body and ret_ty_requires_comptime) comptime_check: {
+            for (block.params.items) |param| {
+                if (!param.is_comptime) break;
+            } else break :comptime_check;
+
+            const msg = try sema.errMsg(
+                block,
+                ret_ty_src,
+                "function with comptime only return type '{}' requires all parameters to be comptime",
+                .{return_type.fmt(sema.mod)},
+            );
+            try sema.explainWhyTypeIsComptime(block, ret_ty_src, msg, ret_ty_src.toSrcLoc(sema.owner_decl), return_type);
+
+            const tags = sema.code.instructions.items(.tag);
+            const data = sema.code.instructions.items(.data);
+            const param_body = sema.code.getParamBody(func_inst);
+            for (block.params.items) |param, i| {
+                if (!param.is_comptime) {
+                    const param_index = param_body[i];
+                    const param_src = switch (tags[param_index]) {
+                        .param => data[param_index].pl_tok.src(),
+                        .param_anytype => data[param_index].str_tok.src(),
+                        else => unreachable,
+                    };
+                    if (param.name.len != 0) {
+                        try sema.errNote(block, param_src, msg, "param '{s}' is required to be comptime", .{param.name});
+                    } else {
+                        try sema.errNote(block, param_src, msg, "param is required to be comptime", .{});
+                    }
+                }
+            }
+            return sema.failWithOwnedErrorMsg(msg);
+        }
+
         const arch = sema.mod.getTarget().cpu.arch;
         if (switch (cc_workaround) {
             .Unspecified, .C, .Naked, .Async, .Inline => null,
@@ -7917,7 +7950,7 @@ fn funcCommon(
         }
         if (is_generic and sema.no_partial_func_ty) return error.GenericPoison;
         for (comptime_params) |ct| is_generic = is_generic or ct;
-        is_generic = is_generic or is_comptime_ret;
+        is_generic = is_generic or ret_ty_requires_comptime;
 
         break :fn_ty try Type.Tag.function.create(sema.arena, .{
             .param_types = param_types,
test/behavior/union.zig
@@ -745,7 +745,7 @@ fn setAttribute(attr: Attribute) void {
     _ = attr;
 }
 
-fn Setter(attr: Attribute) type {
+fn Setter(comptime attr: Attribute) type {
     return struct {
         fn set() void {
             setAttribute(attr);
test/cases/compile_errors/explain_why_fn_is_called_at_comptime.zig
@@ -4,12 +4,12 @@ const S = struct {
 };
 fn bar() void {}
 
-fn foo(a: u8) S {
-    return .{ .fnPtr = bar, .a = a };
+fn foo(comptime a: *u8) S {
+    return .{ .fnPtr = bar, .a = a.* };
 }
 pub export fn entry() void {
     var a: u8 = 1;
-    _ = foo(a);
+    _ = foo(&a);
 }
 
 // error
@@ -18,6 +18,6 @@ pub export fn entry() void {
 //
 // :12:13: error: unable to resolve comptime value
 // :12:13: note: argument to function being called at comptime must be comptime known
-// :7:15: note: function is being called at comptime because it returns a comptime only type 'tmp.S'
+// :7:25: note: function is being called at comptime because it returns a comptime only type 'tmp.S'
 // :2:12: note: struct requires comptime because of this field
 // :2:12: note: use '*const fn() void' for a function pointer type
test/cases/compile_errors/non_comptime_param_in_comptime_function.zig
@@ -0,0 +1,36 @@
+fn F(val: anytype) type {
+    _ = val;
+    return struct {};
+}
+export fn entry() void {
+    _ = F(void{});
+}
+const S = struct {
+    foo: fn () void,
+};
+fn bar(_: u32) S {
+    return undefined;
+}
+export fn entry1() void {
+    _ = bar();
+}
+// prioritize other return type errors
+fn foo(a: u32) callconv(.C) comptime_int {
+    return a;
+}
+export fn entry2() void {
+    _ = foo(1);
+}
+
+// error
+// backend=stage2
+// target=native
+//
+// :1:20: error: function with comptime only return type 'type' requires all parameters to be comptime
+// :1:20: note: types are not available at runtime
+// :1:6: note: param 'val' is required to be comptime
+// :11:16: error: function with comptime only return type 'tmp.S' requires all parameters to be comptime
+// :9:10: note: struct requires comptime because of this field
+// :9:10: note: use '*const fn() void' for a function pointer type
+// :11:8: note: param is required to be comptime
+// :18:29: error: return type 'comptime_int' not allowed in function with calling convention 'C'