Value with comptime-only type 'asyncio.xselectScope((function 'chanSend'),(function 'report_add'))' depends on runtime control flow

Hi folks,
I am not understanding why this would be an issue for a compiler.

pub fn xselectScope(func: anytype, cb: anytype) type {
    return struct {
        const Self = @This();
        func: @TypeOf(func),
        args: libcoro.ArgsTuple(@TypeOf(func)),
        cb: @TypeOf(cb),
        fn run(self: Self, _chan: *SelectChan, _idx: usize) anyerror!void {
            const ret =
                if (@typeInfo(@TypeOf(self.func)).Fn.return_type == std.builtin.Type.ErrorUnion)
                try @call(.auto, self.func, self.args)
            else
                @call(.auto, self.func, self.args);
            try _chan.send(_idx);
            @call(.auto, self.cb, .{ret});
        }
    };
}
fn chanSend(chan: *UsizeChannel) !void {
    _ = chan;
}

fn report_chan(err: anyerror!void) void {
    _ = err;
}

fn selecMain() !void {
 const chan: *UsizeChannel = try env.allocator.create(UsizeChannel);
 chan.* = UsizeChannel.init(null);
 const ChanScope = aio.xselectScope(chanSend, report_add);
 const chan_scope: ChanScope = .{ .func = chanSend, .args = .{chan}, .cb = report_chan };
}
src/test_aio.zig:528:36: error: value with comptime-only type 'asyncio.xselectScope((function 'chanSend'),(function 'report_chan'))' depends on runtime control flow
    const chan_scope: ChanScope = .{ .func = chanSend, .args = .{chan}, .cb = report_chan };
                                  ~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/asyncio.zig:68:12: note: struct declared here
    return struct {

Hi @needon1997 welcome to ziggit :slight_smile:

Replace xselectScope function arguments with types. I am not sure if this is going to solve the problem, but give it a try.

pub fn XselectScope(Func: type, Cb: type) type {
    return struct {
        const Self = @This();
        func: Func,
        args: std.meta.ArgsTuple(Func),
        cb: Cb,
        fn run(self: Self, _chan: *SelectChan, _idx: usize) anyerror!void {
            const ret =
                if (@typeInfo(@TypeOf(self.func)).Fn.return_type == std.builtin.Type.ErrorUnion)
                try @call(.auto, self.func, self.args)
            else
                @call(.auto, self.func, self.args);
            try _chan.send(_idx);
            @call(.auto, self.cb, .{ret});
        }
    };
}

call it using:

const ChanScope = aio.XselectScope(@TypeOf(chanSend), @TypeOf(report_add));

note also the change to std.meta.ArgsTuple

The error you’re getting doesn’t make it very clear why the type returned by xselectScope is comptime-only, and the example code isn’t self-contained so I can’t test it myself, but I’m fairly sure it has to do with @TypeOf(func) and @TypeOf(cb).

The way you’re currently passing those arguments is as aio.xselectScope(chanSend, report_add): the result of @TypeOf(chanSend) here is going to be fn (*UsizeChannel) !void, which is a function body type, as opposed to *const fn (*UsizeChannel) !void, which is a function pointer type. Function body types are comptime-only, while function pointer types can be stored at runtime. The Zig wiki has an overview of the difference in the self-hosted compiler upgrade guide, as this was one of the language changes made as part of that upgrade.

So, in order to fix this, you may want to try calling the function as aio.xselectScope(&chanSend, &report_add) to pass function pointers rather than function bodies. That will also require changes to the @typeInfo logic in xselectScope.

Thanks dimdin,
I tried your suggestion and found that it still show me a similar compiler issue. And the issue finally get resolved by the advice provided by ianprime0509

1 Like

Thanks ianprime0509,
I changed my code as your advises and it finally works.

1 Like

The issue finally get resolved by changing from function body to function pointer as a filed.

fn report_chan(err: anyerror!void) void {
    _ = err catch |_err| {
        std.debug.print("Err {}", .{_err});
    };
}

fn chanSend(chan: *UsizeChannel) !void {
    _ = chan;
}

fn selecMain() !void {
    const stack_size: usize = 1024 * 16;
    const stack1 = try libcoro.stackAlloc(env.allocator, stack_size);
    defer env.allocator.free(stack1);
    const stack2 = try libcoro.stackAlloc(env.allocator, stack_size);
    defer env.allocator.free(stack2);
    const chan: *UsizeChannel = try env.allocator.create(UsizeChannel);
    defer env.allocator.destroy(chan);

    chan.* = UsizeChannel.init(null);
    defer chan.close();

    const ChanScope = aio.xselectScope(@TypeOf(chanSend), @TypeOf(report_chan));
    const chan_scope: ChanScope = ChanScope.init(&chanSend, .{chan}, &report_chan);

    try aio.xselect(.{
        .{ chan_scope, stack2 },
    });
}
const SelectChan = Channel(usize, ChannelConfig.oneshot);

pub fn xselectScope(Func: type, Cb: type) type {
    return struct {
        const Self = @This();
        func: *const Func,
        args: libcoro.ArgsTuple(Func),
        cb: *const Cb,
        pub fn run(self: Self, _chan: *SelectChan, _idx: usize) anyerror!void {
            const ret =
                @call(.auto, self.func, self.args);
            try _chan.send(_idx);
            @call(.auto, self.cb, .{ret});
        }

        pub fn init(_func: *const Func, _args: libcoro.ArgsTuple(Func), _cb: *const Cb) Self {
            return .{ .func = _func, .args = _args, .cb = _cb };
        }
    };
}