We have type unions at home

Hello everyone, I would like to show off my recent abuse of comptime:

fn ParseRecursiveError(MaybeArena: type) type {
    return switch (MaybeArena) {
        std.mem.Allocator => ParseAllocError,
        @TypeOf(null) => ParseError,
        else => comptime unreachable,
    };
}

fn parseRecursive(
    comptime command: Command,
    /// Provide comptime null if we know we won't allocate,
    /// otherwise provide std.mem.Allocator (arena suggested).
    maybe_arena: anytype,
    iter: *Iterator,
    options: ParseOptions,
) ParseRecursiveError(@TypeOf(maybe_arena))!Parsed(command) {
...
}

In this example, I can restrict the type of maybe_arena to comptime @TypeOf(null) or std.mem.Allocator. If I attempt to allocate when I don’t want to (when I pass null to maybe_alloc), I will get a compile error. So i’ve got complicated logic that hides all my allocations at comptime and if I violate that I get a compile error because maybe_alloc will be comptime-known.

I can also restrict the error set of ParseRecursive to not have error.OutOfMemory when I pass comptime null to maybe_alloc so I can delete it from my error switches and I don’t have to mark them unreachable when I know they are comptime unreachable.

So I think I have modeled something like this:

const MaybeArena = union(enum) {
    Arena: std.mem.Allocator,
    Null: @typeOf(Null),
}

Where the union tag is comptime known.

1 Like

IDK how this is an abuse of comptime, seems pretty normal.

Honestly anytime I use comptime it feels like abuse.