mirror of
https://codeberg.org/ziglang/zig.git
synced 2026-10-28 19:00:52 +08:00
This commit implements the `@backingInt` and `@fromBackingInt` builtins. These builtins are meant to replace the now-deprecated `@intFromEnum` and `@enumFromInt` builtins. `@backingInt` works with all enums and with bitpacks with *explicit* backing integer types only. It also works with tagged unions, returning the backing integer of the active tag value. An `undefined` enum or bitpack yields an `undefined` backing integer. The builtin is lowered to `bit_cast`. `@fromBackingInt` infers its result type, which may be any enum or a bitpack with an *explicit* backing integer type. It takes a parameter of exactly that backing integer type. For enums, passing a backing integer that is either `undefined` or would yield an invalid tag value results in safety-checked Illegal Behavior. For bitpacks, passing an `undefined` backing integer yields an `undefined` bitpack. The builtin is lowered to `bit_cast` or `bit_cast_safe` (more details below). `@bitCast` now also performs a safety check for invalid tag values if its destination type is an `enum`. This commit also introduces a new AIR instruction, `bit_cast_safe`. Similarly to `int_cast_safe`, it includes a safety check for invalid enum tag values. When `@enumFromInt` is eventually removed, `int_cast_safe` won't need to perform this safety check anymore. Legalize only implements `expand_bit_cast_safe`, and no additional insts to scalarize `bit_cast_safe` with vector/array operands. That is because it can just reuse the existing scalarizations for regular `bit_cast`. The safety check mandated by `bit_cast_safe` is only desired if the result type is a scalar enum tag value, so the scalarization process is exactly equivalent. The only difference is that we have to perform a safety check on the result of the scalarization. This means that there's no reason for a backend to want scalarization of regular `bit_cast`, but not of `bit_cast_safe` (or the other way round). If a backend is having trouble with the `bit_cast_safe` safety check it should just use `expand_bit_cast_safe` instead. Also adds a `std.meta.BackingInt()` function to conveniently get the result type of `@backingInt` and for use in the langref.
51 lines
1.4 KiB
Zig
51 lines
1.4 KiB
Zig
const std = @import("std");
|
|
const expectEqual = std.testing.expectEqual;
|
|
|
|
const SliceTypeA = extern struct {
|
|
len: usize,
|
|
ptr: [*]u32,
|
|
};
|
|
const SliceTypeB = extern struct {
|
|
ptr: [*]SliceTypeA,
|
|
len: usize,
|
|
};
|
|
const AnySlice = union(enum(u8)) {
|
|
a: SliceTypeA,
|
|
b: SliceTypeB,
|
|
c: []const u8,
|
|
d: []AnySlice,
|
|
};
|
|
|
|
fn withFor(any: AnySlice) usize {
|
|
const Tag = @typeInfo(AnySlice).@"union".tag_type.?;
|
|
const info = @typeInfo(Tag).@"enum";
|
|
inline for (info.field_names, info.field_values) |field_name, field_value| {
|
|
// With `inline for` the function gets generated as
|
|
// a series of `if` statements relying on the optimizer
|
|
// to convert it to a switch.
|
|
if (field_value == @backingInt(any)) {
|
|
return @field(any, field_name).len;
|
|
}
|
|
}
|
|
// When using `inline for` the compiler doesn't know that every
|
|
// possible case has been handled requiring an explicit `unreachable`.
|
|
unreachable;
|
|
}
|
|
|
|
fn withSwitch(any: AnySlice) usize {
|
|
return switch (any) {
|
|
// With `inline else` the function is explicitly generated
|
|
// as the desired switch and the compiler can check that
|
|
// every possible case is handled.
|
|
inline else => |slice| slice.len,
|
|
};
|
|
}
|
|
|
|
test "inline for and inline else similarity" {
|
|
const any = AnySlice{ .c = "hello" };
|
|
try expectEqual(5, withFor(any));
|
|
try expectEqual(5, withSwitch(any));
|
|
}
|
|
|
|
// test
|