Skip to content

@alignOf causing erroneous dependency loop error when used in self-referential union #17874

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
LordMZTE opened this issue Nov 5, 2023 · 4 comments
Labels
bug Observed behavior contradicts documented or intended behavior frontend Tokenization, parsing, AstGen, Sema, and Liveness.
Milestone

Comments

@LordMZTE
Copy link
Contributor

LordMZTE commented Nov 5, 2023

Zig Version

0.12.0-dev.1396+f6de3ec96

Steps to Reproduce and Observed Behavior

Create a file bug.zig:

const std = @import("std");

fn Union(comptime T: type) type {
    const enum_fields = &[1]std.builtin.Type.EnumField{.{
        .name = "x",
        .value = 0,
    }};

    const Enum = @Type(.{ .Enum = .{
        .tag_type = u1,
        .fields = enum_fields,
        .decls = &.{},
        .is_exhaustive = true,
    } });

    const union_fields = &[1]std.builtin.Type.UnionField{.{
        .name = "x",
        .type = T,
        .alignment = @alignOf(T), // This causes the error.
    }};

    return @Type(.{ .Union = .{
        .layout = .Auto,
        .tag_type = Enum,
        .fields = union_fields,
        .decls = &.{},
    } });
}

test {
    _ = struct {
        const A = struct { b: *B };
        const B = Union(A);
    }.B;
}
  1. zig test bug.zig
bug.zig:33:9: error: dependency loop detected
        const B = Union(A);
        ^~~~~~~~~~~~~~~~~~

This might seem logical at first, as A's alignment seems to be dependent on B.
However, if the implementation of Union is replaced with this:

return union(enum) {
    x: T,
};

the compiler can correctly infer the alignment of the x field to be 8.

Considering this works, it seems logical to assume that the @Type-based implementation also does.

Expected Behavior

The code compiles.

@LordMZTE LordMZTE added the bug Observed behavior contradicts documented or intended behavior label Nov 5, 2023
@Vexu Vexu added the frontend Tokenization, parsing, AstGen, Sema, and Liveness. label Nov 5, 2023
@Vexu Vexu added this to the 0.14.0 milestone Nov 5, 2023
@LordMZTE
Copy link
Contributor Author

LordMZTE commented Nov 6, 2023

In my use-case, this seems to be fixable by setting .alignment = 0, making the compiler infer it. We should probably document that this is a valid value and consider setting the default value of the field to 0.

@dweiller
Copy link
Contributor

dweiller commented Nov 7, 2023

Possibly related to #17255 - that issue does not use comptime, but does use @alignOf.

@mlugg
Copy link
Member

mlugg commented Nov 7, 2023

Nope, unrelated. This issue isn't really a bug - resolving the alignment of T requires evaluating Union(A), triggering the dependency loop. The behaviour that makes this not work is that reified struct types do not keep laztly alignment values lazy for future resolution, instead resolving the entire reified type immediately. This could be changed, but the merits of doing so are unclear.

However, we should definitely document the fields of std.builtin.Type better.

@vadim-za
Copy link

The following variation of the originally reported code however does compile:

const std = @import("std");

fn Union(comptime T: type) type {
    const enum_fields = &[1]std.builtin.Type.EnumField{.{
        .name = "x",
        .value = 0,
    }};

    const Enum = @Type(.{ .@"enum" = .{
        .tag_type = u1,
        .fields = enum_fields,
        .decls = &.{},
        .is_exhaustive = true,
    } });

    const union_fields = &[1]std.builtin.Type.UnionField{.{
        .name = "x",
        .type = T,
        .alignment = @alignOf(T), // This causes the error.
    }};

    return @Type(.{ .@"union" = .{
        .layout = .auto,
        .tag_type = Enum,
        .fields = union_fields,
        .decls = &.{},
    } });
}

pub fn main() void {
    const decls = struct {
        const A = struct { b: *B };
        const B = Union(A);
    };
    var t: decls.A = undefined;
    const B0 = decls.B; // if this line is shifted up above the preceding one, the error is back
    _ = B0;
    _ = &t;
}

It however yields a dependency loop error, if the declaration of B0 is put before declaration of t. It looks as if somehow the dependency loop is or is not an issue, depending on at which point it's entered first.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Observed behavior contradicts documented or intended behavior frontend Tokenization, parsing, AstGen, Sema, and Liveness.
Projects
None yet
Development

No branches or pull requests

5 participants