For example, the following code triggers a runtime panic instead of a comptime error.
pub fn main() void {
comptime var t: ?bool = null;
_ = t.?; // runtime panic instead of comptime error
}
Does any official doc mention this point?
For example, the following code triggers a runtime panic instead of a comptime error.
pub fn main() void {
comptime var t: ?bool = null;
_ = t.?; // runtime panic instead of comptime error
}
Does any official doc mention this point?
That line runs at runtime so it makes sense, you have to force it with comptime _ = t.?;
The weirdness is, sometimes t.? is treated as a comptime value, sometimes it is treated as a runtime value.
var b: bool = true;
pub fn main() void {
comptime var t: ?bool = false;
// t.? is viewed as a comptime value.
t.? = b; // error: cannot store runtime value in compile time variable
}
var b: bool = true;
pub fn main() void {
comptime var t: ?bool = null;
// t.? is viewed as a runtime value.
b = t.?; // panic: attempt to use null value
t = t;
}
pub fn main() void {
comptime var t: ?bool = true;
// Both t.? are viewed as comptime values.
t.? = !t.?; // okay
}
For a comptime var variable, a write to it happens at comptime, a read from it takes its value at that point and that value is used at either comptime or runtime depending on the context.
In your 1st example, because you’re writing to a comptime var, the value being written to it has to be comptime known also.
In your 2nd example, because b is a runtime mutable variable, writing to it will always happen at runtime, the write is essentially b = null orelse @panic();
I am not sure what you want to show in the 3rd example.
Many thanks for your explanations.
In the 3rd example, both t.? are viewed as comptime values, right?
And this:
pub fn main() void {
comptime var t: ?bool = true;
comptime var t2: ?bool = null;
// Both t.? and t2.? are viewed as runtime values?
t.? = t2.?; // panic: attempt to use null value
t = t;
t2 = t2;
}
Er, if we always interpret t.? as (t orelse unreachable), then it is easy to understand all the cases.
(t orelse unreachable) is a runtime expression which results a comptime value when reachable.
yeah, I am not really sure why the compiler doesn’t panic at comptime here.
t orelse unreachable doesn’t necessarily mean it runs at runtime, it’s possible that it is forced to run at comptime also by putting comptime in front of it.
Never expected the following code works before. TIL. ![]()
pub fn main() void {
comptime var t: ?bool = null;
var t2: bool = false;
(t orelse t2) = true; // okay
}
That means an expression might result a comptime value or runtime value, even if the expression is used as a destination value.
Yes, the following two are not equivalent.
pub fn main() void {
comptime var t: ?bool = null;
(t orelse unreachable) = true; // panic at runtime
}
pub fn main() void {
comptime var t: ?bool = null;
t.? = true; // error at compile time
}
Maybe they should be equivalent? (to make the explanations simple and consistent.)
I think the issue here is as follows:
calling .? on a variable is safety-check illegal behaviour.
If the zig compiler can detect illegal behaviour at comptime then you get a compile error instead of a crash. (as you can see here, because the variable is comptime known, the compiler knows that what you are calling illegal behaviour at comptime).
I don’t remember where in the documentation I read it, but I am pretty sure I did.
t.? = true is not legal zig In general (Although it works in some examples, I didn’t read anywhere in the docs you can do it). What you are looking for is t = true.
Not the case:
var b: bool = true;
pub fn main() void {
comptime var t: ?bool = null;
// t.? is viewed as a runtime value.
b = t.?; // panic: attempt to use null value
t = t;
}
b = t.?;
This happens at runtime, and therefore triggers a runtime error.
b = t.? runs at runtime.
t.? = true in the original example runs at compile time because t.? is a comptime var.
I agree that the compiler could detect this at compile time, but it doesn’t. I don’t think the current implementation of comptime is written with the goal of detecting every possible time you can reason there’s illegal behaviour.
And these:
pub fn main() void {
const S = struct {
x: bool,
};
comptime var t: ?*S = null;
_ = t.?; // panic: attempt to use null value
t = t;
}
pub fn main() void {
const S = struct {
x: bool,
};
comptime var t: ?*S = null;
_ = t.?.x; // error: unable to unwrap null
t = t;
}
I think I still haven’t got the rules.
Think of the .? operator as a runtime operator by default, even if its operand is comptime-known.
I think Zig’s comptime interpreter just gives up for some reason, I expect the following snippet to fail to compile due to @compileLog, but it doesn’t and no compile log is produced also
pub fn main() !void {
comptime var v0: ?bool = true;
const a: if (v0 == null) i8 else u8 = 1;
std.debug.print("{any}\n", .{@TypeOf(a)});
v0 = (null orelse unreachable);
const b: if (v0 == null) i8 else u8 = 1;
std.debug.print("{any}\n", .{@TypeOf(b)});
@compileLog(std.fmt.comptimePrint("{}\n", .{@TypeOf(b)}));
}
const std = @import("std");
I think this is a bug in the compiler (or at least it should be).
After some discussion on Discord, the behavior is basically if during comptime, a runtime error is known for sure to happen, the compiler will give up on generating the rest of the function.
However, for your example, this does seem to be the compiler is misbehaving. You could check if there’s issue opened related to this or not and report it.
Thanks, I created an issue here: https://codeberg.org/ziglang/zig/issues/35708