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 {...@@ -40,8 +40,7 @@ pub fn print(sc: *const ScannedConfig, w: *Writer) Writer.Error!void {
40 try deps_field.end();40 try deps_field.end();
41 }41 }
42 try step_field.field("max_rss", step.max_rss.toBytes(), .{});42 try step_field.field("max_rss", step.max_rss.toBytes(), .{});
43 const type_erased_flags: Configuration.Step.Flags = @bitCast(c.extra[step.extra_index]);43 switch (step.extended.get(c.extra)) {
44 switch (type_erased_flags.tag) {
45 .check_file => try step_field.field("check_file", .TODO, .{}),44 .check_file => try step_field.field("check_file", .TODO, .{}),
46 .check_object => try step_field.field("check_object", .TODO, .{}),45 .check_object => try step_field.field("check_object", .TODO, .{}),
47 .compile => try step_field.field("compile", .TODO, .{}),46 .compile => try step_field.field("compile", .TODO, .{}),
...@@ -55,8 +54,7 @@ pub fn print(sc: *const ScannedConfig, w: *Writer) Writer.Error!void {...@@ -55,8 +54,7 @@ pub fn print(sc: *const ScannedConfig, w: *Writer) Writer.Error!void {
55 .options => try step_field.field("options", .TODO, .{}),54 .options => try step_field.field("options", .TODO, .{}),
56 .remove_dir => try step_field.field("remove_dir", .TODO, .{}),55 .remove_dir => try step_field.field("remove_dir", .TODO, .{}),
57 .run => try step_field.field("run", .TODO, .{}),56 .run => try step_field.field("run", .TODO, .{}),
58 .top_level => {57 .top_level => |top_level| {
59 const top_level = c.extraData(Configuration.Step.TopLevel, step.extra_index);
60 var sf = try step_field.beginStructField("top_level", .{});58 var sf = try step_field.beginStructField("top_level", .{});
61 try sf.field("description", top_level.description.slice(c), .{});59 try sf.field("description", top_level.description.slice(c), .{});
62 try sf.end();60 try sf.end();
...@@ -82,7 +80,7 @@ pub fn printSteps(sc: *const ScannedConfig, graph: *Graph, w: *Writer) !void {...@@ -82,7 +80,7 @@ pub fn printSteps(sc: *const ScannedConfig, graph: *Graph, w: *Writer) !void {
82 try std.fmt.allocPrint(arena, "{s} (default)", .{name})80 try std.fmt.allocPrint(arena, "{s} (default)", .{name})
83 else81 else
84 name;82 name;
85 const top_level = c.extraData(Configuration.Step.TopLevel, step.extra_index);83 const top_level = step.extended.get(c.extra).top_level;
86 const description = top_level.description.slice(c);84 const description = top_level.description.slice(c);
87 try w.print(" {s:<28} {s}\n", .{ decorated_name, description });85 try w.print(" {s:<28} {s}\n", .{ decorated_name, description });
88 }86 }
lib/compiler/configure_runner.zig+8-8
...@@ -331,12 +331,12 @@ fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void {...@@ -331,12 +331,12 @@ fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void {
331 .owner = try s.builderToPackage(step.owner),331 .owner = try s.builderToPackage(step.owner),
332 .deps = deps,332 .deps = deps,
333 .max_rss = .fromBytes(step.max_rss),333 .max_rss = .fromBytes(step.max_rss),
334 .extra_index = switch (step.tag) {334 .extended = switch (step.tag) {
335 .top_level => e: {335 .top_level => e: {
336 const top_level: *Step.TopLevel = @fieldParentPtr("step", step);336 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, .{
338 .description = try wc.addString(top_level.description),338 .description = try wc.addString(top_level.description),
339 }));339 })));
340 },340 },
341 .compile => e: {341 .compile => e: {
342 const c: *Step.Compile = @fieldParentPtr("step", step);342 const c: *Step.Compile = @fieldParentPtr("step", step);
...@@ -462,11 +462,11 @@ fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void {...@@ -462,11 +462,11 @@ fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void {
462462
463 log.err("TODO serialize the trailing Compile step data", .{});463 log.err("TODO serialize the trailing Compile step data", .{});
464464
465 break :e extra_index;465 break :e @enumFromInt(extra_index);
466 },466 },
467 .install_artifact => e: {467 .install_artifact => e: {
468 const ia: *Step.InstallArtifact = @fieldParentPtr("step", step);468 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, .{
470 .flags = .{470 .flags = .{
471 .dylib_symlinks = ia.dylib_symlinks != null,471 .dylib_symlinks = ia.dylib_symlinks != null,
472 },472 },
...@@ -480,7 +480,7 @@ fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void {...@@ -480,7 +480,7 @@ fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void {
480 .h_dir = try addInstallDir(wc, ia.h_dir),480 .h_dir = try addInstallDir(wc, ia.h_dir),
481 .emitted_h = try s.addOptionalLazyPathEnum(ia.emitted_h),481 .emitted_h = try s.addOptionalLazyPathEnum(ia.emitted_h),
482 .artifact = stepIndex(&step_map, &ia.artifact.step),482 .artifact = stepIndex(&step_map, &ia.artifact.step),
483 }));483 })));
484 },484 },
485 .install_file => @panic("TODO"),485 .install_file => @panic("TODO"),
486 .install_dir => @panic("TODO"),486 .install_dir => @panic("TODO"),
...@@ -536,7 +536,7 @@ fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void {...@@ -536,7 +536,7 @@ fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void {
536536
537 log.err("TODO serialize the trailing Run step data", .{});537 log.err("TODO serialize the trailing Run step data", .{});
538538
539 break :e extra_index;539 break :e @enumFromInt(extra_index);
540 },540 },
541 .check_file => @panic("TODO"),541 .check_file => @panic("TODO"),
542 .check_object => @panic("TODO"),542 .check_object => @panic("TODO"),
...@@ -639,7 +639,7 @@ fn addOptionalResolvedTarget(...@@ -639,7 +639,7 @@ fn addOptionalResolvedTarget(
639 })));639 })));
640}640}
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 {
643 switch (install_dir orelse return .none) {643 switch (install_dir orelse return .none) {
644 .prefix => return .prefix,644 .prefix => return .prefix,
645 .lib => return .lib,645 .lib => return .lib,
lib/std/zig/Configuration.zig+176-13
...@@ -397,9 +397,25 @@ pub const Step = extern struct {...@@ -397,9 +397,25 @@ pub const Step = extern struct {
397 owner: Package.Index,397 owner: Package.Index,
398 deps: Deps,398 deps: Deps,
399 max_rss: MaxRss,399 max_rss: MaxRss,
400 /// Points into `extra` for step-specific data. First element has flags400 extended: Storage.ExtendedIndex(Flags, union(Tag) {
401 /// with `Tag`.401 check_file: CheckFile,
402 extra_index: u32,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
404 /// Points into `steps`.420 /// Points into `steps`.
405 pub const Index = enum(u32) {421 pub const Index = enum(u32) {
...@@ -449,17 +465,17 @@ pub const Step = extern struct {...@@ -449,17 +465,17 @@ pub const Step = extern struct {
449 pub const InstallArtifact = struct {465 pub const InstallArtifact = struct {
450 flags: @This().Flags,466 flags: @This().Flags,
451467
452 dest_dir: InstallDir,468 dest_dir: InstallDestDir,
453 dest_sub_path: String,469 dest_sub_path: String,
454 emitted_bin: OptionalLazyPath,470 emitted_bin: OptionalLazyPath,
455471
456 implib_dir: InstallDir,472 implib_dir: InstallDestDir,
457 emitted_implib: OptionalLazyPath,473 emitted_implib: OptionalLazyPath,
458474
459 pdb_dir: InstallDir,475 pdb_dir: InstallDestDir,
460 emitted_pdb: OptionalLazyPath,476 emitted_pdb: OptionalLazyPath,
461477
462 h_dir: InstallDir,478 h_dir: InstallDestDir,
463 emitted_h: OptionalLazyPath,479 emitted_h: OptionalLazyPath,
464480
465 /// Always a compile step.481 /// Always a compile step.
...@@ -789,8 +805,125 @@ pub const Step = extern struct {...@@ -789,8 +805,125 @@ pub const Step = extern struct {
789 };805 };
790 };806 };
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
792 pub fn flags(s: *const Step, c: *const Configuration) Flags {925 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)]);
794 }927 }
795};928};
796929
...@@ -1081,7 +1214,7 @@ pub const Path = extern struct {...@@ -1081,7 +1214,7 @@ pub const Path = extern struct {
1081 }1214 }
1082};1215};
10831216
1084pub const InstallDir = enum(u32) {1217pub const InstallDestDir = enum(u32) {
1085 none = maxInt(u32) - 4,1218 none = maxInt(u32) - 4,
1086 prefix = maxInt(u32) - 3,1219 prefix = maxInt(u32) - 3,
1087 lib = maxInt(u32) - 2,1220 lib = maxInt(u32) - 2,
...@@ -1090,8 +1223,8 @@ pub const InstallDir = enum(u32) {...@@ -1090,8 +1223,8 @@ pub const InstallDir = enum(u32) {
1090 /// A `String` path relative to the prefix.1223 /// A `String` path relative to the prefix.
1091 _,1224 _,
10921225
1093 pub fn initCustom(sub_path: String) InstallDir {1226 pub fn initCustom(sub_path: String) InstallDestDir {
1094 assert(@intFromEnum(sub_path) < @intFromEnum(InstallDir.none));1227 assert(@intFromEnum(sub_path) < @intFromEnum(InstallDestDir.none));
1095 return @enumFromInt(@intFromEnum(sub_path));1228 return @enumFromInt(@intFromEnum(sub_path));
1096 }1229 }
1097};1230};
...@@ -1496,7 +1629,10 @@ pub const TargetQuery = struct {...@@ -1496,7 +1629,10 @@ pub const TargetQuery = struct {
14961629
1497pub const Storage = enum {1630pub const Storage = enum {
1498 flag_optional,1631 flag_optional,
1632 extended,
14991633
1634 /// The presence of the field is determined by a boolean within a packed
1635 /// struct.
1500 pub fn FlagOptional(1636 pub fn FlagOptional(
1501 comptime flags_arg: @EnumLiteral(),1637 comptime flags_arg: @EnumLiteral(),
1502 comptime flag_arg: @EnumLiteral(),1638 comptime flag_arg: @EnumLiteral(),
...@@ -1512,6 +1648,31 @@ pub const Storage = enum {...@@ -1512,6 +1648,31 @@ pub const Storage = enum {
1512 };1648 };
1513 }1649 }
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
1515 pub fn dataLength(buffer: []const u32, i: usize, comptime S: type) usize {1676 pub fn dataLength(buffer: []const u32, i: usize, comptime S: type) usize {
1516 var end = i;1677 var end = i;
1517 _ = data(buffer, &end, S);1678 _ = data(buffer, &end, S);
...@@ -1536,7 +1697,7 @@ pub const Storage = enum {...@@ -1536,7 +1697,7 @@ pub const Storage = enum {
1536 },1697 },
1537 64 => {1698 64 => {
1538 defer i.* += 2;1699 defer i.* += 2;
1539 return buffer[i.*..][0..2].*;1700 return @bitCast(buffer[i.*..][0..2].*);
1540 },1701 },
1541 else => comptime unreachable,1702 else => comptime unreachable,
1542 },1703 },
...@@ -1573,6 +1734,7 @@ pub const Storage = enum {...@@ -1573,6 +1734,7 @@ pub const Storage = enum {
1573 .value = if (flag) dataField(buffer, i, container, Field.Value) else null,1734 .value = if (flag) dataField(buffer, i, container, Field.Value) else null,
1574 };1735 };
1575 },1736 },
1737 .extended => @compileError("TODO"),
1576 },1738 },
1577 },1739 },
1578 .@"extern" => comptime unreachable,1740 .@"extern" => comptime unreachable,
...@@ -1639,6 +1801,7 @@ pub const Storage = enum {...@@ -1639,6 +1801,7 @@ pub const Storage = enum {
1639 .flag_optional => {1801 .flag_optional => {
1640 return if (value.value) |v| setExtraField(buffer, i, Field.Value, v) else 0;1802 return if (value.value) |v| setExtraField(buffer, i, Field.Value, v) else 0;
1641 },1803 },
1804 .extended => @compileError("TODO"),
1642 },1805 },
1643 },1806 },
1644 .@"extern" => comptime unreachable,1807 .@"extern" => comptime unreachable,
...@@ -1662,7 +1825,7 @@ pub const Storage = enum {...@@ -1662,7 +1825,7 @@ pub const Storage = enum {
1662 else => comptime unreachable,1825 else => comptime unreachable,
1663 },1826 },
1664 .auto => switch (Field.storage) {1827 .auto => switch (Field.storage) {
1665 .flag_optional => 1,1828 .flag_optional, .extended => 1,
1666 },1829 },
1667 .@"extern" => comptime unreachable,1830 .@"extern" => comptime unreachable,
1668 },1831 },