diff --git a/lib/compiler/resinator/cli.zig b/lib/compiler/resinator/cli.zig index ea86752455..f27775d6af 100644 --- a/lib/compiler/resinator/cli.zig +++ b/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"; } diff --git a/lib/compiler/resinator/res.zig b/lib/compiler/resinator/res.zig index 37475b3081..b4fcd53f9d 100644 --- a/lib/compiler/resinator/res.zig +++ b/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"; } diff --git a/lib/std/Build/Step/CheckObject.zig b/lib/std/Build/Step/CheckObject.zig index 5fee8c730b..c2ff85c6f1 100644 --- a/lib/std/Build/Step/CheckObject.zig +++ b/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) { diff --git a/lib/std/enums.zig b/lib/std/enums.zig index 76df493d65..819bb76434 100644 --- a/lib/std/enums.zig +++ b/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 diff --git a/lib/std/json/static.zig b/lib/std/json/static.zig index 9cdb6226f2..2504d59100 100644 --- a/lib/std/json/static.zig +++ b/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 { diff --git a/lib/std/meta.zig b/lib/std/meta.zig index c0aef1fb51..5707527aed 100644 --- a/lib/std/meta.zig +++ b/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. diff --git a/src/Package/Fetch/git.zig b/src/Package/Fetch/git.zig index d74e83d8c3..de3912041f 100644 --- a/src/Package/Fetch/git.zig +++ b/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, diff --git a/src/Value.zig b/src/Value.zig index cac4134e58..6d041bc5d2 100644 --- a/src/Value.zig +++ b/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; diff --git a/test/behavior/enum.zig b/test/behavior/enum.zig index 6994df705e..80ea0c220e 100644 --- a/test/behavior/enum.zig +++ b/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 {