Use an enum for the field name in list.Head

This avoids issues with zig's comptime function memoization semantics
when pointer types such as slices are used.

References: https://github.com/ziglang/zig/issues/7948
This commit is contained in:
Isaac Freund
2023-02-27 12:36:54 +01:00
parent d8f8e4a1d6
commit 45c11bb3ec
2 changed files with 34 additions and 7 deletions

View File

@@ -21,7 +21,7 @@ const C = struct {
pub fn main() void {
{
var a: wl.list.Head(B, "link") = undefined;
var a: wl.list.Head(B, .link) = undefined;
a.init();
var one = B{ .data = 1 };

View File

@@ -413,10 +413,37 @@ pub const list = struct {
reverse,
};
/// Compared to std.meta.FieldEnum this version returns an empty enum for opaque types
/// and only supports structs and opaque types.
pub fn FieldEnum(comptime T: type) type {
const field_infos = switch (@typeInfo(T)) {
.Struct => |info| info.fields,
.Opaque => [_]void{},
else => unreachable,
};
var enum_fields: [field_infos.len]std.builtin.Type.EnumField = undefined;
for (field_infos) |field, i| {
enum_fields[i] = .{
.name = field.name,
.value = i,
};
}
return @Type(.{
.Enum = .{
.layout = .Auto,
.tag_type = std.math.IntFittingRange(0, field_infos.len - 1),
.fields = &enum_fields,
.decls = &.{},
.is_exhaustive = true,
},
});
}
/// This has the same ABI as wl.list.Link/wl_list. If link_field is null, then
/// T.getLink()/T.fromLink() will be used. This allows for compatiability
/// with wl.Client and wl.Resource
pub fn Head(comptime T: type, comptime link_field: ?[]const u8) type {
pub fn Head(comptime T: type, comptime link_field: ?FieldEnum(T)) type {
return extern struct {
const Self = @This();
@@ -427,12 +454,12 @@ pub const list = struct {
}
pub fn prepend(head: *Self, elem: *T) void {
const link = if (link_field) |f| &@field(elem, f) else elem.getLink();
const link = if (link_field) |f| &@field(elem, @tagName(f)) else elem.getLink();
head.link.insert(link);
}
pub fn append(head: *Self, elem: *T) void {
const link = if (link_field) |f| &@field(elem, f) else elem.getLink();
const link = if (link_field) |f| &@field(elem, @tagName(f)) else elem.getLink();
head.link.prev.?.insert(link);
}
@@ -469,7 +496,7 @@ pub const list = struct {
.reverse => it.current.prev.?,
};
if (it.current == it.head) return null;
return if (link_field) |f| @fieldParentPtr(T, f, it.current) else T.fromLink(it.current);
return if (link_field) |f| @fieldParentPtr(T, @tagName(f), it.current) else T.fromLink(it.current);
}
};
}
@@ -494,7 +521,7 @@ pub const list = struct {
.reverse => it.future.prev.?,
};
if (it.current == it.head) return null;
return if (link_field) |f| @fieldParentPtr(T, f, it.current) else T.fromLink(it.current);
return if (link_field) |f| @fieldParentPtr(T, @tagName(f), it.current) else T.fromLink(it.current);
}
};
}
@@ -554,7 +581,7 @@ pub fn Signal(comptime T: type) type {
return extern struct {
const Self = @This();
listener_list: list.Head(Listener(T), "link"),
listener_list: list.Head(Listener(T), .link),
pub fn init(signal: *Self) void {
signal.listener_list.init();