authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-02-19 21:13:00-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-05-25 18:54:34-07:00
logb9aeedd23c303efc3c74a308ed2620ab1c470af7
tree29150e52f0420f7c2248ce9fb10aaee7e5568ba7
parent603e92cdde0afaa046ec8b560ae9e216c72cf588

Configuration: type safety for extended pattern


3 files changed, 187 insertions(+), 26 deletions(-)

lib/compiler/Maker/ScannedConfig.zig+3-5
......@@ -40,8 +40,7 @@ pub fn print(sc: *const ScannedConfig, w: *Writer) Writer.Error!void {
4040 try deps_field.end();
4141 }
4242 try step_field.field("max_rss", step.max_rss.toBytes(), .{});
43 const type_erased_flags: Configuration.Step.Flags = @bitCast(c.extra[step.extra_index]);
44 switch (type_erased_flags.tag) {
43 switch (step.extended.get(c.extra)) {
4544 .check_file => try step_field.field("check_file", .TODO, .{}),
4645 .check_object => try step_field.field("check_object", .TODO, .{}),
4746 .compile => try step_field.field("compile", .TODO, .{}),
......@@ -55,8 +54,7 @@ pub fn print(sc: *const ScannedConfig, w: *Writer) Writer.Error!void {
5554 .options => try step_field.field("options", .TODO, .{}),
5655 .remove_dir => try step_field.field("remove_dir", .TODO, .{}),
5756 .run => try step_field.field("run", .TODO, .{}),
58 .top_level => {
59 const top_level = c.extraData(Configuration.Step.TopLevel, step.extra_index);
57 .top_level => |top_level| {
6058 var sf = try step_field.beginStructField("top_level", .{});
6159 try sf.field("description", top_level.description.slice(c), .{});
6260 try sf.end();
......@@ -82,7 +80,7 @@ pub fn printSteps(sc: *const ScannedConfig, graph: *Graph, w: *Writer) !void {
8280 try std.fmt.allocPrint(arena, "{s} (default)", .{name})
8381 else
8482 name;
85 const top_level = c.extraData(Configuration.Step.TopLevel, step.extra_index);
83 const top_level = step.extended.get(c.extra).top_level;
8684 const description = top_level.description.slice(c);
8785 try w.print(" {s:<28} {s}\n", .{ decorated_name, description });
8886 }
lib/compiler/configure_runner.zig+8-8
......@@ -331,12 +331,12 @@ fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void {
331331 .owner = try s.builderToPackage(step.owner),
332332 .deps = deps,
333333 .max_rss = .fromBytes(step.max_rss),
334 .extra_index = switch (step.tag) {
334 .extended = switch (step.tag) {
335335 .top_level => e: {
336336 const top_level: *Step.TopLevel = @fieldParentPtr("step", step);
337 break :e try wc.addExtra(@as(Configuration.Step.TopLevel, .{
337 break :e @enumFromInt(try wc.addExtra(@as(Configuration.Step.TopLevel, .{
338338 .description = try wc.addString(top_level.description),
339 }));
339 })));
340340 },
341341 .compile => e: {
342342 const c: *Step.Compile = @fieldParentPtr("step", step);
......@@ -462,11 +462,11 @@ fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void {
462462
463463 log.err("TODO serialize the trailing Compile step data", .{});
464464
465 break :e extra_index;
465 break :e @enumFromInt(extra_index);
466466 },
467467 .install_artifact => e: {
468468 const ia: *Step.InstallArtifact = @fieldParentPtr("step", step);
469 break :e try wc.addExtra(@as(Configuration.Step.InstallArtifact, .{
469 break :e @enumFromInt(try wc.addExtra(@as(Configuration.Step.InstallArtifact, .{
470470 .flags = .{
471471 .dylib_symlinks = ia.dylib_symlinks != null,
472472 },
......@@ -480,7 +480,7 @@ fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void {
480480 .h_dir = try addInstallDir(wc, ia.h_dir),
481481 .emitted_h = try s.addOptionalLazyPathEnum(ia.emitted_h),
482482 .artifact = stepIndex(&step_map, &ia.artifact.step),
483 }));
483 })));
484484 },
485485 .install_file => @panic("TODO"),
486486 .install_dir => @panic("TODO"),
......@@ -536,7 +536,7 @@ fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void {
536536
537537 log.err("TODO serialize the trailing Run step data", .{});
538538
539 break :e extra_index;
539 break :e @enumFromInt(extra_index);
540540 },
541541 .check_file => @panic("TODO"),
542542 .check_object => @panic("TODO"),
......@@ -639,7 +639,7 @@ fn addOptionalResolvedTarget(
639639 })));
640640}
641641
642fn addInstallDir(wc: *Configuration.Wip, install_dir: ?std.Build.InstallDir) !Configuration.InstallDir {
642fn addInstallDir(wc: *Configuration.Wip, install_dir: ?std.Build.InstallDir) !Configuration.InstallDestDir {
643643 switch (install_dir orelse return .none) {
644644 .prefix => return .prefix,
645645 .lib => return .lib,
lib/std/zig/Configuration.zig+176-13
......@@ -397,9 +397,25 @@ pub const Step = extern struct {
397397 owner: Package.Index,
398398 deps: Deps,
399399 max_rss: MaxRss,
400 /// Points into `extra` for step-specific data. First element has flags
401 /// with `Tag`.
402 extra_index: u32,
400 extended: Storage.ExtendedIndex(Flags, union(Tag) {
401 check_file: CheckFile,
402 check_object: CheckObject,
403 compile: Compile,
404 config_header: ConfigHeader,
405 fail: Fail,
406 fmt: Fmt,
407 install_artifact: InstallArtifact,
408 install_dir: InstallDir,
409 install_file: InstallFile,
410 objcopy: Objcopy,
411 options: Options,
412 remove_dir: RemoveDir,
413 run: Run,
414 top_level: TopLevel,
415 translate_c: TranslateC,
416 update_source_files: UpdateSourceFiles,
417 write_file: WriteFile,
418 }),
403419
404420 /// Points into `steps`.
405421 pub const Index = enum(u32) {
......@@ -449,17 +465,17 @@ pub const Step = extern struct {
449465 pub const InstallArtifact = struct {
450466 flags: @This().Flags,
451467
452 dest_dir: InstallDir,
468 dest_dir: InstallDestDir,
453469 dest_sub_path: String,
454470 emitted_bin: OptionalLazyPath,
455471
456 implib_dir: InstallDir,
472 implib_dir: InstallDestDir,
457473 emitted_implib: OptionalLazyPath,
458474
459 pdb_dir: InstallDir,
475 pdb_dir: InstallDestDir,
460476 emitted_pdb: OptionalLazyPath,
461477
462 h_dir: InstallDir,
478 h_dir: InstallDestDir,
463479 emitted_h: OptionalLazyPath,
464480
465481 /// Always a compile step.
......@@ -789,8 +805,125 @@ pub const Step = extern struct {
789805 };
790806 };
791807
808 pub const CheckFile = struct {
809 flags: @This().Flags,
810
811 pub const Flags = packed struct(u32) {
812 tag: Tag = .check_file,
813 _: u27 = 0,
814 };
815 };
816
817 pub const CheckObject = struct {
818 flags: @This().Flags,
819
820 pub const Flags = packed struct(u32) {
821 tag: Tag = .check_object,
822 _: u27 = 0,
823 };
824 };
825
826 pub const ConfigHeader = struct {
827 flags: @This().Flags,
828
829 pub const Flags = packed struct(u32) {
830 tag: Tag = .config_header,
831 _: u27 = 0,
832 };
833 };
834
835 pub const Fail = struct {
836 flags: @This().Flags,
837
838 pub const Flags = packed struct(u32) {
839 tag: Tag = .fail,
840 _: u27 = 0,
841 };
842 };
843
844 pub const Fmt = struct {
845 flags: @This().Flags,
846
847 pub const Flags = packed struct(u32) {
848 tag: Tag = .fmt,
849 _: u27 = 0,
850 };
851 };
852
853 pub const InstallDir = struct {
854 flags: @This().Flags,
855
856 pub const Flags = packed struct(u32) {
857 tag: Tag = .install_dir,
858 _: u27 = 0,
859 };
860 };
861
862 pub const InstallFile = struct {
863 flags: @This().Flags,
864
865 pub const Flags = packed struct(u32) {
866 tag: Tag = .install_file,
867 _: u27 = 0,
868 };
869 };
870
871 pub const Objcopy = struct {
872 flags: @This().Flags,
873
874 pub const Flags = packed struct(u32) {
875 tag: Tag = .objcopy,
876 _: u27 = 0,
877 };
878 };
879
880 pub const Options = struct {
881 flags: @This().Flags,
882
883 pub const Flags = packed struct(u32) {
884 tag: Tag = .options,
885 _: u27 = 0,
886 };
887 };
888
889 pub const RemoveDir = struct {
890 flags: @This().Flags,
891
892 pub const Flags = packed struct(u32) {
893 tag: Tag = .remove_dir,
894 _: u27 = 0,
895 };
896 };
897
898 pub const TranslateC = struct {
899 flags: @This().Flags,
900
901 pub const Flags = packed struct(u32) {
902 tag: Tag = .translate_c,
903 _: u27 = 0,
904 };
905 };
906
907 pub const UpdateSourceFiles = struct {
908 flags: @This().Flags,
909
910 pub const Flags = packed struct(u32) {
911 tag: Tag = .update_source_files,
912 _: u27 = 0,
913 };
914 };
915
916 pub const WriteFile = struct {
917 flags: @This().Flags,
918
919 pub const Flags = packed struct(u32) {
920 tag: Tag = .write_file,
921 _: u27 = 0,
922 };
923 };
924
792925 pub fn flags(s: *const Step, c: *const Configuration) Flags {
793 return @bitCast(c.extra[s.extra_index]);
926 return @bitCast(c.extra[@intFromEnum(s.extended)]);
794927 }
795928};
796929
......@@ -1081,7 +1214,7 @@ pub const Path = extern struct {
10811214 }
10821215};
10831216
1084pub const InstallDir = enum(u32) {
1217pub const InstallDestDir = enum(u32) {
10851218 none = maxInt(u32) - 4,
10861219 prefix = maxInt(u32) - 3,
10871220 lib = maxInt(u32) - 2,
......@@ -1090,8 +1223,8 @@ pub const InstallDir = enum(u32) {
10901223 /// A `String` path relative to the prefix.
10911224 _,
10921225
1093 pub fn initCustom(sub_path: String) InstallDir {
1094 assert(@intFromEnum(sub_path) < @intFromEnum(InstallDir.none));
1226 pub fn initCustom(sub_path: String) InstallDestDir {
1227 assert(@intFromEnum(sub_path) < @intFromEnum(InstallDestDir.none));
10951228 return @enumFromInt(@intFromEnum(sub_path));
10961229 }
10971230};
......@@ -1496,7 +1629,10 @@ pub const TargetQuery = struct {
14961629
14971630pub const Storage = enum {
14981631 flag_optional,
1632 extended,
14991633
1634 /// The presence of the field is determined by a boolean within a packed
1635 /// struct.
15001636 pub fn FlagOptional(
15011637 comptime flags_arg: @EnumLiteral(),
15021638 comptime flag_arg: @EnumLiteral(),
......@@ -1512,6 +1648,31 @@ pub const Storage = enum {
15121648 };
15131649 }
15141650
1651 /// The field indexes into an auxilary buffer, with the first element being
1652 /// a packed struct that contains the tag.
1653 pub fn Extended(comptime U: type) type {
1654 return struct {
1655 value: U,
1656
1657 pub const storage: Storage = .extended;
1658 };
1659 }
1660
1661 /// Equivalent to `Extended` but works in an `extern struct`.
1662 pub fn ExtendedIndex(comptime BaseFlags: type, comptime U: type) type {
1663 return enum(u32) {
1664 _,
1665
1666 pub fn get(this: @This(), buffer: []const u32) U {
1667 var i: usize = @intFromEnum(this);
1668 const base_flags: BaseFlags = @bitCast(buffer[i]);
1669 return switch (base_flags.tag) {
1670 inline else => |tag| @unionInit(U, @tagName(tag), data(buffer, &i, @FieldType(U, @tagName(tag)))),
1671 };
1672 }
1673 };
1674 }
1675
15151676 pub fn dataLength(buffer: []const u32, i: usize, comptime S: type) usize {
15161677 var end = i;
15171678 _ = data(buffer, &end, S);
......@@ -1536,7 +1697,7 @@ pub const Storage = enum {
15361697 },
15371698 64 => {
15381699 defer i.* += 2;
1539 return buffer[i.*..][0..2].*;
1700 return @bitCast(buffer[i.*..][0..2].*);
15401701 },
15411702 else => comptime unreachable,
15421703 },
......@@ -1573,6 +1734,7 @@ pub const Storage = enum {
15731734 .value = if (flag) dataField(buffer, i, container, Field.Value) else null,
15741735 };
15751736 },
1737 .extended => @compileError("TODO"),
15761738 },
15771739 },
15781740 .@"extern" => comptime unreachable,
......@@ -1639,6 +1801,7 @@ pub const Storage = enum {
16391801 .flag_optional => {
16401802 return if (value.value) |v| setExtraField(buffer, i, Field.Value, v) else 0;
16411803 },
1804 .extended => @compileError("TODO"),
16421805 },
16431806 },
16441807 .@"extern" => comptime unreachable,
......@@ -1662,7 +1825,7 @@ pub const Storage = enum {
16621825 else => comptime unreachable,
16631826 },
16641827 .auto => switch (Field.storage) {
1665 .flag_optional => 1,
1828 .flag_optional, .extended => 1,
16661829 },
16671830 .@"extern" => comptime unreachable,
16681831 },