About comptime scope

In the following code, the first double(3) call is treated as in a comptime scope, but the second one is not. I can understand the design. Just wonder whether or not it is a good idea to also treat the second one also as in comptime scope, implicitly, just like initializers of global declarations?

fn double(n: u64) @TypeOf(n) {
    return n + n;
}

pub fn main() void {
    const std = @import("std");

    comptime var x: u64 = double(3); // The call must not be marked with comptime
    x = x;
    
    comptime var y: u64 = undefined;
    y = double(3); // error: The call is required to be marked with comptime
    
    std.debug.print("x = {}, y = {}\n", .{ x, y });
}
3 Likes

these kinds of questions belong in the Explain category

I prefer an explicit comptime because there are semantic differences between comptime and runtime, so it is important to be able to tell them apart.

3 Likes

To be honest I’m more surprised that the first comptime var declaration places the initial assignment expression in a comptime scope. It’s legal for an inline fn to return a comptime-only result but take runtime-known arguments and have runtime side effects, but the current behavior means that to assign a comptime var from an inline function call with side effects you need to declare it and assign it as separate statements:

const std = @import("std");

pub fn main() void {
    var x: i32 = 0;
    const y: comptime_int = f(&x);
    //comptime var z: comptime_int = f(&x); // error: unable to resolve comptime value
    //_ = &z;
    comptime var z: comptime_int = undefined;
    z = f(&x);
    std.debug.print("x = {}, y = {}, z = {}\n", .{ x, y, z });
    // prints: x = 2, y = 0, z = 0
}

inline fn f(x: *i32) comptime_int {
    x.* += 1;
    return if (@inComptime()) 1 else 0;
}

If assigning a comptime var always placed the assignment expression in a comptime scope then you wouldn’t be able to perform the assignment above at all.

I think it would be more sound to require comptime on both sides, i.e.

comptime var x: u64 = comptime double(3);
3 Likes

Interesting, TIL. So the source values assigned to comptime var values may be runtime expressions.

Need some time to digest this.

Here’s another variation of the above example:

const std = @import("std");

pub fn main() void {
    var x: i32 = 0;
    const y: comptime_int = five: {
        x += 1;
        break :five 5;
    };
    comptime var z: comptime_int = undefined;
    z = five: {
        x += 1;
        break :five 5;
    };
    std.debug.print("x = {}, y = {}, z = {}\n", .{ x, y, z });
    // prints: x = 2, y = 5, z = 5
}

So the important detail is that while the expression may have runtime side effects, the actual result must be comptime-known.

4 Likes