I’m trying to implement the Nanopass framework in Zig which requires extending union (enum)
among other things. I took a stab at it below but is there a better way to do it?
Is there a way to make L2
show as nanopass.L2
instead of nanopass.extend(nanopass.L1,nanopass.L2__union_19543)
?
Last but not least, unlike enum fields, union fields have an extra alignment
field. I’m concerned that the values may become invalid if I just concatenate the two sets of union fields. Do I need to recalculate it for my new composite union L2
and how if so?
const std = @import("std");
pub const L1 = union(enum) {
a: usize,
b: usize,
};
pub const L2 = extend(L1, union(enum) {
c: usize,
});
pub fn extend(Src: type, Dst: type) type {
const src = @typeInfo(Src).@"union";
const dst = @typeInfo(Dst).@"union";
const src_len = src.fields.len;
const dst_len = dst.fields.len;
var enum_fields: [src_len + dst_len]std.builtin.Type.EnumField = undefined;
inline for (src.fields, 0..) |field, i| {
enum_fields[i] = .{ .name = field.name, .value = i };
}
inline for (dst.fields, src_len..) |field, i| {
enum_fields[i] = .{ .name = field.name, .value = i };
}
const fields = src.fields ++ dst.fields;
const dst_enum = @Type(.{
.@"enum" = .{
.tag_type = std.math.IntFittingRange(0, fields.len - 1),
.fields = &enum_fields,
.decls = &.{},
.is_exhaustive = true,
},
});
return @Type(.{
.@"union" = .{
.decls = &.{},
.layout = .auto,
.tag_type = dst_enum,
.fields = src.fields ++ dst.fields,
},
});
}
// ❯ zig run nanopass.zig
// nanopass.extend(nanopass.L1,nanopass.L2__union_19543){ .a = 10 }
// nanopass.L1{ .a = 10 }%
pub fn main() void {
std.debug.print("{}\n{}", .{ L2{ .a = 10 }, L1{ .a = 10 } });
}
To complete the framework, I’ll need to give users the ability to delete arms of the union.
I also need to write the pass function that would recursively copy L1 into L2 for arms of the union we are not adding.
P.S. I need to do a lot of AST manipulation for a new language I’m working on and I’d like to use the nanopass approach.