zigo

July 19, 2026, 1:26pm 1

const E = enum(u2) {
    foo,
    bar,
    _,
};

const U = union(E) {
    foo,
    bar,
};


var v: U = .foo;
var e: E = .foo;

pub fn main() void {
    // This one compiles.
    switch (v) {
        .foo => {},
        .bar => {},
    }
    
    // This one also compiles.
    switch (v) {
        .foo => {},
        .bar => {},
        else => {},
    }
    
    // This one compiles, too.
    switch (e) {
        .foo => {},
        .bar => {},
        _ => {},
    }
    
    // This one fails to compile.
    switch (v) {
        .foo => {},
        .bar => {},
        _ => {}, // error
    }
}

The compile error is

test.zig:38:5: error: '_' prong only allowed when switching on non-exhaustive enums
    switch (v) {
    ^~~~~~
test.zig:41:9: note: '_' prong here
        _ => {}, // error
        ^
test.zig:38:5: note: consider using 'else'

which, if anything, means that the behavior is completely intentional, as v is a union and not an enum.
The rationale, I believe, being that enums can be non-exhaustive, in which case the _ branch makes sense, but unions cannot.

zigo July 19, 2026, 1:40pm 3

I mean, are the inconsistencies intentional? And is it intentional to allow to use a non-exhaustive enum as union tag type?

Ah I see now, do you mean how the else branch is allowed when switching on the union even though all variants have been exhausted? Indeed this could be an oversight but I am not sure.

Checking into the compiler (around src/Sema.zig:11020), switching on unions is done through the backing enum, and since E is non-exhaustive, the else branch is allowed on v, but since _ is only allowed for non-exhaustive enums, the last example gets rejected.

It might be worth looking into the issues on codeberg (which it’s not letting me right now :^))