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

Unexpected type dependency loop when declaring a function pointer to a function that returns its own type. #18664

Open
dimdin opened this issue Jan 24, 2024 · 6 comments
Labels
bug Observed behavior contradicts documented or intended behavior

Comments

@dimdin
Copy link

dimdin commented Jan 24, 2024

Zig Version

0.11.0, 0.12.0-dev.2327+b0c8a3f31

Steps to Reproduce and Observed Behavior

state.zig:6:1: error: dependency loop detected
const StateFn = *const fn (In, *Out) ?StateFn;
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Example program:

const std = @import("std");

const In = struct {};
const Out = struct {};

const StateFn = *const fn (In, *Out) ?StateFn;

// trampoline
fn run(in: In, out: *Out, initial_state: StateFn) void {
    var current_state: StateFn = initial_state;
    while (current_state(in, out)) |state| {
        current_state = state;
    }
}

fn firstState(in: In, out: *Out) ?StateFn {
    _ = in;
    _ = out;
    std.log.debug("in first state", .{});
    return lastState;
}

fn lastState(in: In, out: *Out) ?StateFn {
    _ = in;
    _ = out;
    std.log.debug("in last state", .{});
    return null;
}

test "run state machine" {
    var out: Out = undefined;
    const in: In = undefined;
    run(in, &out, firstState);
}

Expected Behavior

Expecting zig compiler to accept the function pointer declaration of a function that returns itself.
Such declarations are useful in lexer/scanner trampolines.

Example golang code: https://cs.opensource.google/go/go/+/master:src/text/template/parse/lex.go;l=110

type stateFn func(*lexer) stateFn
@dimdin dimdin added the bug Observed behavior contradicts documented or intended behavior label Jan 24, 2024
@nektro
Copy link
Contributor

nektro commented Jan 24, 2024

Duplicate #12325 likely

@nektro
Copy link
Contributor

nektro commented Jan 28, 2024

I get this running it

test.zig:6:43: error: use of undeclared identifier 'StateFn'
    const StateFn = *const fn (In, *Out) ?StateFn;
                                          ^~~~~~~

@dimdin
Copy link
Author

dimdin commented Jan 28, 2024

With the latest master binaries produced on 2024-01-25, zig version "0.12.0-dev.2341+92211135f" I am still getting "dependency loop detected".
Probably a recent commit changed the behavior.
But the issue is still there.

@dimdin
Copy link
Author

dimdin commented Feb 25, 2024

Related dependency loop issues #16932 and #131

smaller test case:

const StateFn = *const fn () ?StateFn;

fn done() ?StateFn {
    return null;
}

test {
    _ = done();
}

zig 0.12.0-dev.2928+6fddc9cd3 output:

state.zig:1:1: error: dependency loop detected
const StateFn = *const fn () ?StateFn;
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

@mlugg
Copy link
Member

mlugg commented Feb 25, 2024

I should note you can work around this by wrapping this data in a struct:

const State = struct {
    evalFn: *const fn () ?State,
};

The error here happens because we only have lazy resolution (which permits recursive definitions) for structs, unions, enums, and opaques. It's not exactly a duplicate, but is heavily related to #12325.

@dimdin
Copy link
Author

dimdin commented Feb 25, 2024

I should note you can work around this by wrapping this data in a struct:

Thank you very much @mlugg for the workaround

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
Projects
None yet
Development

No branches or pull requests

3 participants