Configuration: type safety for extended pattern

This commit is contained in:
Andrew Kelley
2026-02-19 21:13:00 -08:00
parent bb73f10e26
commit 518588e9fc
3 changed files with 187 additions and 26 deletions
+3 -5
View File
@@ -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 });
}
+8 -8
View File
@@ -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,
+176 -13
View File
@@ -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,
},