Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
- name: Setup Zig
uses: mlugg/setup-zig@v2
with:
version: 0.15.2
version: 0.16.0

- name: Run tests
run: zig build test
Expand Down
6 changes: 3 additions & 3 deletions build.zig.zon
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@
.name = .zig_utils,
.version = "0.6.1",
.fingerprint = 0x6dc482caf73c4a75,
.minimum_zig_version = "0.15.2",
.minimum_zig_version = "0.16.0",

.dependencies = .{
.zspec = .{
.url = "https://github.com/apotema/zspec/archive/v0.7.0.tar.gz",
.hash = "zspec-0.7.0-jaKLbVqmAwDxLB7GYHuw354BQp7_uBSN9rSBUIfAx5xy",
.url = "https://github.com/apotema/zspec/archive/v0.9.1.tar.gz",
.hash = "zspec-0.9.1-jaKLbbX4AwBKANdetxzzWc3UTO0UY0lcJJzTagQHlt5K",
},
},

Expand Down
43 changes: 13 additions & 30 deletions src/zon_coercion.zig
Original file line number Diff line number Diff line change
Expand Up @@ -258,55 +258,38 @@ fn MergedStructType(comptime BaseType: type, comptime OverridesType: type) type
}
}

comptime var fields: [field_count]std.builtin.Type.StructField = undefined;
comptime var field_names: [field_count][]const u8 = undefined;
comptime var field_types: [field_count]type = undefined;
comptime var field_attrs: [field_count]std.builtin.Type.StructField.Attributes = undefined;
comptime var i = 0;

inline for (base_fields) |bf| {
if (@hasField(OverridesType, bf.name)) {
inline for (override_fields) |of| {
if (comptime std.mem.eql(u8, of.name, bf.name)) {
fields[i] = .{
.name = bf.name,
.type = of.type,
.default_value_ptr = null,
.is_comptime = false,
.alignment = @alignOf(of.type),
};
field_names[i] = bf.name;
field_types[i] = of.type;
field_attrs[i] = .{ .@"align" = @alignOf(of.type) };
}
}
} else {
fields[i] = .{
.name = bf.name,
.type = bf.type,
.default_value_ptr = null,
.is_comptime = false,
.alignment = @alignOf(bf.type),
};
field_names[i] = bf.name;
field_types[i] = bf.type;
field_attrs[i] = .{ .@"align" = @alignOf(bf.type) };
}
i += 1;
}

inline for (override_fields) |of| {
if (!@hasField(BaseType, of.name)) {
fields[i] = .{
.name = of.name,
.type = of.type,
.default_value_ptr = null,
.is_comptime = false,
.alignment = @alignOf(of.type),
};
field_names[i] = of.name;
field_types[i] = of.type;
field_attrs[i] = .{ .@"align" = @alignOf(of.type) };
i += 1;
}
}

return @Type(.{
.@"struct" = .{
.layout = .auto,
.fields = &fields,
.decls = &.{},
.is_tuple = false,
},
});
return @Struct(.auto, null, &field_names, &field_types, &field_attrs);
Comment on lines +261 to +292

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The current implementation of MergedStructType uses a nested inline for loop to find override types, resulting in $O(N \cdot M)$ complexity at comptime. This can be simplified and optimized using std.meta.fieldInfo to directly access field metadata by name. Additionally, using the actual alignment from the source fields (bf.alignment, of.alignment, or info.alignment) is preferred over forcing @alignOf(type) to ensure any custom alignment specified in the original types is preserved. Finally, for the @Struct call, passing &.{} instead of null for the declarations argument is more idiomatic for a struct with no declarations.

    comptime var field_names: [field_count][]const u8 = undefined;
    comptime var field_types: [field_count]type = undefined;
    comptime var field_attrs: [field_count]std.builtin.Type.StructField.Attributes = undefined;
    comptime var i = 0;

    inline for (base_fields) |bf| {
        field_names[i] = bf.name;
        if (@hasField(OverridesType, bf.name)) {
            const info = std.meta.fieldInfo(OverridesType, bf.name);
            field_types[i] = info.type;
            field_attrs[i] = .{ .@"align" = info.alignment };
        } else {
            field_types[i] = bf.type;
            field_attrs[i] = .{ .@"align" = bf.alignment };
        }
        i += 1;
    }

    inline for (override_fields) |of| {
        if (!@hasField(BaseType, of.name)) {
            field_names[i] = of.name;
            field_types[i] = of.type;
            field_attrs[i] = .{ .@"align" = of.alignment };
            i += 1;
        }
    }

    return @Struct(.auto, &.{}, &field_names, &field_types, &field_attrs);

}

/// Check if a struct type has any fields
Expand Down
2 changes: 1 addition & 1 deletion tests/root.zig
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@ pub const heuristics_test = @import("heuristics_test.zig");
pub const zon_coercion_test = @import("zon_coercion_test.zig");

test {
std.testing.refAllDeclsRecursive(@This());
std.testing.refAllDecls(@This());

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Switching to refAllDecls silently skips all nested tests

High Severity

Changing refAllDeclsRecursive to refAllDecls silently disables the entire test suite. Every test file uses zspec's nested-struct pattern (e.g., pub const AStarSpec = struct { pub const basic_pathfinding = struct { test "..." { … } } }), with zero top-level test blocks. refAllDecls only references the direct @import declarations without recursing into the nested structs, so the compiler never analyzes the inner test blocks. The zig build test passes vacuously — the single unnamed test in root.zig succeeds, but none of the actual test logic runs.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 6ad88cb. Configure here.

}
Loading