Skip to content

Commit

Permalink
std.testing.refAllDeclsRecursive: Stop self-declaring recursion
Browse files Browse the repository at this point in the history
I didn't see much on a contributing guide, so I tried to work off of
others. Tell me if I missed anything I need. And thanks for reviewing.

Old behavior:

Whenever a type referenced itself, like

```zig
pub const A = struct {
    pub const D = A;
};
```

refAllDeclsRecursive would crash the test with a status code 11 because
of the recursion.

More complex recursion like two things referencing each other would do
the same.

```zig
pub const A = struct {
    pub const D = A;
    pub const C = B;
};

pub const B = struct {
    pub const C = A;
};
```

New Behavior:
This recurs down the whole tree and keeps track of the types as it goes.
This will go over types multiple times, but it will not recurs endlessly
like now it is currently.
The only way I could think of to get it to only recurs down each tree
once was with a array or comptime allocation, and that had more
limitations (or wasn't possible yet).

Limitations:
(potentially) slower compile times on large projects using this, but it
shouldn't be very much.
I did some tests, but I wasn't able to do too many, so someone please
help with that.

I am also not good at zig yet, so any ideas on how to improve it are
welcome.

I don't know if this is quite a good fit for the standard library, but
it doesn't do much more than the function did before, but it is far more
useful and (shouldn't) crash if you have something referencing itself.

Tests:
I made a more complicated example, and it seemed to reference everything
(I checked with @CompileError()).

```zig
pub const A = struct {
    pub const Z = A;
    pub const W = B;
    pub const F = struct {
        pub const U = A;
    };
};

pub const B = struct {
    pub const Z = A;
    pub const W = C;
};

pub const C = struct {
    pub const Z = A;
    pub const W = D;
};

pub const D = struct {
    pub const Z = E;
    pub const W = D;
};

pub const E = struct {
    pub const D = A;
    pub const W = E;
};
```
Again, if they are very large and deep trees, it would likely be slow to
compile the test, but I don't know any way around that, or making this
better.

Thanks, this is a wonderful language and I would like to help out where
I can.
  • Loading branch information
elijahimmer committed Apr 30, 2024
1 parent 956f53b commit 20b5b91
Showing 1 changed file with 7 additions and 1 deletion.
8 changes: 7 additions & 1 deletion lib/std/testing.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1120,10 +1120,16 @@ pub fn refAllDecls(comptime T: type) void {
/// For deep types, you may use `@setEvalBranchQuota`.
pub fn refAllDeclsRecursive(comptime T: type) void {
if (!builtin.is_test) return;
comptime refAllDeclsRecursiveInterior(T, &[0]type{});
}

/// helper function to do the actual recursion for refAllDeclsRecursive
fn refAllDeclsRecursiveInterior(comptime T: type, comptime typeRecord: []const type) void {
inline for (comptime typeRecord) |r| if (T == r) return;
inline for (comptime std.meta.declarations(T)) |decl| {
if (@TypeOf(@field(T, decl.name)) == type) {
switch (@typeInfo(@field(T, decl.name))) {
.Struct, .Enum, .Union, .Opaque => refAllDeclsRecursive(@field(T, decl.name)),
.Struct, .Enum, .Union, .Opaque => refAllDeclsRecursiveInterior(@field(T, decl.name), typeRecord ++ .{T}),
else => {},
}
}
Expand Down

0 comments on commit 20b5b91

Please sign in to comment.