Description
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.