Maybe the compiler could offer me a simple way to fix the declaration somehow. But being explicit and transparent here feels important to me; if I wanted to second-guess the compiler and meditate over disassembly, I could pick C++.
For every allocation, also store on the heap a "reference object", that keeps track of this new reference.
struct reference_t {
// the ptr returned by malloc
void* ptr;
// the stack/heap location that ptr was written to.
// i.e the reference location.
void* referenceAddr;
}
Reference track: every time ptr is copied or overwritten, create and delete reference objects as needed.If ptr is freed, visit every reference and call *referenceAddr = NULL, turning all of these references into NULL pointers.
All in all, a mark-and-sweep will likely be faster (in throughput at least) let alone a good GC.
The programmer would still decide when to free the object, not some automatic system. Manual memory management, with Ref Counting just to add some additional Use After Free checks.
At least in simple cases, this means that the memory for escaped variables could be allocated all at once at the beginning of the program not too differently to how the program allocates memory for the stack.
I’d hazard a guess that the implementation will rely on use-after-free faulting, meaning that the use of any escaped variable will fault rather than corrupting the stack.
Zig has future plans to require recursive functions to declare their maximum stack memory usage up-front, so that will provide the rest.
What If we combined the 'non-repeating' malloc idea with 128-bit uuids?
malloc would just return a 128-bit uuid, and to get to the data ptr you'd need to consult a hash table.
dataPtrArr[hash(uuid)].dataPtr = dataPtr
We'd check if it's been freed by checking: dataPtrArr[hash(uuid)].uuid == uuidThat solution is basically how platforms like Psion or Symbian used handles due to memory constraints.
You also don't need to worry about ref cycles or GC pauses.
https://floooh.github.io/2018/06/17/handles-vs-pointers.html
(but for this an "auto-decaying pointer" would be nice which cannot be stored outside the stack and cannot be "carried across" function calls.