Commit a365971a33

wooster0 <wooster0@proton.me>
2025-05-11 10:38:16
std.meta.intToEnum -> std.enums.fromInt
Also use an optional as the return type instead of an error code.
1 parent a3693aa
Changed files (9)
lib
compiler
resinator
std
src
Package
Fetch
test
behavior
lib/compiler/resinator/cli.zig
@@ -354,9 +354,9 @@ pub const Options = struct {
 
         const language_id = self.default_language_id orelse res.Language.default;
         const language_name = language_name: {
-            if (std.meta.intToEnum(lang.LanguageId, language_id)) |lang_enum_val| {
+            if (std.enums.fromInt(lang.LanguageId, language_id)) |lang_enum_val| {
                 break :language_name @tagName(lang_enum_val);
-            } else |_| {}
+            }
             if (language_id == lang.LOCALE_CUSTOM_UNSPECIFIED) {
                 break :language_name "LOCALE_CUSTOM_UNSPECIFIED";
             }
lib/compiler/resinator/res.zig
@@ -173,9 +173,9 @@ pub const Language = packed struct(u16) {
         _ = options;
         const language_id = language.asInt();
         const language_name = language_name: {
-            if (std.meta.intToEnum(lang.LanguageId, language_id)) |lang_enum_val| {
+            if (std.enums.fromInt(lang.LanguageId, language_id)) |lang_enum_val| {
                 break :language_name @tagName(lang_enum_val);
-            } else |_| {}
+            }
             if (language_id == lang.LOCALE_CUSTOM_UNSPECIFIED) {
                 break :language_name "LOCALE_CUSTOM_UNSPECIFIED";
             }
lib/std/Build/Step/CheckObject.zig
@@ -2445,7 +2445,7 @@ const WasmDumper = struct {
         switch (check.kind) {
             .headers => {
                 while (reader.readByte()) |current_byte| {
-                    const section = std.meta.intToEnum(std.wasm.Section, current_byte) catch {
+                    const section = std.enums.fromInt(std.wasm.Section, current_byte) orelse {
                         return step.fail("Found invalid section id '{d}'", .{current_byte});
                     };
 
@@ -2551,7 +2551,7 @@ const WasmDumper = struct {
                     const name = data[fbs.pos..][0..name_len];
                     fbs.pos += name_len;
 
-                    const kind = std.meta.intToEnum(std.wasm.ExternalKind, try reader.readByte()) catch {
+                    const kind = std.enums.fromInt(std.wasm.ExternalKind, try reader.readByte()) orelse {
                         return step.fail("invalid import kind", .{});
                     };
 
@@ -2613,7 +2613,7 @@ const WasmDumper = struct {
                     const name = data[fbs.pos..][0..name_len];
                     fbs.pos += name_len;
                     const kind_byte = try std.leb.readUleb128(u8, reader);
-                    const kind = std.meta.intToEnum(std.wasm.ExternalKind, kind_byte) catch {
+                    const kind = std.enums.fromInt(std.wasm.ExternalKind, kind_byte) orelse {
                         return step.fail("invalid export kind value '{d}'", .{kind_byte});
                     };
                     const index = try std.leb.readUleb128(u32, reader);
@@ -2664,7 +2664,7 @@ const WasmDumper = struct {
 
     fn parseDumpType(step: *Step, comptime E: type, reader: anytype, writer: anytype) !E {
         const byte = try reader.readByte();
-        const tag = std.meta.intToEnum(E, byte) catch {
+        const tag = std.enums.fromInt(E, byte) orelse {
             return step.fail("invalid wasm type value '{d}'", .{byte});
         };
         try writer.print("type {s}\n", .{@tagName(tag)});
@@ -2683,7 +2683,7 @@ const WasmDumper = struct {
 
     fn parseDumpInit(step: *Step, reader: anytype, writer: anytype) !void {
         const byte = try reader.readByte();
-        const opcode = std.meta.intToEnum(std.wasm.Opcode, byte) catch {
+        const opcode = std.enums.fromInt(std.wasm.Opcode, byte) orelse {
             return step.fail("invalid wasm opcode '{d}'", .{byte});
         };
         switch (opcode) {
lib/std/json/static.zig
@@ -595,7 +595,7 @@ pub fn innerParseFromValue(
 
             switch (source) {
                 .float => return error.InvalidEnumTag,
-                .integer => |i| return std.meta.intToEnum(T, i),
+                .integer => |i| return std.enums.fromInt(T, i) orelse return error.InvalidEnumTag,
                 .number_string, .string => |s| return sliceToEnum(T, s),
                 else => return error.UnexpectedToken,
             }
@@ -780,7 +780,7 @@ fn sliceToEnum(comptime T: type, slice: []const u8) !T {
     // Check for a numeric value.
     if (!isNumberFormattedLikeAnInteger(slice)) return error.InvalidEnumTag;
     const n = std.fmt.parseInt(@typeInfo(T).@"enum".tag_type, slice, 10) catch return error.InvalidEnumTag;
-    return std.meta.intToEnum(T, n);
+    return std.enums.fromInt(T, n) orelse return error.InvalidEnumTag;
 }
 
 fn fillDefaultStructValues(comptime T: type, r: *T, fields_seen: *[@typeInfo(T).@"struct".fields.len]bool) !void {
lib/std/enums.zig
@@ -8,6 +8,25 @@ const EnumField = std.builtin.Type.EnumField;
 /// Increment this value when adding APIs that add single backwards branches.
 const eval_branch_quota_cushion = 10;
 
+pub fn fromInt(comptime E: type, integer: anytype) ?E {
+    const enum_info = @typeInfo(E).@"enum";
+    if (!enum_info.is_exhaustive) {
+        if (std.math.cast(enum_info.tag_type, integer)) |tag| {
+            return @enumFromInt(tag);
+        }
+        return null;
+    }
+    // We don't directly iterate over the fields of E, as that
+    // would require an inline loop. Instead, we create an array of
+    // values that is comptime-know, but can be iterated at runtime
+    // without requiring an inline loop.
+    // This generates better machine code.
+    for (values(E)) |value| {
+        if (@intFromEnum(value) == integer) return @enumFromInt(integer);
+    }
+    return null;
+}
+
 /// Returns a struct with a field matching each unique named enum element.
 /// If the enum is extern and has multiple names for the same value, only
 /// the first name is used.  Each field is of type Data and has the provided
@@ -239,6 +258,30 @@ test nameCast {
     try testing.expectEqual(B.b, nameCast(B, "b"));
 }
 
+test fromInt {
+    const E1 = enum {
+        A,
+    };
+    const E2 = enum {
+        A,
+        B,
+    };
+    const E3 = enum(i8) { A, _ };
+
+    var zero: u8 = 0;
+    var one: u16 = 1;
+    _ = &zero;
+    _ = &one;
+    try testing.expect(fromInt(E1, zero).? == E1.A);
+    try testing.expect(fromInt(E2, one).? == E2.B);
+    try testing.expect(fromInt(E3, zero).? == E3.A);
+    try testing.expect(fromInt(E3, 127).? == @as(E3, @enumFromInt(127)));
+    try testing.expect(fromInt(E3, -128).? == @as(E3, @enumFromInt(-128)));
+    try testing.expectEqual(null, fromInt(E1, one));
+    try testing.expectEqual(null, fromInt(E3, 128));
+    try testing.expectEqual(null, fromInt(E3, -129));
+}
+
 /// A set of enum elements, backed by a bitfield.  If the enum
 /// is exhaustive but not dense, a mapping will be constructed from enum values
 /// to dense indices.  This type does no dynamic allocation and
lib/std/meta.zig
@@ -857,58 +857,12 @@ test eql {
     try testing.expect(!eql(CU{ .a = {} }, .b));
 }
 
-test intToEnum {
-    const E1 = enum {
-        A,
-    };
-    const E2 = enum {
-        A,
-        B,
-    };
-    const E3 = enum(i8) { A, _ };
-
-    var zero: u8 = 0;
-    var one: u16 = 1;
-    _ = &zero;
-    _ = &one;
-    try testing.expect(intToEnum(E1, zero) catch unreachable == E1.A);
-    try testing.expect(intToEnum(E2, one) catch unreachable == E2.B);
-    try testing.expect(intToEnum(E3, zero) catch unreachable == E3.A);
-    try testing.expect(intToEnum(E3, 127) catch unreachable == @as(E3, @enumFromInt(127)));
-    try testing.expect(intToEnum(E3, -128) catch unreachable == @as(E3, @enumFromInt(-128)));
-    try testing.expectError(error.InvalidEnumTag, intToEnum(E1, one));
-    try testing.expectError(error.InvalidEnumTag, intToEnum(E3, 128));
-    try testing.expectError(error.InvalidEnumTag, intToEnum(E3, -129));
-}
-
+/// Deprecated: use `std.enums.fromInt` instead and handle null.
 pub const IntToEnumError = error{InvalidEnumTag};
 
+/// Deprecated: use `std.enums.fromInt` instead and handle null instead of an error.
 pub fn intToEnum(comptime EnumTag: type, tag_int: anytype) IntToEnumError!EnumTag {
-    const enum_info = @typeInfo(EnumTag).@"enum";
-
-    if (!enum_info.is_exhaustive) {
-        if (std.math.cast(enum_info.tag_type, tag_int)) |tag| {
-            return @as(EnumTag, @enumFromInt(tag));
-        }
-        return error.InvalidEnumTag;
-    }
-
-    // We don't directly iterate over the fields of EnumTag, as that
-    // would require an inline loop. Instead, we create an array of
-    // values that is comptime-know, but can be iterated at runtime
-    // without requiring an inline loop. This generates better
-    // machine code.
-    const values = comptime blk: {
-        var result: [enum_info.fields.len]enum_info.tag_type = undefined;
-        for (&result, enum_info.fields) |*dst, src| {
-            dst.* = src.value;
-        }
-        break :blk result;
-    };
-    for (values) |v| {
-        if (v == tag_int) return @enumFromInt(tag_int);
-    }
-    return error.InvalidEnumTag;
+    return std.enums.fromInt(EnumTag, tag_int) orelse return error.InvalidEnumTag;
 }
 
 /// Given a type and a name, return the field index according to source order.
src/Package/Fetch/git.zig
@@ -313,7 +313,7 @@ pub const Repository = struct {
                 unused: u3,
                 type: u4,
             } = @bitCast(std.fmt.parseUnsigned(u16, iterator.data[iterator.pos..mode_end], 8) catch return error.InvalidTree);
-            const @"type" = std.meta.intToEnum(Entry.Type, mode.type) catch return error.InvalidTree;
+            const @"type" = std.enums.fromInt(Entry.Type, mode.type) orelse return error.InvalidTree;
             const executable = switch (mode.permission) {
                 0 => if (@"type" == .file) return error.InvalidTree else false,
                 0o644 => if (@"type" != .file) return error.InvalidTree else false,
@@ -1144,7 +1144,7 @@ const EntryHeader = union(Type) {
         const rest_len = if (initial.has_next) try readSizeVarInt(reader) else 0;
         var uncompressed_length: u64 = initial.len;
         uncompressed_length |= std.math.shlExact(u64, rest_len, 4) catch return error.InvalidFormat;
-        const @"type" = std.meta.intToEnum(EntryHeader.Type, initial.type) catch return error.InvalidFormat;
+        const @"type" = std.enums.fromInt(EntryHeader.Type, initial.type) orelse return error.InvalidFormat;
         return switch (@"type") {
             inline .commit, .tree, .blob, .tag => |tag| @unionInit(EntryHeader, @tagName(tag), .{
                 .uncompressed_length = uncompressed_length,
src/Value.zig
@@ -3814,7 +3814,7 @@ pub fn interpret(val: Value, comptime T: type, pt: Zcu.PerThread) error{ OutOfMe
         .@"enum" => switch (interpret_mode) {
             .direct => {
                 const int = val.getUnsignedInt(zcu) orelse return error.TypeMismatch;
-                return std.meta.intToEnum(T, int) catch error.TypeMismatch;
+                return std.enums.fromInt(T, int) orelse error.TypeMismatch;
             },
             .by_name => {
                 const field_index = ty.enumTagFieldIndex(val, zcu) orelse return error.TypeMismatch;
test/behavior/enum.zig
@@ -19,16 +19,16 @@ test "enum to int" {
     try shouldEqual(Number.Four, 4);
 }
 
-fn testIntToEnumEval(x: i32) !void {
-    try expect(@as(IntToEnumNumber, @enumFromInt(x)) == IntToEnumNumber.Three);
+fn testEnumFromIntEval(x: i32) !void {
+    try expect(@as(EnumFromIntNumber, @enumFromInt(x)) == EnumFromIntNumber.Three);
 }
-const IntToEnumNumber = enum { Zero, One, Two, Three, Four };
+const EnumFromIntNumber = enum { Zero, One, Two, Three, Four };
 
 test "int to enum" {
     if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
     if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
 
-    try testIntToEnumEval(3);
+    try testEnumFromIntEval(3);
 }
 
 const ValueCount1 = enum {