Skip to content

Errors returning tagged union from nested switch statements #9483

@andrewb10x

Description

@andrewb10x

Returning a tagged union from within two nested switch statements can fail with a spurious error message or crash the compiler, depending on precisely how the type information is specified in the code.
This works:

const std = @import("std");

pub fn main() !void {
    const Outer = enum { first, second };
    const Inner = enum { alpha, beta };
    const Action = union(enum) { go: u8, stop };

    for (std.enums.values(Outer)) |outer| {
        for (std.enums.values(Inner)) |inner| {
            const action: Action = switch (outer) { // Note type IS specified here
                .first => @as(Action, switch (inner) {
                    .alpha => .{ .go = 1 }, // Type NOT specified here
                    .beta => .stop, // Type NOT specified here
                }),
                .second => .stop, // Type NOT specified here
            };
            std.debug.print("{}\n", .{action});
        }
    }
}

This version below (which has insufficient type information) causes a compiler crash (checked with versions 0.8.0 and 0.9.0-dev.656+ba71b96fe on linux x86_64):

const std = @import("std");

pub fn main() !void {
    const Outer = enum { first, second };
    const Inner = enum { alpha, beta };
    const Action = union(enum) { go: u8, stop };

    for (std.enums.values(Outer)) |outer| {
        for (std.enums.values(Inner)) |inner| {
            const action = switch (outer) { // Type NOT specified here
                .first => @as(Action, switch (inner) {
                    .alpha => .{ .go = 1 }, // Type NOT specified here
                    .beta => .stop, // Type NOT specified here
                }),
                .second => .stop, // Type NOT specified here
            };
            std.debug.print("{}\n", .{action});
        }
    }
}

The error message is:

$ zig run nested_switch_tagged_union_crash.zig 
Assertion failed at /home/andy/dev/bootstrap-zig/zig/src/stage1/analyze.cpp:4634 in find_union_type_field. This is a bug in the Zig compiler.thread 5083 panic: 
Unable to dump stack trace: debug info stripped
Aborted (core dumped)

The following code does not crash the compiler but the error message appears to be spurious as it says there is no member named '.go' in the union:

const std = @import("std");

pub fn main() !void {
    const Outer = enum { first, second };
    const Inner = enum { alpha, beta };
    const Action = union(enum) { go: u8, stop };

    for (std.enums.values(Outer)) |outer| {
        for (std.enums.values(Inner)) |inner| {
            const action = switch (outer) { // Note type NOT specified here
                .first => @as(Action, switch (inner) {
                    .alpha => Action{ .go = 1 }, // Type IS specified here
                    .beta => Action.stop, // Type IS specified here
                }),
                .second => Action.stop, // Type IS specified here
            };
            std.debug.print("{}\n", .{action});
        }
    }
}

The output is:

$ zig run nested_switch_tagged_union_fails.zig 
./nested_switch_tagged_union_fails.zig:12:39: error: no member named 'go' in enum '@typeInfo(Action).Union.tag_type.?'
                    .alpha => Action{ .go = 1 },
                                      ^
/home/andrew/apps/zig-linux-x86_64-0.8.0/lib/std/start.zig:458:40: note: referenced here
            const result = root.main() catch |err| {
                                       ^

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugObserved behavior contradicts documented or intended behaviorstage1The process of building from source via WebAssembly and the C backend.

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions