Basically, I had a struct with only one field and decided to turn it into an enum instead, because I had quite a few constants of type @This() which I thought could be more clear and terse as enum fields, but I ran into a problem turning the constants into fields. Below is a minimal reproduction of the problem I ran into. Why do these fields create circular dependency errors when the constant declarations don’t? Because this is an enum, aren’t they practically the same? Is it better to use a struct with a single field instead of a non-exhaustive enum?
test.zig:
pub const E = enum(u32) {
/// What this function does doesn't matter
pub fn foo(x: E) u32 {
return @intFromEnum(x) + 100;
}
pub const this_works: E = @enumFromInt(foo(.zero));
pub const this_works_too: E = @enumFromInt(foo(.one));
pub const zero: E = @enumFromInt(0);
one = 1,
this_doesnt_work = foo(.zero),
this_doesnt_work_either = foo(.one),
_,
};
test {
@import("std").testing.refAllDecls(E);
}
console output:
$ zig test test.zig
error: dependency loop with length 2
test.zig:13:24: note: type 'test.E' uses value of declaration 'test.E.foo' here
this_doesnt_work = foo(.zero),
^~~
test.zig:3:16: note: value of declaration 'test.E.foo' depends on type 'test.E' for function parameter declared here
pub fn foo(x: E) u32 {
^~~~
note: eliminate any one of these dependencies to break the loop
because these are fields, they are a part of the types “shape”
Those fields numeric values depend on calling foo
foo depends on the enum type itself
so by extension those fields depend on their own type.
I can see that, my question is why the same rule doesn’t apply to declarations which are also part of a type. I really don’t see why this shouldn’t be allowed anyway
They work because the type doesn’t depend on them
if, however, the type did depend on them (and they depended on the type), you would get the same error
const E = enum(u8) {
foo = @intFromEnum(E.baz), // error cause baz depends on type
bar = safe_baz, // works cause safe_baz does not depend on the typ
_,
const baz: E = @enumFromInt(1);
const safe_baz = 2;
};
FYI your code also has examples of this: foo (functions are declarations) and zero whose type is E.
You didn’t get an error for zero simply because foo was analysed first.
..but the declarations DO depend on the rest of the type. They depend on foo.
Sorry mistyped, yes they depend on the type, but the type doesn’t depend on them
A non-exhaustive enum doesn’t depend on the fields any more than it depends on the declarations though? The only thing that influences the structure of the type is the backing integer type
You are thinking of memory structure, at the point this error happens is analysing semantic structure, the enum fields and their values absolutely do matter.
Perhaps that will change perhaps it won’t.
At least I prefer it this way as it is consistent with other types.
Maybe this pair of examples will be useful:
const T = struct {
foo: u32
bar: [@sizeOf(T)]u8
};
const T = struct {
foo: u32,
const bar: [@sizeOf(T)]u8 = @splat(0);
};
The first can’t work, as that’s a diverging recursion. The second one is fine though. This sort of thing is what motivates some difference.
It is debatable where exactly the boundary lies. In particular, default field values could be made independent, and, IIRC, that’s how it used to work, and then that was simplified (for incremental compilation) to “field definition as a whole can’t depend on the type”, I think.
1 Like
On the other hand this is a non-exhaustive enum. So the amount of possible values is already given via different means.
Your example would be more similar to this which one should think should be able to work since the types size is given by the u8:
const T = packed struct(u8) {
first_half: @Int(.unsigned, half),
second_half: @Int(.unsigned, @sizeOf(T) * 8 - half),
const half = @sizeOf(T) / 2 * 8;
};
and changing it to this does:
const T = packed struct(T.Backing) {
first_half: @Int(.unsigned, half),
second_half: @Int(.unsigned, @sizeOf(Backing) * 8 - half),
const half = @sizeOf(Backing) / 2 * 8;
const Backing = u8;
};
1 Like