Is zig compiler over smart?

This code works:

const std = @import("std");

const T = union {
    x: usize,
    y: bool,
};

var t: T = .{.x = 1};

pub fn main() !void {
    const b: @TypeOf(t.y) = true;
    std.debug.print("b = {}\n", .{b});
}

But when changing t to const, the @TypeOf line gets a error: access of union field 'y' while field 'x' is active error.

Language Reference: union:

The in-memory representation of bare unions is not guaranteed. Bare unions cannot be used to reinterpret memory. For that, use @ptrCast, or use an extern union or a packed union which have guaranteed in-memory layout. Accessing the non-active field is safety-checked Illegal Behavior


But you also could use:

const b: @FieldType(@TypeOf(t), "y") = true;

which avoids the access

5 Likes

it seem it has to do with the way @Typeof work. since its a compile time evaluated function its trying to fully evaluate the y but it can’t. When its just var its can infer the type of the value, its a bool that all it needs to see. When the compiler sees const I imagine it tries to do a full evaluation right there but it see that its not init so it causes the error.

var → shallow analysis
const → compute the value.

3 Likes

That is why I say it is over smart. :smiley: Because the evaluation is unnecessary, just as when t is a var,

I am indeed aware of this way. I just prefer using builtin functions. :slight_smile:

Those are builtin functions, and it works regardless of if t is comptime known so it is objectively more robust, that being said it can be simplified to @FieldType(T, "y").

4 Likes

My mistake. My example is for demo purpose. Sometimes, the name T is not so simple to touch.

I think that the distinction here is not const vs var per se, but rather comptime-known vs runtime-known.

There are a few factors at play:

  1. In a namespace (as opposed to a function or block), const declarations are comptime-known, whereas var declarations are runtime-known.
  2. Attribute access on a comptime-known value is comptime-known.
  3. All comptime-known expressions are evaluated at comptime, causing compilation failure if they are illegal.

This explains why you get a compilation error when you declare t as const, and why you shouldn’t expect a compilation error when you declare it as var. The @TypeOf builtin is not (yet) relevant.

The remaining question is, why don’t you get a runtime error when t is declared var? The answer is that the @TypeOf builtin resolves at comptime and the contained expression leaves no trace at runtime. The crux being - this lack of runtime residue doesn’t mean that you don’t evaluate the contained expression at comptime if it’s comptime-known.

Here’s another example:

const x: usize = undefined;
const y = @TypeOf([x]u8);

This will fail to compile, even though you might be able to reason that the value of y is type, because the compiler will attempt to evaluate [x]u8.

In summary - no, the compiler is not being too smart for its own good! It’s just a natural consequence of comptime semantics, and changing the behavior would involve making it more complex.

4 Likes

Reasonable.