What is our general opinion around passing both gpa
and arena
to many functions. I noticed that we have a solid number of tiny allocations going to the gpa (like paths and such). Generally it is better for those to got to an arena instead. On top of that, we have some memory leaks that I am fixing up and in a few case, I think it might be nicer to put those things in the arena and not need to worry about them not being freed.
My general thought is that this is best practice, but definitely a bit inconvenient. Definitely not something that needs to be done of first pass or anything. I'm just in general thinking of cleaning up some locations and also trying to explicitly name allocators as gpa
or arena
to make it clearer what is expected. Of course for agnostic functions, it would stay as allocator
.
Thoughts?
Given we make both the gpa and the arena right at the start of main, maybe we should just eventually switch to a struct that contains both. That way, all functions can just choose what makes sense for each allocation?
const Allocators = struct {
gpa: Allocator,
arena: Allocator,
}
This is kinda what zig does in their compiler, just at a per stage basis:
https://github.com/ziglang/zig/blob/87c18945c207c77b5bc84123a7ba043b848bbabb/src/Sema.zig#L43-L46
/// Alias to `zcu.gpa`.
gpa: Allocator,
/// Points to the temporary arena allocator of the Sema.
/// This arena will be cleared when the sema is destroyed.
arena: Allocator,
Though I guess they have a perf stage arena instead of a global arena here. I think for all the paths and other things we would have global arena for roc. Separately we could consider per stage arenas for roc, but I think we already have our scratch buffers which kinda fill that role.
Also, context: https://github.com/roc-lang/roc/actions/runs/18436673436/job/52531070396?pr=8285
These are some memory leaks we currently have that probably should be in an arena anyway.
I like that idea!
+1 to more arena usage
could we call it like scratch
or something?
since sometimes we use an arena allocator for gpa :smile:
In the most extreme I would see 3 categories:
gpa
only for things that grow and reallocate like all our arraylistsarena
for things that like the whole program (or at least for multiple stages) but are tiny and not worth deallocating.scratch
anything that is specific to a compiler stage but is static size or exceptionally unlikely to grow and thus fine to consider static. Will be fully cleared between each compiler stage.That said, I'm not sure it is worth having all 3. Might just be inconvenient to wrangle. I'm also not sure how much stuff we have in category 3. If it isn't a lot, it can still just go into 2 instead.
And I'm open to whatever name. I was assuming functions would have allocs: Allocators
as the first arg and then use allocs.gpa
and allocs.arena
(or whatever other name).
Do you have a sense of how much stuff we have in category 3? I know that some of the stages have scratch space, but to my understand it is dynamically resizing, so probably should be under the gpa instead of in a true scratch buffer for the specific stage.
Of note, I think zig used to have the 3 allocator system. They called them gpa
, perm_arena
, and arena
. But they remove the perm_arena
actually.
interesting! We actually have a bunch of scratch array lists that are used as typed stacks of temporary work, so maybe those are sufficient
Yeah, as long as they are amortizing the allocations, they are probably fine. Different ways to essentially do the same thing.
Like the other design with an arena would probably be to free form allocate into the arena instead of having the n different array lists for scratch.
I would say that for now these are a fine scratch solutions and I just want to add a global arena for all the random tiny allocations that we really don't care about.
sounds good to me, go for it!
Add new Allocators struct to pass gpa and arena to more functions #8285
Really only got to updating main.zig (tons of paths and things now using the arena). That said, I think it is big enough that it is worth submitting. And follow ups and expand to more files.
Last updated: Oct 18 2025 at 12:13 UTC