Making a value parameter comptime makes it a type?

Hello,

const Channel = std.meta.FieldEnum(zigimg.color.Rgb24);
fn f(
    in: *const zigimg.Image,
    out: *zigimg.Image,
    buf: []i16,
    comptime channel: Channel,
) void {
    const col = comptime @tagName(channel);
    ...

Is my code. I really don’t understand, why I get this compile error, though:

src/main.zig:207:35: error: expected enum or union; found 'type'
    const col = comptime @tagName(channel);

When removing the comptime keyword in from of channel, I get a normal compile error telling me that the compiler is unable to resolve the value at comptime. I really dont wanna make a big inline else but current I don’t see an alternative.

I have tried using different “channel selector types”, a plain enum { a, b, c } or a ´´u2´´ which I later convert to a comptime string still end up being a type for some reason.

Can you post the full snippet (or a link to your code)? This reduction works fine for me:

const Channel = @import("std").meta.FieldEnum(struct { a: u32, b: u32, c: u32 });
fn foo(comptime channel: Channel) void {
    const col = comptime @tagName(channel);
    @compileLog(col);
}
export fn entry() void {
    foo(.a);
}
zig build-obj -fno-emit-bin repro.zig
repro.zig:4:5: error: found compile log statement
    @compileLog(col);
    ^~~~~~~~~~~~~~~~
referenced by:
    entry: repro.zig:7:8
    entry: repro.zig:6:1
    4 reference(s) hidden; use '-freference-trace=6' to see all references

Compile Log Output:
@as(*const [1:0]u8, "a")

(on 0.15.2, 0.16.0 and latest master)

I can post a snippet but the full code uses some dependencies I host in a local git so sadly not the entire code.

fn @"rgb-floyd-steinberg"(
    io: std.Io,
    in: *const zigimg.Image,
    out: *zigimg.Image,
    rbuf: []i16,
    gbuf: []i16,
    bbuf: []i16,
) void {
    std.debug.assert(in.pixelFormat() == .rgb24);
    std.debug.assert(out.pixelFormat() == .rgb24);
    std.debug.assert(in.width == out.width and in.height == out.height);

    var r_future = io.async(f, .{ in, out, rbuf, .r });
    defer r_future.cancel(io);
    var g_future = io.async(f, .{ in, out, gbuf, .g });
    defer g_future.cancel(io);
    var b_future = io.async(f, .{ in, out, bbuf, .b });
    defer b_future.cancel(io);
}

const Channel = std.meta.FieldEnum(zigimg.color.Rgb24);
fn f(
    in: *const zigimg.Image,
    out: *zigimg.Image,
    buf: []i16,
    comptime channel: Channel,
) void {
    const col = @tagName(channel);
    const len = in.pixels.len();
    const t: u8 = b: {
        var sum: u32 = 0;
        for (in.pixels.rgb24) |p| sum += @field(p, col);
        break :b @intCast(sum / (3 * len));
    };
    @memset(buf, t);

    for (in.pixels.rgb24, out.pixels.rgb24, buf, 0..) |i, *o, thresh, idx| {
        const oc = &@field(o, col);
        const ic = &@field(i, col);
        oc.* = if (thresh > ic.*) 0 else 255;
        const diff = @as(i16, oc.*) - @as(i16, ic.*) + thresh - t;
        for (
            [_]usize{ idx + 1, idx + in.width - 1, idx + in.width, idx + in.width + 1 },
            [_]i16{ 7, 3, 5, 1 },
        ) |tidx, factor| {
            if (tidx >= buf.len) continue;
            buf[tidx] += @divFloor(diff * factor, 16);
        }
    }
}

This is a bug with @call and passing runtime args to it.

Regardless you would get a compile error since you can only pass runtime args through io.async/concurrent, this is because you can no longer generate a tuple with comptime fields.

3 Likes