From 518588e9fcdf7a7b8e819014f7a706e05bd1b902 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 19 Feb 2026 21:13:00 -0800 Subject: [PATCH] Configuration: type safety for extended pattern --- lib/compiler/Maker/ScannedConfig.zig | 8 +- lib/compiler/configure_runner.zig | 16 +-- lib/std/zig/Configuration.zig | 189 +++++++++++++++++++++++++-- 3 files changed, 187 insertions(+), 26 deletions(-) diff --git a/lib/compiler/Maker/ScannedConfig.zig b/lib/compiler/Maker/ScannedConfig.zig index d375f8775a..fc0bcd37eb 100644 --- a/lib/compiler/Maker/ScannedConfig.zig +++ b/lib/compiler/Maker/ScannedConfig.zig @@ -40,8 +40,7 @@ pub fn print(sc: *const ScannedConfig, w: *Writer) Writer.Error!void { try deps_field.end(); } try step_field.field("max_rss", step.max_rss.toBytes(), .{}); - const type_erased_flags: Configuration.Step.Flags = @bitCast(c.extra[step.extra_index]); - switch (type_erased_flags.tag) { + switch (step.extended.get(c.extra)) { .check_file => try step_field.field("check_file", .TODO, .{}), .check_object => try step_field.field("check_object", .TODO, .{}), .compile => try step_field.field("compile", .TODO, .{}), @@ -55,8 +54,7 @@ pub fn print(sc: *const ScannedConfig, w: *Writer) Writer.Error!void { .options => try step_field.field("options", .TODO, .{}), .remove_dir => try step_field.field("remove_dir", .TODO, .{}), .run => try step_field.field("run", .TODO, .{}), - .top_level => { - const top_level = c.extraData(Configuration.Step.TopLevel, step.extra_index); + .top_level => |top_level| { var sf = try step_field.beginStructField("top_level", .{}); try sf.field("description", top_level.description.slice(c), .{}); try sf.end(); @@ -82,7 +80,7 @@ pub fn printSteps(sc: *const ScannedConfig, graph: *Graph, w: *Writer) !void { try std.fmt.allocPrint(arena, "{s} (default)", .{name}) else name; - const top_level = c.extraData(Configuration.Step.TopLevel, step.extra_index); + const top_level = step.extended.get(c.extra).top_level; const description = top_level.description.slice(c); try w.print(" {s:<28} {s}\n", .{ decorated_name, description }); } diff --git a/lib/compiler/configure_runner.zig b/lib/compiler/configure_runner.zig index 901b494713..f3df4d6ec1 100644 --- a/lib/compiler/configure_runner.zig +++ b/lib/compiler/configure_runner.zig @@ -331,12 +331,12 @@ fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void { .owner = try s.builderToPackage(step.owner), .deps = deps, .max_rss = .fromBytes(step.max_rss), - .extra_index = switch (step.tag) { + .extended = switch (step.tag) { .top_level => e: { const top_level: *Step.TopLevel = @fieldParentPtr("step", step); - break :e try wc.addExtra(@as(Configuration.Step.TopLevel, .{ + break :e @enumFromInt(try wc.addExtra(@as(Configuration.Step.TopLevel, .{ .description = try wc.addString(top_level.description), - })); + }))); }, .compile => e: { const c: *Step.Compile = @fieldParentPtr("step", step); @@ -462,11 +462,11 @@ fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void { log.err("TODO serialize the trailing Compile step data", .{}); - break :e extra_index; + break :e @enumFromInt(extra_index); }, .install_artifact => e: { const ia: *Step.InstallArtifact = @fieldParentPtr("step", step); - break :e try wc.addExtra(@as(Configuration.Step.InstallArtifact, .{ + break :e @enumFromInt(try wc.addExtra(@as(Configuration.Step.InstallArtifact, .{ .flags = .{ .dylib_symlinks = ia.dylib_symlinks != null, }, @@ -480,7 +480,7 @@ fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void { .h_dir = try addInstallDir(wc, ia.h_dir), .emitted_h = try s.addOptionalLazyPathEnum(ia.emitted_h), .artifact = stepIndex(&step_map, &ia.artifact.step), - })); + }))); }, .install_file => @panic("TODO"), .install_dir => @panic("TODO"), @@ -536,7 +536,7 @@ fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void { log.err("TODO serialize the trailing Run step data", .{}); - break :e extra_index; + break :e @enumFromInt(extra_index); }, .check_file => @panic("TODO"), .check_object => @panic("TODO"), @@ -639,7 +639,7 @@ fn addOptionalResolvedTarget( }))); } -fn addInstallDir(wc: *Configuration.Wip, install_dir: ?std.Build.InstallDir) !Configuration.InstallDir { +fn addInstallDir(wc: *Configuration.Wip, install_dir: ?std.Build.InstallDir) !Configuration.InstallDestDir { switch (install_dir orelse return .none) { .prefix => return .prefix, .lib => return .lib, diff --git a/lib/std/zig/Configuration.zig b/lib/std/zig/Configuration.zig index 6ae31958c6..fbd9d7e570 100644 --- a/lib/std/zig/Configuration.zig +++ b/lib/std/zig/Configuration.zig @@ -397,9 +397,25 @@ pub const Step = extern struct { owner: Package.Index, deps: Deps, max_rss: MaxRss, - /// Points into `extra` for step-specific data. First element has flags - /// with `Tag`. - extra_index: u32, + extended: Storage.ExtendedIndex(Flags, union(Tag) { + check_file: CheckFile, + check_object: CheckObject, + compile: Compile, + config_header: ConfigHeader, + fail: Fail, + fmt: Fmt, + install_artifact: InstallArtifact, + install_dir: InstallDir, + install_file: InstallFile, + objcopy: Objcopy, + options: Options, + remove_dir: RemoveDir, + run: Run, + top_level: TopLevel, + translate_c: TranslateC, + update_source_files: UpdateSourceFiles, + write_file: WriteFile, + }), /// Points into `steps`. pub const Index = enum(u32) { @@ -449,17 +465,17 @@ pub const Step = extern struct { pub const InstallArtifact = struct { flags: @This().Flags, - dest_dir: InstallDir, + dest_dir: InstallDestDir, dest_sub_path: String, emitted_bin: OptionalLazyPath, - implib_dir: InstallDir, + implib_dir: InstallDestDir, emitted_implib: OptionalLazyPath, - pdb_dir: InstallDir, + pdb_dir: InstallDestDir, emitted_pdb: OptionalLazyPath, - h_dir: InstallDir, + h_dir: InstallDestDir, emitted_h: OptionalLazyPath, /// Always a compile step. @@ -789,8 +805,125 @@ pub const Step = extern struct { }; }; + pub const CheckFile = struct { + flags: @This().Flags, + + pub const Flags = packed struct(u32) { + tag: Tag = .check_file, + _: u27 = 0, + }; + }; + + pub const CheckObject = struct { + flags: @This().Flags, + + pub const Flags = packed struct(u32) { + tag: Tag = .check_object, + _: u27 = 0, + }; + }; + + pub const ConfigHeader = struct { + flags: @This().Flags, + + pub const Flags = packed struct(u32) { + tag: Tag = .config_header, + _: u27 = 0, + }; + }; + + pub const Fail = struct { + flags: @This().Flags, + + pub const Flags = packed struct(u32) { + tag: Tag = .fail, + _: u27 = 0, + }; + }; + + pub const Fmt = struct { + flags: @This().Flags, + + pub const Flags = packed struct(u32) { + tag: Tag = .fmt, + _: u27 = 0, + }; + }; + + pub const InstallDir = struct { + flags: @This().Flags, + + pub const Flags = packed struct(u32) { + tag: Tag = .install_dir, + _: u27 = 0, + }; + }; + + pub const InstallFile = struct { + flags: @This().Flags, + + pub const Flags = packed struct(u32) { + tag: Tag = .install_file, + _: u27 = 0, + }; + }; + + pub const Objcopy = struct { + flags: @This().Flags, + + pub const Flags = packed struct(u32) { + tag: Tag = .objcopy, + _: u27 = 0, + }; + }; + + pub const Options = struct { + flags: @This().Flags, + + pub const Flags = packed struct(u32) { + tag: Tag = .options, + _: u27 = 0, + }; + }; + + pub const RemoveDir = struct { + flags: @This().Flags, + + pub const Flags = packed struct(u32) { + tag: Tag = .remove_dir, + _: u27 = 0, + }; + }; + + pub const TranslateC = struct { + flags: @This().Flags, + + pub const Flags = packed struct(u32) { + tag: Tag = .translate_c, + _: u27 = 0, + }; + }; + + pub const UpdateSourceFiles = struct { + flags: @This().Flags, + + pub const Flags = packed struct(u32) { + tag: Tag = .update_source_files, + _: u27 = 0, + }; + }; + + pub const WriteFile = struct { + flags: @This().Flags, + + pub const Flags = packed struct(u32) { + tag: Tag = .write_file, + _: u27 = 0, + }; + }; + pub fn flags(s: *const Step, c: *const Configuration) Flags { - return @bitCast(c.extra[s.extra_index]); + return @bitCast(c.extra[@intFromEnum(s.extended)]); } }; @@ -1081,7 +1214,7 @@ pub const Path = extern struct { } }; -pub const InstallDir = enum(u32) { +pub const InstallDestDir = enum(u32) { none = maxInt(u32) - 4, prefix = maxInt(u32) - 3, lib = maxInt(u32) - 2, @@ -1090,8 +1223,8 @@ pub const InstallDir = enum(u32) { /// A `String` path relative to the prefix. _, - pub fn initCustom(sub_path: String) InstallDir { - assert(@intFromEnum(sub_path) < @intFromEnum(InstallDir.none)); + pub fn initCustom(sub_path: String) InstallDestDir { + assert(@intFromEnum(sub_path) < @intFromEnum(InstallDestDir.none)); return @enumFromInt(@intFromEnum(sub_path)); } }; @@ -1496,7 +1629,10 @@ pub const TargetQuery = struct { pub const Storage = enum { flag_optional, + extended, + /// The presence of the field is determined by a boolean within a packed + /// struct. pub fn FlagOptional( comptime flags_arg: @EnumLiteral(), comptime flag_arg: @EnumLiteral(), @@ -1512,6 +1648,31 @@ pub const Storage = enum { }; } + /// The field indexes into an auxilary buffer, with the first element being + /// a packed struct that contains the tag. + pub fn Extended(comptime U: type) type { + return struct { + value: U, + + pub const storage: Storage = .extended; + }; + } + + /// Equivalent to `Extended` but works in an `extern struct`. + pub fn ExtendedIndex(comptime BaseFlags: type, comptime U: type) type { + return enum(u32) { + _, + + pub fn get(this: @This(), buffer: []const u32) U { + var i: usize = @intFromEnum(this); + const base_flags: BaseFlags = @bitCast(buffer[i]); + return switch (base_flags.tag) { + inline else => |tag| @unionInit(U, @tagName(tag), data(buffer, &i, @FieldType(U, @tagName(tag)))), + }; + } + }; + } + pub fn dataLength(buffer: []const u32, i: usize, comptime S: type) usize { var end = i; _ = data(buffer, &end, S); @@ -1536,7 +1697,7 @@ pub const Storage = enum { }, 64 => { defer i.* += 2; - return buffer[i.*..][0..2].*; + return @bitCast(buffer[i.*..][0..2].*); }, else => comptime unreachable, }, @@ -1573,6 +1734,7 @@ pub const Storage = enum { .value = if (flag) dataField(buffer, i, container, Field.Value) else null, }; }, + .extended => @compileError("TODO"), }, }, .@"extern" => comptime unreachable, @@ -1639,6 +1801,7 @@ pub const Storage = enum { .flag_optional => { return if (value.value) |v| setExtraField(buffer, i, Field.Value, v) else 0; }, + .extended => @compileError("TODO"), }, }, .@"extern" => comptime unreachable, @@ -1662,7 +1825,7 @@ pub const Storage = enum { else => comptime unreachable, }, .auto => switch (Field.storage) { - .flag_optional => 1, + .flag_optional, .extended => 1, }, .@"extern" => comptime unreachable, },