Use case:
```zig
pub const Index = enum(u32) {
pub const first_type: In…dex = .u1_type;
u1_type,
u8_type,
i8_type,
u16_type,
i16_type,
u29_type,
u32_type,
i32_type,
u64_type,
i64_type,
u80_type,
u128_type,
i128_type,
usize_type,
isize_type,
c_char_type,
c_short_type,
c_ushort_type,
c_int_type,
c_uint_type,
c_long_type,
c_ulong_type,
c_longlong_type,
c_ulonglong_type,
c_longdouble_type,
f16_type,
f32_type,
f64_type,
f80_type,
f128_type,
anyopaque_type,
bool_type,
void_type,
type_type,
anyerror_type,
comptime_int_type,
comptime_float_type,
noreturn_type,
anyframe_type,
null_type,
undefined_type,
enum_literal_type,
atomic_order_type,
atomic_rmw_op_type,
calling_convention_type,
address_space_type,
float_mode_type,
reduce_op_type,
call_modifier_type,
prefetch_options_type,
export_options_type,
extern_options_type,
type_info_type,
manyptr_u8_type,
manyptr_const_u8_type,
single_const_pointer_to_comptime_int_type,
const_slice_u8_type,
anyerror_void_error_union_type,
generic_poison_type,
empty_struct_type,
pub const last_type: Index = .empty_struct_type;
pub const first_value: Index = .undef;
/// `undefined` (untyped)
undef,
/// `0` (comptime_int)
zero,
/// `0` (usize)
zero_usize,
/// `1` (comptime_int)
one,
/// `1` (usize)
one_usize,
/// `std.builtin.CallingConvention.C`
calling_convention_c,
/// `std.builtin.CallingConvention.Inline`
calling_convention_inline,
/// `{}`
void_value,
/// `unreachable` (noreturn type)
unreachable_value,
/// `null` (untyped)
null_value,
/// `true`
bool_true,
/// `false`
bool_false,
/// `.{}` (untyped)
empty_struct,
pub const last_value: Index = .empty_struct;
/// Used for generic parameters where the type and value
/// is not known until generic function instantiation.
generic_poison,
none = std.math.maxInt(u32),
_,
```
```zig
pub fn isNoReturn(ty: Type) bool {
switch (ty.ip_index) {
InternPool.Index.first_type...@intToEnum(InternPool.Index, @enumToInt(InternPool.Index.noreturn_type) - 1) => return false,
.noreturn_type => return true,
@intToEnum(InternPool.Index, @enumToInt(InternPool.Index.noreturn_type) + 1)...InternPool.Index.last_type => return false,
InternPool.Index.first_value...InternPool.Index.last_value => unreachable,
.generic_poison => unreachable,
// TODO add empty error sets here
// TODO add enums with no fields here
_ => return false,
.none => switch (ty.tag()) {
.noreturn => return true,
.error_set => {
const err_set_obj = ty.castTag(.error_set).?.data;
const names = err_set_obj.names.keys();
return names.len == 0;
},
.error_set_merged => {
const name_map = ty.castTag(.error_set_merged).?.data;
const names = name_map.keys();
return names.len == 0;
},
else => return false,
},
}
}
```
Currently this gives an error for having declarations in the enum. If those are worked around, then it gives this error:
```
/home/andy/Downloads/zig/src/type.zig:2847:19: error: ranges not allowed when switching on type 'InternPool.Index'
switch (ty.ip_index) {
~~^~~~~~~~~
/home/andy/Downloads/zig/src/type.zig:2848:40: note: range here
InternPool.Index.first_type...@intToEnum(InternPool.Index, @enumToInt(InternPool.Index.noreturn_type) - 1) => return false,
~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
```
I think this should be allowed. It makes sense what should be generated in machine code, and this is more type-safe than the alternative, which does compile:
```zig
pub fn isNoReturn(ty: Type) bool {
switch (@enumToInt(ty.ip_index)) {
@enumToInt(InternPool.Index.first_type)...@enumToInt(InternPool.Index.noreturn_type) - 1 => return false,
@enumToInt(InternPool.Index.noreturn_type) => return true,
@enumToInt(InternPool.Index.noreturn_type) + 1...@enumToInt(InternPool.Index.last_type) => return false,
@enumToInt(InternPool.Index.first_value)...@enumToInt(InternPool.Index.last_value) => unreachable,
@enumToInt(InternPool.Index.generic_poison) => unreachable,
// TODO add empty error sets here
// TODO add enums with no fields here
else => return false,
@enumToInt(InternPool.Index.none) => switch (ty.tag()) {
.noreturn => return true,
.error_set => {
const err_set_obj = ty.castTag(.error_set).?.data;
const names = err_set_obj.names.keys();
return names.len == 0;
},
.error_set_merged => {
const name_map = ty.castTag(.error_set_merged).?.data;
const names = name_map.keys();
return names.len == 0;
},
else => return false,
},
}
}
}
```
This has two problems compared to the original example:
* A wrong type could be used inside the `@enumToInt` calls
* It uses `else` instead of `_` so newly added enum tags will not be noticed at this site by the compiler