Skip to content

tagged union switch prong capture doesn't coerce noreturn / uninstantiable field type #20187

Open
@rohlem

Description

@rohlem

Zig Version

0.13.0-dev.365+332fbb4b0

Steps to Reproduce and Observed Behavior

In a tagged union type, uninstantiable field/payload types (such as noreturn, but also f.e. enum{}) signal that values of the union type will never be in these states (see also #15909 ).
As such, accesses to them are unreachable, including their switch prongs.

The issue is that when sharing a prong with capture between multiple cases, the compiler currently doesn't seem to perform the usual coercion logic that noreturn (and arguably all uninstantiable types) are never reachable, and so should coerce with values of all other types.
Test case:

test {
    var u: union(enum) { //tagged union type with an uninstantiable state payload type
        a: void,
        b: noreturn,
    } = .a;
    _ = &u;

    // if behavior, just for completeness
    var condition = true; //runtime condition to check type coercion logic in if
    _ = &condition;
    //const x = if(condition) "a" else 3; //proof this checks runtime coercion: compile error "incompatible types"
    //_ = x;
    const y = if (condition) u.a else u.b; //no compile error, coercion in if works
    _ = y;

    _ = switch (u) {
        .a => |p| p,
        .b => |p| p, //splitting the prongs is supported, the compiler generates this prong as unreachable
    };
    _ = switch (u) {
        .a, .b => {}, //shared prong without capture works
    };
    _ = switch (u) {
        .a, .b => |p| p, //shared prong with capture triggers "expected type 'void', found 'noreturn'"
    };
}

Output of zig test on the file:

main.zig:27:14: error: expected type 'void', found 'noreturn'
        .a, .b => |p| p, //non-inline shared prong with payload incorrectly triggers "expected type 'void', found 'noreturn'"
            ~^

Expected Behavior

The shared prong with capture should be allowed by the compiler by generating the cases with uninstantiable types as implicit unreachable, just as is done without capture.
This becomes useful when writing generic code, where a single switch in source code can handle values of various union types. A field can be instantiable for some types and not for others.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugObserved behavior contradicts documented or intended behaviorfrontendTokenization, parsing, AstGen, Sema, and Liveness.

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions