ziglang / zig

General-purpose programming language and toolchain for maintaining robust, optimal, and reusable software.
https://ziglang.org
MIT License
34.85k stars 2.55k forks source link

`@TypeOf(f(x))` with non-`inline` `f` in return type expression leads to compile error with either pointer to `comptime var` or runtime value #21213

Open rohlem opened 2 months ago

rohlem commented 2 months ago

Zig Version

0.14.0-dev.617+208baa37c

Steps to Reproduce and Observed Behavior

minimized example:

pub fn noop(x: anytype) @TypeOf(x) {
    return x;
}
pub fn foo(x: anytype) @TypeOf(noop(x)) {
    return noop(x);
}
test {
    comptime var v: type = void;
    _ = comptime foo(&v);
}

zig test .zig output:

.zig:4:37: error: runtime value contains reference to comptime var
pub fn foo(x: anytype) @TypeOf(noop(x)) {
                                    ^
.zig:4:37: note: comptime var pointers are not available at runtime
.zig:9:21: note: called from here
    _ = comptime foo(&v);
                 ~~~^~~~

Expected Behavior

The function is called in a comptime context, so that instantiation should be fully evaluated at comptime. Further, the flagged occurrence is in the return type expression, where the value should already only be available if it is a comptime-only type. If the evaluation of noop (or in this case of its return type) at comptime fails, the error should state that. Otherwise I believe within comptime contexts the compiler should allow resolving it for comptime-only pointers as arguments.

Marking noop as inline resolves the compile error; that to me seems like another strange quirk that may not be intended.

NicoElbers commented 2 months ago

Changing your program to:

pub fn noop(x: anytype) @TypeOf(x) {
    return x;
}
pub fn foo(x: anytype) @TypeOf(comptime noop(x)) { // note the comptime
    return noop(x);
}
test {
    comptime var v: type = void;
    _ = comptime foo(&v);
}

compiles and runs fine.

I'm not sure if this would be a bug or weird syntax, either way I feel this is pretty intuitive. I'd assume the compiler could at least infer return type of @TypeOf(noop(x))

rohlem commented 2 months ago

@NicoElbers Thanks for the idea, that indeed works! The reason I hadn't tried it before is that return type expressions are already comptime contexts based on grammar. The compiler currently forbids the comptime operator if it's seen as redundant:

.zig:4:24: error: redundant comptime keyword in already comptime scope
pub fn foo(x: anytype) comptime @TypeOf(noop(x)) {
                       ^~~~~~~~~~~~~~~~~~~~~~~~~

I don't see a reason why the argument of @TypeOf in a comptime context wouldn't already be considered to be in a comptime context as well - that seems like an unintentional oversight.

rohlem commented 2 months ago

Turns out the workaround actually doesn't satisfy my use case: If I add comptime inside the @TypeOf argument, it errors when given a runtime value as an argument.

Expanded example:

```zig pub fn noop(x: anytype) @TypeOf(x) { return x; } pub fn foo(x: anytype) @TypeOf(comptime noop(x)) { return noop(x); } test { comptime var v: type = void; _ = comptime foo(&v); var x: u2 = 3; _ = &x; _ = foo(x); } ``` The error this triggers: ``` .zig:4:46: error: unable to resolve comptime value pub fn foo(x: anytype) @TypeOf(comptime noop(x)) { ^ .zig:4:46: note: argument to function being called at comptime must be comptime-known ``` So adding `comptime` breaks `foo(x)`, while removing `comptime` breaks `foo(&v)`. I next tried to cleverly work around this using `@inComptime()` within the `@TypeOf` argument: `pub fn foo(x: anytype) @TypeOf(if (@inComptime()) (comptime noop(x)) else (noop(x)))` however, it seems like for both calls the `@inComptime() == false` branch is taken. This currently leads me to the conclusion that this use case is impossible to solve by a (EDIT: non-`inline`) function in status-quo. I'll probably need to flag each call site individually (split it into 2 functions, or introduce a `comptime bool` argument). ---

EDIT: Got confused analyzing/discussing this other workaround - my original idea of declaring the function inline does work in both scenarios, so that will be my way forward for the time being.