# I've painted myself into a corner with comptime and I'm seeking advice

**URL:** <https://ziggit.dev/t/ive-painted-myself-into-a-corner-with-comptime-and-im-seeking-advice/18012>\
**Category:** Help\
**Tags:** language, comptime, build-system\
**Created:** [October 18, 2026, 12:23am UTC](https://ziggit.dev/t/ive-painted-myself-into-a-corner-with-comptime-and-im-seeking-advice/18012 "2026-10-18T00:23:27Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![grurbo](https://ziggit.dev/user_avatar/ziggit.dev/grurbo/32/10367_2.png) [@grurbo](https://ziggit.dev/u/grurbo)\
**Post date:** [October 18, 2026, 12:23am UTC](https://ziggit.dev/t/ive-painted-myself-into-a-corner-with-comptime-and-im-seeking-advice/18012/1 "2026-10-18T00:23:27Z")

</div>

I didn’t know it was possible to create a comptime program that was so large it would no longer compile. I kept raising `@setEvalBranchQuota` to fix it, but now, by passing maxInt(u32), the compiler just segfaults. I have to assume I’m either missing something obvious to work around this or I’ve been misusing comptime altogether. After investing several weeks into this tool already, I wanted to see if anyone had insight into how salvageable this project is.

I work in transportation and this is a cli/library for my job. There are a range of versions of raw/processed geodata this library has to pull from. My solution to this was to create a multi-step build system that embedded these files at comptime. When I originally wrote it I just parsed these json files and created the derived hashmaps I needed at runtime. I decided this wasn’t good enough, and I got greedy.

To get around not being able to parse json at comptime, I made a build module that turned the json into zon and handed it + a manifest off to the final program. By largely copying the StaticStringMap implementation, I made a generalized comptime map that would make these maps “instantly” available at runtime. (there’s a lot more to the system but hopefully you get the picture)

```zig
pub fn Map(
    comptime K: type, // has to be an int
    comptime V: type, // hopefully a struct, if it's an array we gotta do something else I think
) type {
    return struct {
        kvs: *const KVs = &empty_kvs,

        pub const KV = struct {
            key: K,
            value: V,
        };

        const KVs = struct {
            keys: [*]const K,
            values: [*]const V,
            len: u32,
        };

        const Self = @This();

        const empty_kvs = KVs{
            .keys = &empty_keys,
            .values = &empty_vals,
            .len = 0,
        };
        const empty_keys = [0]K{};
        const empty_vals = [0]V{};
        const empty_len_indexes = [0]u32{};

        // same deal as StaticStringMap
        // has to be a list of struct { K, V }
        pub inline fn init(comptime kvs_list: anytype) Self {
            comptime {
                var self = Self {};
                @setEvalBranchQuota(std.math.maxInt(u32));

                // this is a dogshit sort but it's comptime so you can't get mad at me
                var keys: [kvs_list.len]K = undefined;
                for(kvs_list, 0..kvs_list.len) |kv, i| {
                    const key, const val = kv;
                    _ = val;
                    keys[i] = key;
                }
                std.mem.sort(K, &keys, {}, std.sort.asc(K));
                
                var values: [kvs_list.len]V = undefined;
                for(keys, 0..keys.len) |sorted_key, i| {
                    const index = blk: {
                        for(kvs_list, 0..kvs_list.len) |kv, j| {
                            const key, const val = kv;
                            _ = val;
                            if(key == sorted_key) {
                                break :blk j;
                            }
                        }
                        unreachable;
                    };
                    const key, const val = kvs_list[index];
                    _ = key;

                    values[i] = val;
                }

                const final_keys = keys;
                const final_values = values;

                self.kvs = &.{
                    .keys = &final_keys,
                    .values = &final_values,
                    .len = @intCast(kvs_list.len),
                };

                return self;
            }
        }

        pub fn get(self: Self, key: K) ?V {
            // TODO: keys are sorted, do a binary search
            // but I just want to see if it compiles

            for(self.kvs.keys, 0..self.kvs.len) |map_key, i| {
                if(map_key == key) {
                    return self.kvs.values[i];
                }
            }

            return null;
        }
    };
}

```

This worked, but compile times ballooned to an insane degree. This should have been a warning, but I pressed on. I made it pretty far and the library is ok and functional, but now that I need to add a set of maps that was derived from 2 other maps, I’ve hit my head on the setEvalBranchQuota ceiling.

I’m sure I’m not the first person to obsessively pre-compute to this degree. Where did I go wrong

---

<div class="post-metadata">

**Author:** ![andrewrk](https://ziggit.dev/user_avatar/ziggit.dev/andrewrk/32/7011_2.png) [@andrewrk](https://ziggit.dev/u/andrewrk)\
**Post date:** [October 18, 2026, 12:38am UTC](https://ziggit.dev/t/ive-painted-myself-into-a-corner-with-comptime-and-im-seeking-advice/18012/2 "2026-10-18T00:38:34Z")

</div>

Keep an eye on this issue:

> <https://github.com/ziglang/zig/issues/4055>
>
> While investigating https://github.com/ziglang/zig/issues/3863, I ran into an un…expected problem: the performance of certain things (in my case, \`std.hash.Wyhash\` and \`std.sort.sort\`) is hugely degraded during comptime.
> 
> Here's a test file that shows the problem using \`std.sort\`:
> 
> \`\`\`zig
> const std = @import("std");
> 
> const num\_values: usize = 10000;
> const expected\_first\_element: usize = num\_values - 1;
> 
> fn sortSomething() usize {
> var buf = init: {
> var arr: \[num\_values\]usize = undefined;
> for (arr) |\*v, i| {
> v.\* = i;
> }
> break :init arr;
> };
> std.sort.sort(usize, buf\[0..\], std.sort.desc(usize));
> return buf\[0\];
> }
> 
> test "comptime" {
> @setEvalBranchQuota(100000);
> comptime std.testing.expectEqual(expected\_first\_element, sortSomething());
> }
> 
> test "runtime" {
> std.testing.expectEqual(expected\_first\_element, sortSomething());
> }
> \`\`\`
> 
> The runtime test on its own (\`--test-filter runtime\`) runs very quickly (as expected) with \`num\_values = 10000\`. For comptime, however, here's what I get with various different \`num\_values\`:
> 
> | \`num\_values\` | time spent compiling | memory usage | status |
> | --- | --- | --- | --- |
> | 10 | 1s | ? | OK |
> | 100 | 1s | ? | OK |
> | 1000 | 3s | 500mb | OK |
> | 10000 | 75s | 3gb | evaluation exceeded 100000 backwards branches |
> 
> (tested with zig \`0.5.0+33d9dda55\` on Windows)
> 
> I would assume most of this is due to things like gathering stack traces for potential compile errors and things like that, but maybe there needs to be a way to disable that for certain comptime blocks? Or maybe its something else that's causing the performance issues here?

It hasn’t been a top priority issue for some time, but at some point it will become the focus.

In the meantime, remember that anything you can do at compile-time you can also do at build-time. And at build-time, run-time is compile-time! 🤪

Silliness aside, I do think that generating code or data as a build step is the best thing to do when you run into limitations like this. You might even be able to get something out of it as a consolation prize, for instance, making it easier to inspect and visualize those intermediate artifacts.

---

<div class="post-metadata">

**Author:** ![grurbo](https://ziggit.dev/user_avatar/ziggit.dev/grurbo/32/10367_2.png) [@grurbo](https://ziggit.dev/u/grurbo)\
**Post date:** [October 18, 2026, 12:45am UTC](https://ziggit.dev/t/ive-painted-myself-into-a-corner-with-comptime-and-im-seeking-advice/18012/3 "2026-10-18T00:45:14Z")

</div>

> I do think that generating code or data as a build step is the best thing to do when you run into limitations like this

If I understand correctly, I believe that’s what I’m already doing? The json → zon conversion happens as its own build step with a separate executable. I could probably try splitting some of it into yet another step though

---

<div class="post-metadata">

**Author:** ![andrewrk](https://ziggit.dev/user_avatar/ziggit.dev/andrewrk/32/7011_2.png) [@andrewrk](https://ziggit.dev/u/andrewrk)\
**Post date:** [October 18, 2026, 12:50am UTC](https://ziggit.dev/t/ive-painted-myself-into-a-corner-with-comptime-and-im-seeking-advice/18012/4 "2026-10-18T00:50:26Z")

</div>

But then you’re processing that zon into something else at comptime right? you could do that at build-time as well.

Edit: also I’m sure you already checked this but make sure you’re not doing anything O(N^2)…

---

<div class="post-metadata">

**Author:** ![truly-not-taken](https://ziggit.dev/user_avatar/ziggit.dev/truly-not-taken/32/6205_2.png) [@truly-not-taken](https://ziggit.dev/u/truly-not-taken)\
**Post date:** [October 18, 2026, 12:51am UTC](https://ziggit.dev/t/ive-painted-myself-into-a-corner-with-comptime-and-im-seeking-advice/18012/5 "2026-10-18T00:51:33Z")

</div>

> [@grurbo](#):
>
> that embedded these files at comptime

Sounds like a job for `@embedFile()`.

You seem to use text format (json or zon) and parse it at comptime. You should convert text to binary format and just embed the binary file.

---

<div class="post-metadata">

**Author:** ![grurbo](https://ziggit.dev/user_avatar/ziggit.dev/grurbo/32/10367_2.png) [@grurbo](https://ziggit.dev/u/grurbo)\
**Post date:** [October 18, 2026, 1:01am UTC](https://ziggit.dev/t/ive-painted-myself-into-a-corner-with-comptime-and-im-seeking-advice/18012/6 "2026-10-18T01:01:00Z")

</div>

The trouble with `embedFile()` is I’m making a map that holds structs that have methods on them, not just raw text. To Andrew’s point though, I will try splitting zon → struct conversion into its own step. A bit shaky on the details but I’ll do some reading. My hope is I won’t have to turn the zon into structs, turn _those_ struct into zon, then “de-anonymize” them in the final program. Either way thank you for the insight!

---

<div class="post-metadata">

**Author:** ![truly-not-taken](https://ziggit.dev/user_avatar/ziggit.dev/truly-not-taken/32/6205_2.png) [@truly-not-taken](https://ziggit.dev/u/truly-not-taken)\
**Post date:** [October 18, 2026, 1:53am UTC](https://ziggit.dev/t/ive-painted-myself-into-a-corner-with-comptime-and-im-seeking-advice/18012/7 "2026-10-18T01:53:02Z")

</div>

> [@grurbo](#):
>
> The trouble with `embedFile()` is I’m making a map that holds structs that have methods on them, not just raw text.

You can use `std.mem.bytesAsSlice`:

```zig
const StructWithMethod = struct {
    text: [7]u8,
    integer: u32,
    float: f32,

    fn method(self: *const StructWithMethod) f64 {
        return @as(f64, self.integer) + self.float;
    }
};

test "generate" {
    const io = std.testing.io;
    const file = try std.Io.Dir.cwd().createFile(io, "binary_data", .{});
    defer file.close(io);
    const slice: []const StructWithMethod = &.{
        .{ .text = @splat('a'), .integer = 2, .float = 3.0 },
        .{ .text = @splat('b'), .integer = 5, .float = 7.0 },
    };
    try file.writeStreamingAll(io, std.mem.sliceAsBytes(slice));
}

const raw_binary = @embedFile("binary_data");
const aligned_binary: [raw_binary.len]u8 align(@alignOf(StructWithMethod)) = raw_binary.*;

test "embed" {
    const slice: []const StructWithMethod = std.mem.bytesAsSlice(StructWithMethod, &aligned_binary);
    try std.testing.expectEqual(5, slice[0].method());
    try std.testing.expectEqual(12, slice[1].method());
}

```

---

<div class="post-metadata">

**Author:** ![grurbo](https://ziggit.dev/user_avatar/ziggit.dev/grurbo/32/10367_2.png) [@grurbo](https://ziggit.dev/u/grurbo)\
**Post date:** [October 18, 2026, 1:58am UTC](https://ziggit.dev/t/ive-painted-myself-into-a-corner-with-comptime-and-im-seeking-advice/18012/8 "2026-10-18T01:58:11Z")

</div>

oh shit that is exactly what I needed, thank you

---

<div class="post-metadata">

**Author:** ![truly-not-taken](https://ziggit.dev/user_avatar/ziggit.dev/truly-not-taken/32/6205_2.png) [@truly-not-taken](https://ziggit.dev/u/truly-not-taken)\
**Post date:** [October 18, 2026, 2:07am UTC](https://ziggit.dev/t/ive-painted-myself-into-a-corner-with-comptime-and-im-seeking-advice/18012/9 "2026-10-18T02:07:30Z")

</div>

Make sure to check that the compiler did not mess up the layout of the struct. Make the struct `extern` if it did.

Can you ☑ my answer as solution instead of Andrew’s?
