Runtime argument prevents comptime evaluation even if unused

Zig version 0.14.0-dev.2424+7cd2c1ce8

Functions f and g are the same, except that g takes an unused parameter. f can be evaluated at comptime, but g can be evaluated at comptime only if its argument is comptime. Do you think g should be comptime-evaluatable with any (runtime, comptime) argument?

fn f() i32 {
    return 42;
}

fn g(_: i32) i32 {
    return 42;
}

pub fn main() void {
    _ = comptime f();

    var v: i32 = 0;
    _ = &v;
    _ = comptime g(v);
}

Currently this code gives the following error.

src\main.zig:14:20: error: unable to resolve comptime value
    _ = comptime g(v);
                   ^
src\main.zig:14:20: note: argument to function being called at comptime must be comptime-known

Making g inline doesn’t help.

I want to create structs that follow the same (comptime) interface, but give some of the functions of some of the structs an ability to be comptime-evaluatable. A silly example would be

fn S0(comptime i: i32) type {
    return struct {
        pub fn f(_: @This()) i32 {
            return i;
        }
    };
}
const S1 = struct {
    i: i32,
    pub fn f(self: S1) i32 {
        return self.i;
    }
};

Unfortunately, S0(...).f can’t be called at comptime if the object of type S0 is runtime-known.

1 Like

I’ve run into this myself. Zig is currently ‘pessimistic’ about passing runtime-only values into a comptime-only context: it simply doesn’t allow it.

That’s understandable, but I would love to see that become optimistic: for example, a comptime-only function can be passed a runtime-known variable, as long as it’s only used for its type information, or not used at all (there’s little point in that but it would follow from the premise).

But currently that isn’t how it works: runtime-known information can’t cross the comptime boundary at all, the error is at that boundary rather than on a line of code which tries to make comptime use of the runtime information.

2 Likes

A followup: marking something comptime means that it must be executed at comptime, and you’ll get an error if that’s impossible.

If you want to call a runtime-usable function at comptime, you can do that by marking the function call as comptime:

const propagated_constant = comptime fooBusiness(42, true);

Zig will sometimes do this anyway, but it isn’t always clear when: marking the function call comptime will give you a compile error if the call can’t be completed at comptime, but the converse isn’t true: function calls without the comptime tag may or may not become part of the runtime code. It’s a reliable way to check if you’re not sure.

1 Like

Yes, that’s how I showed that f works in comptime and g doesn’t, by forcing comptime evaluation.

Unfortunately, the current state of the things means that I have to dublicate interface as comptime constans, like this

fn S0(comptime i: i32) type {
    return struct {
        const f_const = i;
        pub fn f(_: @This()) i32 {
            return f_const;
        }
    };
}

And call @hasDecl on the calling side to check whether the value is available at comptime.