Hi! This is my idea about the feature I want to add into my own language Hexa*
and especially Zig**
(because it fits Zig very well!).
* not promoting Hexa here, really want Zig to fight Rust "monopoly" on safe memory management
** very unlikely that I will implement it myself, hopefully someone will be yakbaited
I was evaluating usage of Zig for my projects and never liked the absence of any assisted memory managemnt
(defer
is not really a solution to complex memory issues but it doesn't matter on the subject)
(and Rust is too annoying to deal with).
Okay that's my short story. While coding in C, I was always amased how it lacks such a basic things like a
"pointer to a single element" (AFAIK C has only "indexable" pointers and people tend to use only them).
But while doing osdev, out of a sudden, realized that it also laks... stack pointers! Which would be very useful
for any low and hi-level programming in general. I mean, with stack allocation you don't have to deal with
memory management at all, things generally "just work" and it is very convenient to use (to pass parameters to
function as structures with TypeName { a, b, ... }
etc). Most of the time we don't need non-stack pointers (in Zig).
This is very easy to validate with a most common Zig pattern: allocate, use and just defer free
it, all in a single function scope.
So here is my idea.
Note that I'm not into vague static analisis, thus ideas like autimatic inference seems to be... well at best slowing compilation times and confusing at worst.
Introduce a stack pointers
in addition to normal pointers, for example *stack T
and operator of taking a pointer &stack value
(actual syntax is up to your imagination).
Add a simple rule: you can assign normal pointer to a stack pointer, but not vise versa. The idea of not allowing stack pointers to be assigned to normal pointers makes sense, as it prevents stack objects from being freed manually and ensures that they will be automatically freed when they go out of scope. Yeah let's assume for now that this is a whole feature (some improvements would be described later).
So, say we have an object that we allocated manually and we definitely don't want anyone to deallocate it:
(I will use C-like syntax here because I'm more familiar with it)
void free(void* ptr); // Note this is a normal pointer
void demo(void* stack ptr) {
// free(ptr); Error! Impossible to call this way!
demo(ptr); // Can be passed to other functions that take *stack argument
}
{
void* ptr = malloc();
void* stack ptr_on_stack = ptr; // Normal pointers convertible to stack ones for convenience,
// and because they are obviusly logically compatible
defer free(ptr); // Called on exit, pretty common pattern for Zig
demo(ptr); // Ok
demo(ptr_on_stack); // Ok
struct Demo example = {.value = 10}; // Allocate on stack
demo(&example); // Safe to call!
// We are sure nobody would call `free` on it or save it into dictionary/linked list/etc
// & here is just normal pointer but it is compatible to call this way
demo(&stack example); // Explicit stack pointer
struct Demo *stack another = &example; // Safe to take multiple on-stack references
}
Well to me this design looks pretty natural and sadly no C nor Zig supports such a simple feature.
Okay what about storing of the pointer somewhere? Well seem easy to support too!
We should assume that any structure taking a stack pointer should be possible to be referred to
only with a stack
pointers.
Secondly, it is not allowed to store stack pointers
to fields of structures,
whose (structures) are declared before the one we are pointing too. Sorry for my English.
This restriction would prevent stack pointers from referencing objects that have already gone out of scope, which would further enhance the safety of the language's memory management.
Note that this concept is very easy to implement in the compiler, because the only thing it checks is position in the code (aka something like
line<=pointee_line && column<=pointee_column
). A simple and efficient approach that could be easily implemented in the compiler.
struct Demo { // Inferred to allow only stack-only pointers to self
struct Another *stack ptr;
};
{
/* line 1 */ auto another = (Another) {...}; // Allocated on stack
/* line 2 */ auto demo = (Demo) { ptr = another }; // Allocated on stack, cannot be allocated on the heap
/* line 3 */ auto another_after_demo = (Another) {...}; // Allocated on stack
demo.ptr = another_after_demo; // Error! another_after_demo is defined on line 3, while demo is on line 2
// i.e. demo outlives another_after_demo
// You may imagine more situations by yourself
}
I have more ideas but they a more Rust-like and not C-like and not feasible for Zig aestetics, so that's it! Likely would add an additional layer of safety and control over memory management in the language.
Probably forghot something (this idea came to me about half a year ago or so) and will update this gist if required.
Please share what you think and share to other people in the Zig community! Thanks for hangin' there! :P