When is comptime annotation required

This compiles fine:

fn List(comptime value: type) type {
    return struct {
        values: []const value,
    };
}

and so is this, without the comptime annotation:

fn List(value: type) type {
    return struct {
        values: []const value,
    };
}

comptime is still a part of the language I am 100% cluless about, so not sure how to know when it is optional

1 Like

For types that can be used in both runtime and comptime, the keyword comptime forces the parameter to be comptime.
For comptime-only types, like comptime_int and type, there’s been some back and forth about requiring the comptime keyword. Originally it was not mandatory (the compiler would implicitly put the comptime keyword), then they made it mandatory, and based on what you showed they probably rolled that back again. I don’t even know, I just tackle the comptime keyword to keep the compiler happy.

1 Like

Probably the safest thing to do for now. :slight_smile:

This is also confusing to me. At present, it seems the keyword isn’t even necessary. If code makes use of a value that can only be comptime known, it will be assumed to be comptime with or without the keyword. And if it can’t be (because it’s a runtime value), you get an error.

So my current approach is to use the keyword in order to force a compile error a bit earlier, for instance when calling a function that wants to use an argument at comptime.

If I were a language lawyer, I’d require the use of the comptime keyword in all circumstances, in the same way that const and var are required to correctly reflect variable status.

Yeah… That was the goal, but then it caused a whole lot of problems with generic programming. Consider this:

fn A(a: anytype) u8{}

If a is a comptime-only type and you’re missing the comptime keyword, you get a compiler error, so you need two versions of your function, one for comptime-only types and one for everything else, even if they do the same thing. For math functions that can work even with comptime_int types, this becomes a problem.
The solution to that is to make generic functions exempt from the rule. That’s fine, but then you have:

fn List(comptime T: type) type{
  return struct{
    pub fn add(self: *@This(), a: T) void{}
  };
}

What if you want a List(type)? Should work, right? The list can hold any payload, it doesn’t need to know what it is, it just holds it. But you would get a compiler error. It may seem like add is generic, but it isn’t. Once you pass the T to list, there can only be one add, so for the compiler, that is a concrete function, and it’s not exempt from the rule.
The last I checked the issue on this, the solution they were going for was to make the compiler analyse if you arrived at this concrete function through a generic means, and if it depends on any of the generic parameters, make it exempt from the rule. Sounds very convoluted and I guess they gave up on that.

1 Like

Interesting, I tried this and it compiled:

Did I misunderstand? List(type) and List(u16) are both different generic instantiations and they both compile without error.

It compiled because they gave up on requiring the comptime keyword for comptime-only types. Hence the back and forth that I mentioned earlier about this rule. If you switch to zig 0.11 you get the error.

2 Likes