zig
zig copied to clipboard
compiler crashes when switching on a union enum
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?
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.
I also find these very confusing. I would definitely like to see more examples in the documentation.
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!
related: #995
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)
passes on 0.12.0-dev.1836+dd189a354