zig icon indicating copy to clipboard operation
zig copied to clipboard

compiler crashes when switching on a union enum

Open rbscott opened this issue 6 years ago • 6 comments

I just started playing around with the language and ran into an unexpected compiler error.

I narrowed it down to following code:

const State = union(enum) {
    NoParameter,
    Parameter: usize,
};

export fn small_test() void {
    var state = State.NoParameter;

    state = switch (state) {
        State.NoParameter => State{ .Parameter = 2 },
        State.Parameter => |parameter| State.NoParameter,
    };
}

I get the error message:

small_test.zig:11:29: error: switch on type '@TagType(State)' provides no expression parameter
        State.Parameter => |parameter| State.NoParameter,

I am pretty new to the language and this is probably just a typo or something silly. I tried searching through the docs and the codebase and only found this reference, but as far as I can tell the two are slightly different. Any chance someone could highlight what is wrong with this code snippet?

rbscott avatar Apr 16 '19 09:04 rbscott

So the issue here is that State.NoParameter is not actually a tagged enum of type State, but is simply the tag State.NoParameter.

If you add a @compileLog(@typeOf(state)) you can see the type of State.

You can create a tagged union with an empty value by using the void value {}, like this:

const State = union(enum) {
    NoParameter,
    Parameter: usize,
};

export fn small_test() void {
    var state = State{ .NoParameter = {} };

    state = switch (state) {
        State.NoParameter => State{ .Parameter = 2 },
        State.Parameter => |parameter| State{ .NoParameter = {} },
    };
}

I do agree that this is quite a confusing error if you don't know what you are looking for. I think we could repurpose this issue into improving this error message (maybe with a hint) and/or updating the documentation to explicitly outline the difference between initializing an tagged enum with a void value and the tag itself.

tiehuis avatar Apr 16 '19 10:04 tiehuis

I also find these very confusing. I would definitely like to see more examples in the documentation.

justinbalexander avatar Apr 16 '19 13:04 justinbalexander

Thanks, that makes sense. I was looking for a few examples setting a value to void union enum, but didn't find many. Perhaps one more case which I think is related, but also kind of confusing is this one:

const State = union(enum) {
    NoParameter,
    Parameter: usize,
};

export fn small_test() void {
    var state = State{ .NoParameter = {} };

    state = switch (state) {
        State.NoParameter => State{ .Parameter = 2 },
        State.Parameter => |parameter| State.NoParameter,
    };
}

This yields the error:

small_test.zig:36:13: error: runtime cast to union 'State' which has non-void fields
    state = switch (state) {
            ^
small_test.zig:8:5: note: field 'Parameter' has type 'usize'
    Parameter: usize,

In this case, I forgot to use the correct syntax again, but for me the error was pointing me to the wrong place, so this tripped me up fro a while.

Happy to see this was added to the 0.5.0 release, let me know if there is anything I can do to help!

rbscott avatar Apr 17 '19 06:04 rbscott

related: #995

emekoi avatar Apr 17 '19 21:04 emekoi

As of 0.11.0-dev.971+19056cb68, the example crashes the compiler:

const std = @import("std");
const expect = std.testing.expect;

const State = union(enum) {
    NoParameter,
    Parameter: usize,
};

export fn small_test() void {
    var state = State.NoParameter;

    state = switch (state) {
        State.NoParameter => State{ .Parameter = 2 },
        State.Parameter => |_| State.NoParameter,
    };
}
$ stage4/bin/zig build-obj test.zig 
thread 1550849 panic: unexpected AIR tag for coerce_result_ptr: Air.Inst.Tag.get_union_tag
Analyzing test.zig: test.zig:small_test
      %32 = dbg_block_begin()
      %33 = decl_val("State") 
      %34 = validate_struct_init_ty(%33) 
    > %35 = coerce_result_ptr(%33, %16) 
      %36 = field_ptr_init(%35, "Parameter") 
      %37 = int(2)
      %38 = store_node(%36, %37) 
      %40 = dbg_block_end()
      %39 = validate_struct_init({
        %36 = field_ptr_init(%35, "Parameter") 
      }) 
      %41 = break(%28, @Zir.Inst.Ref.void_value)
    For full context, use the command
      zig ast-check -t test.zig

  in test.zig: test.zig:small_test
    > %28 = switch_block(%25,
        %31 => {%32..%41},
        %44 => {%45..%51}) 
  in test.zig: test.zig:small_test
    > %13 = block({%14..%54}) 

/home/andy/dev/zig/lib/std/debug.zig:316:22: 0x6478b4e in panicExtra__anon_176417 (zig)
    std.builtin.panic(msg, trace, ret_addr);
                     ^
/home/andy/dev/zig/lib/std/debug.zig:291:15: 0x6167cb6 in panic__anon_158591 (zig)
    panicExtra(null, null, format, args);
              ^
/home/andy/dev/zig/src/Sema.zig:2549:33: 0x5e13ad6 in coerceResultPtr (zig)
                        air_tags[trash_inst],
                                ^
/home/andy/dev/zig/src/Sema.zig:2458:32: 0x59d1269 in zirCoerceResultPtr (zig)
    return sema.coerceResultPtr(block, src, ptr, dummy_ptr, dummy_operand, &trash_block);
                               ^
/home/andy/dev/zig/src/Sema.zig:926:73: 0x57908d5 in analyzeBodyInner (zig)
            .coerce_result_ptr            => try sema.zirCoerceResultPtr(block, inst),
                                                                        ^
/home/andy/dev/zig/src/Sema.zig:781:30: 0x5e422c2 in analyzeBodyRuntimeBreak (zig)
    _ = sema.analyzeBodyInner(block, body) catch |err| switch (err) {
                             ^
/home/andy/dev/zig/src/Sema.zig:10671:45: 0x59f14cf in zirSwitchBlock (zig)
            try sema.analyzeBodyRuntimeBreak(&case_block, body);
                                            ^
/home/andy/dev/zig/src/Sema.zig:982:69: 0x579398d in analyzeBodyInner (zig)
            .switch_block                 => try sema.zirSwitchBlock(block, inst),
                                                                    ^
/home/andy/dev/zig/src/Sema.zig:5288:34: 0x5e47089 in resolveBlockBody (zig)
        if (sema.analyzeBodyInner(child_block, body)) |_| {
                                 ^
/home/andy/dev/zig/src/Sema.zig:5271:85: 0x5a91dad in zirBlock (zig)
    return sema.resolveBlockBody(parent_block, src, &child_block, body, inst, &label.merges);
                                                                                    ^
/home/andy/dev/zig/src/Sema.zig:1453:69: 0x579f7db in analyzeBodyInner (zig)
                if (!block.is_comptime) break :blk try sema.zirBlock(block, inst);
                                                                    ^
/home/andy/dev/zig/src/Sema.zig:800:30: 0x5976618 in analyzeBody (zig)
    _ = sema.analyzeBodyInner(block, body) catch |err| switch (err) {
                             ^
/home/andy/dev/zig/src/Module.zig:5697:43: 0x577285a in analyzeFnBody (zig)
    sema.analyzeBody(&inner_block, fn_info.body) catch |err| switch (err) {
                                          ^
/home/andy/dev/zig/src/Module.zig:4377:40: 0x55a15bd in ensureFuncBodyAnalyzed (zig)
            var air = mod.analyzeFnBody(func, sema_arena) catch |err| switch (err) {
                                       ^
/home/andy/dev/zig/src/Compilation.zig:3119:42: 0x559f13c in processOneJob (zig)
            module.ensureFuncBodyAnalyzed(func) catch |err| switch (err) {
                                         ^
/home/andy/dev/zig/src/Compilation.zig:3057:30: 0x54676a4 in performAllTheWork (zig)
            try processOneJob(comp, work_item);
                             ^
/home/andy/dev/zig/src/Compilation.zig:2361:31: 0x54631a8 in update (zig)
    try comp.performAllTheWork(main_progress_node);
                              ^
/home/andy/dev/zig/src/main.zig:3437:20: 0x54923ed in updateModule (zig)
    try comp.update();
                   ^
/home/andy/dev/zig/src/main.zig:3099:17: 0x537296d in buildOutputType (zig)
    updateModule(gpa, comp, hook) catch |err| switch (err) {
                ^
/home/andy/dev/zig/src/main.zig:261:31: 0x534ccc3 in mainArgs (zig)
        return buildOutputType(gpa, arena, args, .{ .build = .Obj });
                              ^
/home/andy/dev/zig/src/main.zig:201:20: 0x534c225 in main (zig)
    return mainArgs(gpa, arena, args);
                   ^
/home/andy/dev/zig/lib/std/start.zig:614:37: 0x534e947 in main (zig)
            const result = root.main() catch |err| {
                                    ^
???:?:?: 0x7f0bb190b24d in ??? (???)
???:?:?: 0x7fff0ce409eb in ??? (???)
Aborted (core dumped)

andrewrk avatar Dec 27 '22 21:12 andrewrk

passes on 0.12.0-dev.1836+dd189a354

nektro avatar Dec 21 '23 01:12 nektro