Skip to content
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

Stage2: false dependency loop #12325

Open
Vexu opened this issue Aug 3, 2022 · 19 comments
Open

Stage2: false dependency loop #12325

Vexu opened this issue Aug 3, 2022 · 19 comments
Labels
bug Observed behavior contradicts documented or intended behavior frontend Tokenization, parsing, AstGen, Sema, and Liveness.
Milestone

Comments

@Vexu
Copy link
Member

Vexu commented Aug 3, 2022

const S = struct {
    a: *[@sizeOf(S)]u8,
};
test {
    var s: S = undefined;
    _ = s;
}
a.zig:19:10: error: struct 'a.S' depends on itself
    a: *[@sizeOf(S)]u8,
         ^~~~~~~~~~

Should be fixed by implementing lazy pointer types, lazy array types, or both.

@Vexu Vexu added bug Observed behavior contradicts documented or intended behavior frontend Tokenization, parsing, AstGen, Sema, and Liveness. labels Aug 3, 2022
@Vexu Vexu added this to the 0.10.0 milestone Aug 3, 2022
@michal-z
Copy link
Contributor

michal-z commented Aug 11, 2022

This also fails:

pub const DeviceCallback = *const fn (*Device) void;
pub const Device = struct {
    callback: DeviceCallback,
};
test {
    _ = DeviceCallback;
}

error: dependency loop detected

@Vexu Do you think this is the same bug or should I open a new issue?

@Vexu
Copy link
Member Author

Vexu commented Aug 12, 2022

It's the same bug.

@mlugg
Copy link
Member

mlugg commented Sep 7, 2022

Slight variation on this issue, this example straight-up crashes the compiler:

const Foo = struct {
    ptr: *[1]Foo,
};

test {
    const x: Foo = undefined;
    _ = x;
}

Weirdly, _ = @as(Foo, undefined) works fine - this example seems specifically related to assignment.

@Vexu
Copy link
Member Author

Vexu commented Sep 7, 2022

That is a separate bug in the LLVM backend's debug info creation, adding either --fno-LLVM or --strip avoids the crash.

@mlugg
Copy link
Member

mlugg commented Sep 7, 2022

Ah okay, thank you. Is there an open issue for that bug (just so I can reference it in a comment)?

@Vexu
Copy link
Member Author

Vexu commented Sep 7, 2022

Not that I know of.

@mitchellh
Copy link
Contributor

Note as a workaround for this I'm just hand-modifying the cimport.zig to just replace the pointers with anyopaque pointers. I've only had this issue with C imports (probably due to the style of C this sort of reference is common -- I know you can get this in pure Zig too I just haven't seen or written code in practice that does). This works for now!

@somethingelseentirely
Copy link

somethingelseentirely commented Oct 25, 2022

The following also fails:

const std = @import("std");

const node_size = 16;

const Tag = enum(u8) {
    none,
    some
};

pub const Maybe = extern union {
    none: extern struct {
        tag: Tag = .none,
        padding: [node_size - @sizeOf(Tag)]u8 = undefined,
    },
    some: Some,
};

const Body = extern struct {
    maybe: Maybe = Maybe{.none = .{}},
};

const Some = extern struct {
    const padding_len = node_size - (@sizeOf(Tag) + @sizeOf(usize));

    tag: Tag = .some,
    padding: [padding_len]u8 = [_]u8{0} ** padding_len,
    body: *Body,
};


pub fn main() !void {
    std.debug.print("{any}\n", .{@sizeOf(Maybe)});
}

Interestingly when body: *Body, is replaced with body: *Maybe, it compiles fine.

@Vexu
Copy link
Member Author

Vexu commented Oct 25, 2022

That looks very similar to my original case that I was unable to reduce.

zenith391 added a commit to capy-ui/capy that referenced this issue Nov 19, 2022
zenith391 added a commit to capy-ui/capy that referenced this issue Nov 21, 2022
This includes workarounds for ziglang/zig#12325 issue by using `*anyopaque` instead of `*T` and casting the function pointers.
giann added a commit to buzz-language/buzz that referenced this issue Dec 8, 2022
Main problem is ziglang/zig#12325 which forces
me to use *anyopaque pointers and waste time casting them back to their
actual type and possibly slowing things down because of the added
pointer alignment check
giann added a commit to buzz-language/buzz that referenced this issue Dec 8, 2022
Main problem is ziglang/zig#12325 which forces
me to use *anyopaque pointers and waste time casting them back to their
actual type and possibly slowing things down because of the added
pointer alignment check
giann added a commit to buzz-language/buzz that referenced this issue Dec 8, 2022
Main problem is ziglang/zig#12325 which forces
me to use *anyopaque pointers and waste time casting them back to their
actual type and possibly slowing things down because of the added
pointer alignment check

closes #97
@andrewrk andrewrk modified the milestones: 0.10.1, 0.11.0 Jan 10, 2023
@travisstaloch
Copy link
Contributor

i'm having a similar issue. is this the same thing? if not is there already an issue for this or should i make a one?

really wanting this feature for generated code in my protobuf-zig lib so that i don't have to resort to generating duplicate .c,/h files to achieve this.

// /tmp/tmp.zig
pub const A = extern struct { // same happens w/ non-extern struct
    b: *const B,
};

pub const B = extern struct {
    a: *const A,
};

const a = A{ .b = &b };
const b = B{ .a = &a };

test {
    _ = a;
}
$ zig test /tmp/tmp.zig
/tmp/tmp.zig:11:1: error: dependency loop detected
const a = A{ .b = &b };
^~~~~~~~~~~~~~~~~~~~~~

@Vexu
Copy link
Member Author

Vexu commented Feb 2, 2023

This issue is about types incorrectly depending on themselves while yours is caused by declarations depending on each others address recursively and reduces to:

const a = &b;
const b = &a;
test {
    _ = a;
}

@travisstaloch
Copy link
Contributor

This issue is about types incorrectly depending on themselves while yours is caused by declarations depending on each others address recursively and reduces to:

Thanks. Created #14517

@rhoot
Copy link

rhoot commented Oct 9, 2023

This also fails:

pub const DeviceCallback = *const fn (*Device) void;
pub const Device = struct {
    callback: DeviceCallback,
};
test {
    _ = DeviceCallback;
}

error: dependency loop detected

I hit this as well, in basically the same situation. Eventually I resorted to using *anyopaque instead of my equivalent of *Device and casting it in each callback. Is there a better workaround than this?

pub const DeviceCallback = *const fn (*anyopaque) void;

fn someCallback(p: *anyopaque) void {
    const device: *Device = @ptrCast(@alignCast(p));
}

@Jarred-Sumner
Copy link
Contributor

Running into this in Bun, but there’s no stack/return trace other than the comptime function generating the type, which does not point to the line causing a dependency loop

image

note: the MultiArrayList error is unrelated & usually means UB in zig’s compiler. It happens when any compiler error occurs in a a non-root module within the build

@andrewrk andrewrk modified the milestones: 0.11.1, 0.12.0 Jan 29, 2024
@andrewrk andrewrk modified the milestones: 0.12.0, 0.13.0 Jan 31, 2024
sphaerophoria added a commit to sphaerophoria/video-editor that referenced this issue May 13, 2024
Implement audio playback, hopefully motivation is self explanatory

The audio landscape is vast in linux, we have alsa, pulse, jack,
pipewire and alsa, pulse, jack interfaces into pipewire, alsa interfaces
into pulse, etc. Instead of trying to deal with this ourselves, pull in
an audio lib to deal with it for us

Just skimming online, miniaudio fits well. PortAudio and libsoundio also
seem to both be good candidates. No strong reasoning went into this
choice, the single header impl of miniaudio felt easy to work with.

We were tricked though, with the default miniaudio impl we hit a zig
compiler bug that triggers circular dependencies. Something about
using a struct pointer in a function pointer stored by the struct, see
ziglang/zig#18247 (comment)

Patch miniaudio to just use void pointers instead, this doesn't seem to
have any negative effects

Add an audio player abstraction that just injects a sin wave for now,
add test binary to try it

Potentially related issues...
ziglang/zig#12325
ziglang/zig#16419
sphaerophoria added a commit to sphaerophoria/video-editor that referenced this issue May 13, 2024
Implement audio playback, hopefully motivation is self explanatory

The audio landscape is vast in linux, we have alsa, pulse, jack,
pipewire and alsa, pulse, jack interfaces into pipewire, alsa interfaces
into pulse, etc. Instead of trying to deal with this ourselves, pull in
an audio lib to deal with it for us

Just skimming online, miniaudio fits well. PortAudio and libsoundio also
seem to both be good candidates. No strong reasoning went into this
choice, the single header impl of miniaudio felt easy to work with.

We were tricked though, with the default miniaudio impl we hit a zig
compiler bug that triggers circular dependencies. Something about
using a struct pointer in a function pointer stored by the struct, see
ziglang/zig#18247 (comment)

Patch miniaudio to just use void pointers instead, this doesn't seem to
have any negative effects

Add an audio player abstraction that just injects a sin wave for now,
add test binary to try it

Potentially related issues...
ziglang/zig#12325
ziglang/zig#16419
ziglang/zig#19392
sphaerophoria added a commit to sphaerophoria/video-editor that referenced this issue May 14, 2024
Implement audio playback, hopefully motivation is self explanatory

The audio landscape is vast in linux, we have alsa, pulse, jack,
pipewire and alsa, pulse, jack interfaces into pipewire, alsa interfaces
into pulse, etc. Instead of trying to deal with this ourselves, pull in
an audio lib to deal with it for us

Just skimming online, miniaudio fits well. PortAudio and libsoundio also
seem to both be good candidates. No strong reasoning went into this
choice, the single header impl of miniaudio felt easy to work with.

We were tricked though, with the default miniaudio impl we hit a zig
compiler bug that triggers circular dependencies. Something about
using a struct pointer in a function pointer stored by the struct, see
ziglang/zig#18247 (comment)

Patch miniaudio to just use void pointers instead, this doesn't seem to
have any negative effects

Extract audio frames from ffmepg, and feed them into our audio
subsystem. Not a ton to say here, everything is just using the APIs
provided by miniaudio and ffmpeg

Replace test video with a 20s segment of big buck bunny

Potentially related issues...
ziglang/zig#12325
ziglang/zig#16419
ziglang/zig#19392
@dee0xeed
Copy link

Just in case, one more example.

This one does not compile:

const std = @import("std");

const Foo = struct {

    const FnPtr = *const fn (*Foo, u8) void;
    data: u8,
    cb: FooCallBack,

    const FooCallBack = struct {
        fptr: FnPtr,
        data: u8,
    };

    fn init(fptr: FnPtr) Foo {
        return .{
            .data = 7,
            .cb = .{.fptr = fptr, .data = 8},
        };
    }

    fn call(foo: *Foo, data: u8) void {
        foo.cb.fptr(foo, data);
    }
};

fn bar(foo: *Foo, data: u8) void {
    std.debug.print (
        "data-1 = {}, data-2 = {}, data-3 = {}\n",
        .{foo.data, foo.cb.data, data}
    );
}

pub fn main() !void {
    var foo = Foo.init(&bar);
    foo.call(9);
}

Get

222-b.zig:6:5: error: dependency loop detected
    const FnPtr = *const fn (*Foo, u8) void;
    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

But this one (without "construtor") compiles:

const std = @import("std");

const Foo = struct {

    const FnPtr = *const fn (*Foo, u8) void;
    data: u8,
    cb: FooCallBack,

    const FooCallBack = struct {
        fptr: FnPtr,
        data: u8,
    };

    pub fn call(foo: *Foo, data: u8) void {
        foo.cb.fptr(foo, data);
    }
};

fn bar(foo: *Foo, data: u8) void {
    std.debug.print (
        "data-1 = {}, data-2 = {}, data-3 = {}\n",
        .{foo.data, foo.cb.data, data}
    );
}

pub fn main() !void {
    var foo: Foo = .{
        .data = 7,
        .cb = .{.fptr = &bar, .data = 8},
    };
    foo.call(9);
}

@cosineblast
Copy link

I'm facing a very similar issue with dependency loops with regard to function pointers, although I'm not sure it's the exact same (tested in 0.13 and 0.12.1):

const std = @import("std");

const A = struct {
    field: std.meta.Tuple(&.{ i32, B }),
};

const B = union(enum) {
    thing: *const fn (a: *A) void,
};

pub fn main() void {
    const b: B = undefined;
    _ = b;
}

Weirdly enough, the dependency loop error does not occur when using an ordinary struct instead std.meta.Tuple in A, or when B is a struct instead of a tagged union.

@Fri3dNstuff
Copy link
Contributor

the recent changes to tuples (suspected cause) now make the following code error (tested on 0.14.0-dev.2335+8594f179f):

test {
    _ = Foo;
}

const Foo = struct {
    //      ^~~~~~
    // error: struct 'main.Foo' depends on itself
    field: *struct { Foo },
};

the code compiles properly on 0.13.0

@clemenssielaff
Copy link

I've encountered a similar issue with std.ArrayListAligned. The code:

const std = @import("std");

const Node = struct {
    child: ?*NodeList,
};
const NodeList = std.ArrayListAligned(Node, @alignOf(Node));

pub fn main() void {
    var test_node = Node{
        .child = null,
    };
    test_node = test_node;
}

fails to compile with

example.zig:3:14: error: struct 'example.Node' depends on itself

See example at Godbolt

It works fine when I replace child: ?*NodeList on line 4 with child: ?*Node.

The error occurs with every version of Zig I've tested, so I'm not sure if I'm just doing it wrong.

@andrewrk andrewrk modified the milestones: 0.14.0, 0.15.0 Feb 10, 2025
@dvmason
Copy link

dvmason commented Feb 17, 2025

Another example. I've been struggling with dependency loops forever, and here is the most recent:

const execute = struct {
    const ThreadedFn = packed struct {
        f: Fn,
        const Fn = *const fn (
            process: *Process,
        ) void;
    };
};
const Process = struct {
    m: [@sizeOf(P)]u8,
    const P = extern struct {
        h: Fields,
        const Fields = extern struct {
            debugFn: execute.ThreadedFn,
        };
    };
};
fn f(_: *Process) void {}
test "die" {
    const tfn = execute.ThreadedFn{.f = &f};
    var p: Process = undefined;
    tfn.f(&p);
}

The m field and the P struct are part of my effort to break the dependency loop. I have a workaround, as the size of the Process actually is a constant, and I tune the size of the P struct to match it. The h:Fields is just to mimic my code... the loop remains with debugFn directly in P. This is probably just a more complex example of the top post problem.

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