Hi everyone! I’m maintaining pretty lib that recursively pretty prints arbitrary data structures. Someone reported a case (Fails to compile if `max_depth` option is 0 and printed type is recursive · Issue #4 · timfayz/pretty · GitHub) where the printer fails to traverse the recursive type like this:
const A = union(enum) {
int: u8,
a: *const @This(),
};
try pretty.print(alloc, A{ .int = 10 }, .{ .max_depth = 0 }); // max_depth controls the recursion depth, 0 = no limit
Running the above gives:
zsh: segmentation fault zig run main.zig
I was trying to reduce the noise behind full pretty logic into a slim reproducible example but when I do so, it begins to work just fine :). Anyway, here is the “scheme”:
const std = @import("std");
fn traverse(val: anytype) void {
const T = @TypeOf(val);
switch (@typeInfo(T)) {
.Union => {
if (@typeInfo(T).Union.tag_type) |Tag| {
switch (@as(Tag, val)) {
inline else => |tag| {
traverse(@field(val, @tagName(tag)));
},
}
}
},
.Pointer => {
switch (@typeInfo(T).Pointer.size) {
.One => {
traverse(val.*); // this is what in the original code leads to segfault (commenting it out makes the failure disappear)
},
else => |load| std.log.err("{}", .{load}),
}
},
else => |load| std.log.err("{}", .{load}),
}
}
pub fn main() !void {
const Union = union(enum) {
int: u8,
self: *const @This(),
};
const val = Union{ .int = 10 };
traverse(val);
}
I put a comment above on the line which, in the original code, leads to segfault. Here is the link to that line in source: pretty/src/pretty.zig at 794e0408c39781a8fdf4b3f070e786526c7f3eca · timfayz/pretty · GitHub
Zig version: 0.12.0
.
My best guess is that it happens because zig, during comptime, attempts to unroll the chain of calls to set up proper types for anytype
arguments in every traverse(val: anytype)
call. I think so because even if I do @comptimeLog(1)
at the very first line of the pretty.print()
, zig cannot reach it out because it does some other analysis first and crashes. I’m kinda stuck in the loop. Any ideas where can I start?