From 8dfddf95fee5982d22512c0ac15278eabac5bcbb Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 7 Aug 2026 17:52:24 -0700 Subject: [PATCH] std.meta: deprecate fieldInfo, fieldNames, fieldTypes Technically there is one valid use case for `fieldNames` which is use with an enum or union so that those types can be used interchangeably. But in practice these functions are mainly abused, because the callsites always know what kind of type it is. This commit encourages Zig users to embrace using `@typeInfo` directly when doing type reflection. --- lib/std/Build.zig | 4 ++-- lib/std/enums.zig | 5 +++-- lib/std/meta.zig | 25 ++++++++++++++++++------- lib/std/zig/AstGen.zig | 4 ++-- lib/std/zig/LibCInstallation.zig | 2 +- lib/std/zig/llvm/Builder.zig | 4 ++-- lib/std/zig/llvm/bitcode_writer.zig | 2 +- lib/std/zig/system.zig | 2 +- src/Air/Liveness.zig | 6 +++--- src/Sema.zig | 2 +- src/codegen/riscv64/encoding.zig | 2 +- src/codegen/wasm/CodeGen.zig | 2 +- src/codegen/x86_64/CodeGen.zig | 2 +- src/link/Coff.zig | 2 +- src/print_targets.zig | 6 +++--- 15 files changed, 41 insertions(+), 29 deletions(-) diff --git a/lib/std/Build.zig b/lib/std/Build.zig index 0258ce941c5fbb32bb162095d2ebec28262f6fc6..97bb40d52addfc8208693ee071c53bd50eb6c5b2 100644 --- a/lib/std/Build.zig +++ b/lib/std/Build.zig @@ -1109,7 +1109,7 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw const type_id = comptime typeToEnum(T); const enum_options = if (type_id == .@"enum" or type_id == .enum_list) blk: { const EnumType = if (type_id == .enum_list) @typeInfo(T).pointer.child else T; - const field_names = comptime std.meta.fieldNames(EnumType); + const field_names = @typeInfo(EnumType).@"enum".field_names; var options = std.array_list.Managed([]const u8).initCapacity(b.allocator, field_names.len) catch @panic("OOM"); inline for (field_names) |field_name| { @@ -1420,7 +1420,7 @@ pub fn parseTargetQuery(options: std.Target.Query.ParseOptions) error{ParseFaile \\available operating systems: \\ , .{diags.os_name.?}); - inline for (comptime std.meta.fieldNames(Target.Os.Tag)) |field_name| { + inline for (@typeInfo(Target.Os.Tag).@"enum".field_names) |field_name| { std.debug.print(" {s}\n", .{field_name}); } return error.ParseFailed; diff --git a/lib/std/enums.zig b/lib/std/enums.zig index 551cf71fcdd1c757b0e8a7db34fa11a93692a835..0f7439e00961485c4ce57f8a2a6286f6a2b9c818 100644 --- a/lib/std/enums.zig +++ b/lib/std/enums.zig @@ -33,7 +33,8 @@ pub fn fromInt(comptime E: type, integer: anytype) ?E { pub fn EnumFieldStruct(comptime E: type, comptime Data: type, comptime field_default: ?Data) type { @setEvalBranchQuota(@typeInfo(E).@"enum".field_names.len + eval_branch_quota_cushion); const default_ptr: ?*const anyopaque = if (field_default) |d| @ptrCast(&d) else null; - return @Struct(.auto, null, std.meta.fieldNames(E), &@splat(Data), &@splat(.{ .default_value_ptr = default_ptr })); + const field_names = @typeInfo(E).@"enum".field_names; + return @Struct(.auto, null, field_names, &@splat(Data), &@splat(.{ .default_value_ptr = default_ptr })); } /// Looks up the supplied field values in the given enum type. @@ -454,7 +455,7 @@ pub fn EnumMap(comptime E: type, comptime V: type) type { } } } else { - inline for (std.meta.fieldNames(E)) |field_name| { + inline for (@typeInfo(E).@"enum".field_names) |field_name| { const key = @field(E, field_name); if (@field(init_values, field_name)) |*v| { const i = comptime Indexer.indexOf(key); diff --git a/lib/std/meta.zig b/lib/std/meta.zig index edc4a5c4e8eca6de0891b5435e644f0917a1ed28..f1dec9a1df078b0cf3916573c25fd5fe62bfc9ce 100644 --- a/lib/std/meta.zig +++ b/lib/std/meta.zig @@ -197,15 +197,17 @@ test containerLayout { try testing.expect(containerLayout(U3) == .@"extern"); } -/// Instead of this function, prefer to use e.g. `@typeInfo(foo).@"struct".decl_names` -/// directly when you know what kind of type it is. +/// Returns the list of declaration names of namespace types. +/// +/// This function is only useful when the callsite does not know statically +/// which kind of container it is. pub fn declarations(comptime T: type) []const [:0]const u8 { return switch (@typeInfo(T)) { .@"struct" => |info| info.decl_names, .@"enum" => |info| info.decl_names, .@"union" => |info| info.decl_names, .@"opaque" => |info| info.decl_names, - else => @compileError("Expected struct, enum, union, or opaque type, found '" ++ @typeName(T) ++ "'"), + else => comptime unreachable, // type lacks namespace }; } @@ -241,10 +243,13 @@ test declarations { } /// To be removed after Zig 0.17.0 is tagged. -pub const declarationInfo = @compileError("Deprecated; use '@hasDecl' instead"); +pub const declarationInfo = @compileError("deprecated in favor of @hasDecl"); /// To be removed after Zig 0.17.0 is tagged. -pub const fields = @compileError("Deprecated; use 'fieldNames' and 'fieldTypes' instead"); +pub const fields = @compileError("deprecated in favor of @typeInfo"); +/// Deprecated in favor of `@typeInfo`. +/// +/// To be removed after 0.17.0 is tagged. pub fn fieldInfo(comptime T: type, comptime field: FieldEnum(T)) switch (@typeInfo(T)) { .@"struct" => struct { name: [:0]const u8, type: type, attrs: Type.Struct.FieldAttributes }, .@"union" => struct { name: [:0]const u8, type: type, attrs: Type.Union.FieldAttributes }, @@ -298,13 +303,16 @@ test fieldInfo { try testing.expect(comptime uf.type == u8); } +/// Deprecated in favor of `@typeInfo`. +/// +/// To be removed after 0.17.0 is tagged. pub fn fieldNames(comptime T: type) []const [:0]const u8 { return switch (@typeInfo(T)) { .@"struct" => |s| s.field_names, .@"union" => |u| u.field_names, .@"enum" => |e| e.field_names, .error_set => |es| es.error_names.?, - else => @compileError("Expected struct, union, error set or enum type, found '" ++ @typeName(T) ++ "'"), + else => comptime unreachable, }; } @@ -336,11 +344,14 @@ test fieldNames { try testing.expectEqualSlices(u8, u1names[1], "b"); } +/// Deprecated in favor of `@typeInfo`. +/// +/// To be removed after 0.17.0 is tagged. pub fn fieldTypes(comptime T: type) []const type { return switch (@typeInfo(T)) { .@"struct" => |s| s.field_types, .@"union" => |u| u.field_types, - else => @compileError("Expected struct or union type, found '" ++ @typeName(T) ++ "'"), + else => comptime unreachable, }; } diff --git a/lib/std/zig/AstGen.zig b/lib/std/zig/AstGen.zig index 1408f3c6f037e6e802ce1944764722955faa1fd8..c0860b71f61109a5b8dce360c1dea585d31d2b37 100644 --- a/lib/std/zig/AstGen.zig +++ b/lib/std/zig/AstGen.zig @@ -74,13 +74,13 @@ src_hasher: std.zig.SrcHasher, const InnerError = error{ OutOfMemory, AnalysisFail }; fn addExtra(astgen: *AstGen, extra: anytype) Allocator.Error!u32 { - const field_count = std.meta.fieldNames(@TypeOf(extra)).len; + const field_count = @typeInfo(@TypeOf(extra)).@"struct".field_names.len; try astgen.extra.ensureUnusedCapacity(astgen.gpa, field_count); return addExtraAssumeCapacity(astgen, extra); } fn addExtraAssumeCapacity(astgen: *AstGen, extra: anytype) u32 { - const field_count = std.meta.fieldNames(@TypeOf(extra)).len; + const field_count = @typeInfo(@TypeOf(extra)).@"struct".field_names.len; const extra_index: u32 = @intCast(astgen.extra.items.len); astgen.extra.items.len += field_count; setExtra(astgen, extra_index, extra); diff --git a/lib/std/zig/LibCInstallation.zig b/lib/std/zig/LibCInstallation.zig index 6fa49a0ce985c9115495a16c862e7d6e1a9062d5..f9e0404606ee0fe503feb2b1ce096f2a8df0ddca 100644 --- a/lib/std/zig/LibCInstallation.zig +++ b/lib/std/zig/LibCInstallation.zig @@ -43,7 +43,7 @@ pub const FindError = error{ pub fn parse(allocator: Allocator, io: Io, libc_file: []const u8, target: *const std.Target) !LibCInstallation { var self: LibCInstallation = .{}; - const field_names = comptime std.meta.fieldNames(LibCInstallation); + const field_names = @typeInfo(LibCInstallation).@"struct".field_names; const FoundKey = struct { found: bool, allocated: ?[]u8, diff --git a/lib/std/zig/llvm/Builder.zig b/lib/std/zig/llvm/Builder.zig index 8376a569c45ac7191272eabbd0cb84b73b1843db..93a1e4e438b6f5ca5b312db0417f0311f9e7460b 100644 --- a/lib/std/zig/llvm/Builder.zig +++ b/lib/std/zig/llvm/Builder.zig @@ -9517,7 +9517,7 @@ pub const Metadata = packed struct(u32) { nodes: anytype, w: *Writer, ) !void { - const names = comptime std.meta.fieldNames(@TypeOf(nodes)); + const names = @typeInfo(@TypeOf(nodes)).@"struct".field_names; comptime var fmt_str: []const u8 = "{[distinct]s}{[node]s}("; inline for (names) |name| fmt_str = fmt_str ++ "{[" ++ name ++ "]f}"; @@ -13484,7 +13484,7 @@ fn metadataSimpleAssumeCapacity(self: *Builder, tag: Metadata.Tag, value: anytyp builder: *const Builder, pub fn hash(_: @This(), key: Key) u32 { var hasher = std.hash.Wyhash.init(std.hash.int(@backingInt(key.tag))); - inline for (comptime std.meta.fieldNames(@TypeOf(value))) |field_name| { + inline for (@typeInfo(@TypeOf(value)).@"struct".field_names) |field_name| { hasher.update(std.mem.asBytes(&@field(key.value, field_name))); } return @truncate(hasher.final()); diff --git a/lib/std/zig/llvm/bitcode_writer.zig b/lib/std/zig/llvm/bitcode_writer.zig index 98c8489172ee3960ab4502bd9ccbb360ed519735..0ee722165be0a39a2695d4f23f249c6e2a8299f0 100644 --- a/lib/std/zig/llvm/bitcode_writer.zig +++ b/lib/std/zig/llvm/bitcode_writer.zig @@ -246,7 +246,7 @@ pub fn BitcodeWriter(comptime types: []const type) type { try self.bitcode.writeBits(comptime abbrevId(Abbrev), abbrev_len); - const field_names = comptime std.meta.fieldNames(Abbrev); + const field_names = @typeInfo(Abbrev).@"struct".field_names; // This abbreviation might only contain literals if (field_names.len == 0) return; diff --git a/lib/std/zig/system.zig b/lib/std/zig/system.zig index 4d63d0c995024fa317bb59aca5e0fc2022c63c9b..1baa6e8cad8a996b1705219bd5a25d514ce8090f 100644 --- a/lib/std/zig/system.zig +++ b/lib/std/zig/system.zig @@ -973,7 +973,7 @@ fn detectAbiAndDynamicLinker(io: Io, cpu: Target.Cpu, os: Target.Os, query: Targ // relying on `builtin.target`. const all_abis = comptime blk: { assert(@backingInt(Target.Abi.none) == 0); - const field_names = std.meta.fieldNames(Target.Abi)[1..]; + const field_names = @typeInfo(Target.Abi).@"enum".field_names[1..]; var array: [field_names.len]Target.Abi = undefined; for (field_names, 0..) |field_name, i| { array[i] = @field(Target.Abi, field_name); diff --git a/src/Air/Liveness.zig b/src/Air/Liveness.zig index 520f71f313fc6e4b1a4c64860b4ce7f040391c26..3940ae27b8446d71c053e938dbbb9344b56daa7e 100644 --- a/src/Air/Liveness.zig +++ b/src/Air/Liveness.zig @@ -351,7 +351,7 @@ const Analysis = struct { extra: std.ArrayList(u32), fn addExtra(a: *Analysis, extra: anytype) Allocator.Error!u32 { - const field_count = std.meta.fieldNames(@TypeOf(extra)).len; + const field_count = @typeInfo(@TypeOf(extra)).@"struct".field_names.len; try a.extra.ensureUnusedCapacity(a.gpa, field_count); return addExtraAssumeCapacity(a, extra); } @@ -1012,7 +1012,7 @@ fn analyzeInstBlock( const block_scope = data.block_scopes.get(inst).?; const num_deaths = data.live_set.count() - block_scope.live_set.count(); - try a.extra.ensureUnusedCapacity(gpa, num_deaths + std.meta.fieldNames(Block).len); + try a.extra.ensureUnusedCapacity(gpa, num_deaths + @typeInfo(Block).@"struct".field_names.len); const extra_index = a.addExtraAssumeCapacity(Block{ .death_count = num_deaths, }); @@ -1275,7 +1275,7 @@ fn analyzeInstCondBr( // Write the mirrored deaths to `extra` const then_death_count = @as(u32, @intCast(then_mirrored_deaths.items.len)); const else_death_count = @as(u32, @intCast(else_mirrored_deaths.items.len)); - try a.extra.ensureUnusedCapacity(gpa, std.meta.fieldNames(CondBr).len + then_death_count + else_death_count); + try a.extra.ensureUnusedCapacity(gpa, @typeInfo(CondBr).@"struct".field_names.len + then_death_count + else_death_count); const extra_index = a.addExtraAssumeCapacity(CondBr{ .then_death_count = then_death_count, .else_death_count = else_death_count, diff --git a/src/Sema.zig b/src/Sema.zig index c10b69fea8b0d5300d33bdbaa83f390ed463d89b..920bf997512c7c59bcca6715c7cf30c4f02e9bb1 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -34033,7 +34033,7 @@ pub fn getTmpAir(sema: Sema) Air { } pub fn addExtra(sema: *Sema, extra: anytype) Allocator.Error!u32 { - const field_count = std.meta.fieldNames(@TypeOf(extra)).len; + const field_count = @typeInfo(@TypeOf(extra)).@"struct".field_names.len; try sema.air_extra.ensureUnusedCapacity(sema.gpa, field_count); return sema.addExtraAssumeCapacity(extra); } diff --git a/src/codegen/riscv64/encoding.zig b/src/codegen/riscv64/encoding.zig index 40de9855cd342b35ea2ba540a20d589822c1b76f..5ca6a094d774fbaeb6a96c6c75ecd26a903da705 100644 --- a/src/codegen/riscv64/encoding.zig +++ b/src/codegen/riscv64/encoding.zig @@ -498,7 +498,7 @@ pub const Instruction = union(Lir.Format) { extra: u32, comptime { - for (std.meta.fieldTypes(Instruction)) |field_type| { + for (@typeInfo(Instruction).@"union".field_types) |field_type| { assert(@bitSizeOf(field_type) == 32); } } diff --git a/src/codegen/wasm/CodeGen.zig b/src/codegen/wasm/CodeGen.zig index 8b9b5869d85fcf498847c2fe49fb72dd7fa6232a..df15671e3379fabd3fbd3e2a75a015367b0e77a0 100644 --- a/src/codegen/wasm/CodeGen.zig +++ b/src/codegen/wasm/CodeGen.zig @@ -567,7 +567,7 @@ fn addCallIntrinsic(cg: *CodeGen, intrinsic: Mir.Intrinsic) error{OutOfMemory}!v /// Appends entries to `mir_extra` based on the type of `extra`. /// Returns the index into `mir_extra` fn addExtra(cg: *CodeGen, extra: anytype) error{OutOfMemory}!u32 { - const field_count = std.meta.fieldNames(@TypeOf(extra)).len; + const field_count = @typeInfo(@TypeOf(extra)).@"struct".field_names.len; try cg.mir_extra.ensureUnusedCapacity(cg.gpa, field_count); return cg.addExtraAssumeCapacity(extra); } diff --git a/src/codegen/x86_64/CodeGen.zig b/src/codegen/x86_64/CodeGen.zig index 9a087ca03e284f1bf751c52f77ef393a1c1c5896..063a8c158f3ff592ded65497dc14640098588e31 100644 --- a/src/codegen/x86_64/CodeGen.zig +++ b/src/codegen/x86_64/CodeGen.zig @@ -1301,7 +1301,7 @@ fn addInst(self: *CodeGen, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index { } fn addExtra(self: *CodeGen, extra: anytype) Allocator.Error!u32 { - const field_count = std.meta.fieldNames(@TypeOf(extra)).len; + const field_count = @typeInfo(@TypeOf(extra)).@"struct".field_names.len; try self.mir_extra.ensureUnusedCapacity(self.gpa, field_count); return self.addExtraAssumeCapacity(extra); } diff --git a/src/link/Coff.zig b/src/link/Coff.zig index 729e6393845bdcd5d8edbf98ea8fa126f718e978..78195f75241f3808610760eedb7d4b83eba681d5 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -3654,7 +3654,7 @@ fn verifyParentSectionAttributes( parent.name(coff).toSlice(coff), }); - inline for (comptime std.meta.fieldNames(ObjectSectionAttributes)) |field| { + inline for (@typeInfo(ObjectSectionAttributes).@"struct".field_names) |field| { if (@field(child_attrs, field) != @field(parent_attrs, field)) { err.addNote("flags.{s} was {d} in {s}, but {d} in {s}", .{ field, diff --git a/src/print_targets.zig b/src/print_targets.zig index 702a684de3e829b8e7d475c265a5ed2e584caa1e..695a9a5ef4dc168b85d479f691073d4008efa3a8 100644 --- a/src/print_targets.zig +++ b/src/print_targets.zig @@ -43,9 +43,9 @@ pub fn cmdTargets( { var root_obj = try serializer.beginStruct(.{}); - try root_obj.field("arch", meta.fieldNames(Target.Cpu.Arch), .{}); - try root_obj.field("os", meta.fieldNames(Target.Os.Tag), .{}); - try root_obj.field("abi", meta.fieldNames(Target.Abi), .{}); + try root_obj.field("arch", @typeInfo(Target.Cpu.Arch).@"enum".field_names, .{}); + try root_obj.field("os", @typeInfo(Target.Os.Tag).@"enum".field_names, .{}); + try root_obj.field("abi", @typeInfo(Target.Abi).@"enum".field_names, .{}); { var libc_obj = try root_obj.beginTupleField("libc", .{}); -- 2.54.0