I just want to add that creating and using an allocator locally within a function is a perfectly valid use case.
For example I have a bunch of functions that just format some text and create null terminated strings which are then passed on to raylib draw calls that display those strings on the screen, these strings are short lived and only need to exist long enough so that raylib can convert them to glyphs and batch those up to be rendered later.
These functions aren’t even allowed to fail with an error, they just catch allocation failure with
catch "<out of memory>" thus if my fixedbufferallocator is too small it will render that special string instead. Why? Because it is easier for me to see where it fails if I can see that string instead of my program crashing and giving me a source location that is used with a bunch of different data, when the program continues running I can actually inspect for which kind of things the bug exists / some limit is hit.
And all these things being printed are debug output tools for my convenience. At the same time I don’t have to worry about my custom tools built for getting insight at run time crashing the program I am trying to interact with, if I have a bug in my tool it is easy to see and fix, or if the fixedbuffer needs to be bigger because I want to display more stuff I can increase the size. (I also reset the FixedBufferAllocator all the time after I used the temporary strings, so that the next one can have the maximum size)
I would argue that writing tests is the way to make sure the allocators are used properly, one tool you can use is checkAllAllocationFailures and I guess you could extend that to libraries by saying those should have tests as well.
Having general tools that help identify problems with libraries (beyond just looking at their source code) could be helpful, but I think general memory profiling applications could already be a big help in for example taking notice of a library that internally uses its own allocator reserving lots of memory.
I think the only way you could end up with a language that prevents you from using allocators in certain ways is if you would enforce some system of restrictions how allocators can be used, it seems like that would quickly result in situations where legitimate uses aren’t allowed anymore, just because the system isn’t able to proof that they are ok.
Thinking a bit more about it, I think something that could potentially make sense for zig, still feel ziggy and be helpful, without preventing valid programs, would be some kind of language feature to essentially be able to express that some subset of the program is somehow isolated from the rest of the program and can’t escape that box, I guess you could say sandboxing as a language feature. But I don’t know how easy or difficult it would be to implement that, I think so far it seems that wasm is being planned on being the solution for sandboxing needs, but it could be interesting to have a sandboxing feature that can be used without needing to know the underlying method how that is achieved. And with sandboxing you often want some kind of limits on cpu and memory, from my standpoint that would be the more appropriate way to get safe memory limits, rather than micromanaging how you can use allocators.
(But I am also not sure whether we really need a sandboxing feature as a language feature, haven’t thought that much about it, maybe it is actually better if you are forced to think about what your sandboxing needs are and in which different ways those could be implemented, do you need arbitrary programs, or just a subset of some simple language that can be proven to be safe and thus be compiled to save program, do you want to use wasm, write an interpreter for some custom lang, compile some subset to a plugin/jit, use docker etc… The more I think about it, I feel like it would be great to have different kinds of libraries that implement sandboxing for zig programs in different ways and then you can choose which one you want, even better would be if they could have similar apis. In the end I am just left with the question what is your actual goal and is whatever you are doing worth it to achieve that? I think the answer to that is heavily dependent on the needs of the project.)
That said if you want to go the managing allocators route, you probably still could put a lot of tracing code in the root allocator, or maybe even hack the compiler to also add some kind of monitoring for all fixed size buffers above some arbitrary size (to find buffers that may be used with fixedbufferallocators or equivalent things). But I think all of that would probably be code that adds project specific monitoring that is more heuristic based like @IntegratedQuantum said.