Confused about @tagName and comptime behavior

I’m working on a library for a project targeting Arm Cortex-M. I want to convert a enum value to an integer based on the enum tag name.

For example:

const E = enum {
    mul4,
    mul8,
    mul16,
};

// This should map:
// mul4  -> 4
// mul8  -> 8
// mul16 -> 16
fn mulVal(x: anytype) u32 { ... }

The implementation that ended up working is:

pub fn mulDivValue(mul_or_div: anytype) u32 {
    switch (mul_or_div) {
        inline else => |tag| {
            @setEvalBranchQuota(50000);

            // Comptime checks to make sure variant name is translated correctly
            const tagName = @tagName(tag);
            if (comptime !(std.mem.startsWith(u8, tagName, "div") or std.mem.startsWith(u8, tagName, "mul"))) {
                @compileError("enum variant must start with 'div' or 'mul': " ++ tagName);
            }
            const tagDigits = tagName[3..];
            inline for (tagDigits) |c| {
                if (comptime !std.ascii.isDigit(c)) @compileError("enum variant must contain only digits after prefix: " ++ tagName);
            }

            const val = comptime std.fmt.parseUnsigned(u32, tagDigits, 10) catch unreachable;
            return val;
        },
    }
}

With const val = comptime ... it works but removing comptime ends up derefencing an invalid address.

My guess is something related to @tagName but I don’t really understand how it works. I thought I wouldn’t need to force it to comptime.

Why do I need to explicitly mention comptime in val assignment?

Would be better to show the actual error message.

Why aren’t you using?:

const E = enum {
    mul4 = 4,
    mul8 = 8,
    mul16 = 16,
};

That way you can use @intFromEnum to get the value.

Or possibly even:

const E = enum {
    @"4" = 4,
    @"8" = 8,
    @"16" = 16,
};
const MulDiv = union(enum) {
    mul: E,
    div: E,

    pub fn mul(v:u8) MulDiv {
        return .{ .mul = @enumFromInt(v) };
    }
    pub fn div(v:u8) MulDiv {
        return .{ .div = @enumFromInt(v) };
    }
};

// const md:MulDiv = .mul(4);
3 Likes

Removing comptime from the integer parse shouldn’t result in a compiler error. I just tested on 0.16 and nightly master and it works fine. What version of Zig are you using?

However, you should use comptime in that statement (or better yet, put the whole extraction of the numerical value inside of a comptime block). The reason is that functions don’t run in comptime automatically, even if all of the arguments are comptime known. So parseUnsigned would be run every time the function was called.

but generally yeah this usage looks like a bit of an overuse of comptime without further context. Never underestimate the power of just writing out a few values.