Error: unable to resolve comptime value

const Fiber = struct {
    func: fn (*Fiber) void, // Function to execute
    is_completed: bool = false, // Whether the fiber has completed
};
...
fn runScheduler(allocator: std.mem.Allocator) !
void {
    var fibers = std.ArrayList(*Fiber).init(allocator);
    defer fibers.deinit();
...
}

$zig run abc.zig

error: unable to resolve comptime value
var fibers = std.ArrayList(*Fiber).init(allocator);

Regards!

Raw function types like fn (*Fiber) void are comptime types and a structure containing a comptime type member becomes comptime itself. If you want to pass around a reference to a function at runtime you should use a const function pointer, e.g. change the type of the func member to *const fn (*Fiber) void.

2 Likes

OP, could you please provide the full source file and error message? The answer above me is correct, but I expected the compiler to emit a more useful error message in this case (in particular, I expected it to include several notes explaining the issue). If it’s not, that’s probably something I can fix.

abc.zig:19:45: error: unable to resolve comptime value
var fibers = std.ArrayList(*Fiber).init(allocator); ^~~~~~~~~
abc.zig:19:45: note: argument to function being called at comptime must be comptime-known
/data/data/com.termux/files/usr/lib/zig/lib/std/array_list.zig:53:43: note: expression is evaluated at comptime because the function returns a comptime-only type ‘array_list.ArrayListAligned(*abc.Fiber,null)’
pub fn init(allocator: Allocator) Self {
^~~~
/data/data/com.termux/files/usr/lib/zig/lib/std/array_list.zig:40:16: note: struct requires comptime because of this field
items: Slice,
^~~~~
abc.zig:5:11: note: struct requires comptime because of this field
func: fn (*Fiber) void, // Function to execute
^~~~~~~~~~~~~~~~
abc.zig:5:11: note: use ‘*const fn (comptime *abc.Fiber) void’ for a function pointer type
func: fn (*Fiber) void, // Function to execute

const std = @import("std");

// Fiber structure
const Fiber = struct {
    func: fn (*Fiber) void, // Function to execute
    is_completed: bool = false, // Whether the fiber has completed
};

// Example fiber task
fn exampleTask(fiber: *Fiber) void {
    std.debug.print("Task started\n", .{});
    std.debug.print("Task resumed\n", .{});
    std.debug.print("Task completed\n", .{});
    fiber.is_completed = true;
}

// Scheduler
fn runScheduler(allocator: std.mem.Allocator) !void {
    var fibers = std.ArrayList(*Fiber).init(allocator);
    defer fibers.deinit();
// Create fibers
    const fiber1 = try allocator.create(Fiber);
    fiber1.func = exampleTask;
    try fibers.append(fiber1);

    const fiber2 = try allocator.create(Fiber);
    fiber2.func = exampleTask;
    try fibers.append(fiber2);

// Run fibers until all are completed
    while (true) {
        var all_completed = true;
        for (fibers.items) |fiber| {
            if (!fiber.is_completed) {
                all_completed = false;
                fiber.func(fiber); // Run the fiber's function
            }
        }
        if (all_completed) break;
    }

// Clean up fibers
    for (fibers.items) |fiber| {
        allocator.destroy(fiber);
    }
}

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    const allocator = gpa.allocator();

    try runScheduler(allocator);
}

After change it to func: *const fn (*Fiber) void, it works like a charm !

Nicely formatted for anyone else reading (to do this, you put three backticks, i.e. ```, before and after the block of text):

abc.zig:19:45: error: unable to resolve comptime value
    var fibers = std.ArrayList(*Fiber).init(allocator);
                                            ^~~~~~~~~
abc.zig:19:45: note: argument to function being called at comptime must be comptime-known
/data/data/com.termux/files/usr/lib/zig/lib/std/array_list.zig:53:43: note: expression is evaluated at comptime because the function returns a comptime-only type ‘array_list.ArrayListAligned(*abc.Fiber,null)’
        pub fn init(allocator: Allocator) Self {
                                          ^~~~
/data/data/com.termux/files/usr/lib/zig/lib/std/array_list.zig:40:16: note: struct requires comptime because of this field
        items: Slice,
               ^~~~~
abc.zig:5:11: note: struct requires comptime because of this field
    func: fn (*Fiber) void, // Function to execute
          ^~~~~~~~~~~~~~~~
abc.zig:5:11: note: use ‘*const fn (comptime *abc.Fiber) void’ for a function pointer type
    func: fn (*Fiber) void, // Function to execute
          ^~~~~~~~~~~~~~~~

The error actually is telling you what’s wrong! I’m not faulting you for not understanding it, it’s totally fine to need help with this sort of thing when you’re starting out, but it might help you in future if I walk you through what this error is telling you.

The first bit is telling you that it couldn’t resolve allocator at compile-time. Well, that makes some sense – after all, it’s a runtime value. But the question then becomes, why would it be trying to evaluate it at comptime? Everything you’re doing here looks like a runtime operation!

Well, that’s what the error notes are going to tell us. The first note says:

note: argument to function being called at comptime must be comptime-known

Okay, well, that’s logical, but it’s just moved the question… why is the function being called at comptime? Let’s see if the next note tells us:

note: expression is evaluated at comptime because the function returns a comptime-only type ‘array_list.ArrayListAligned(*abc.Fiber,null)’

Right – this note is telling us that it’s because our ArrayList(*Fiber) type is comptime-only. (By the way, error message, at least at the time of writing, will always print ArrayList(T) as ArrayListAligned(T, null); that’s just a property of how ArrayList is implemented. You’ll learn to ignore the “aligned” bit!)

Well, that’s moved the question yet again – why would that ArrayList(*Fiber) type be comptime-only? Let’s read on, perhaps the next note will tell us…

note: struct requires comptime because of this field
        items: Slice,
               ^~~~~

I’ve included the source snippet here, because it’s relevant; items is the field in ArrayList(*Fiber) which makes it a comptime-only type. The only way a field can make a struct be comptime-only, is by that field itself being comptime-only. So, Slice is a comptime-only type. Checking the array_list.zig source code, on the line it pointed at, we can check the definition of Slice and see that it’ll be []*Fiber. (This is the one place this error could be possibly improved; this note might be better if it wrote out the type, i.e. struct requires comptime because of field with comptime-only type '[]*Fiber).

Okay, well, a slice is comptime-only only if its element type is comptime-only, and the same goes for normal pointers; so Fiber must be comptime-only. Why’s that? Let’s read on:

abc.zig:5:11: note: struct requires comptime because of this field
    func: fn (*Fiber) void, // Function to execute
          ^~~~~~~~~~~~~~~~

Okay, so fn (*Fiber) void must be a comptime-only type. Why’s that?

abc.zig:5:11: note: use ‘*const fn (comptime *abc.Fiber) void’ for a function pointer type
    func: fn (*Fiber) void, // Function to execute
          ^~~~~~~~~~~~~~~~

Ah, there we go! We thought that fn (*Fiber) void was a function pointer, but it’s not; it’s a different type, which is comptime-only. Even if you don’t know what a “function body type” is, the hint is giving you something which sounds like what you want, so you can try it and hopefully it works!

12 Likes