arena allocation with staging ----------------------------- ref Node n = staging_alloc(ctx.arena) check n.header = parse_header(ctx), # these will allocate inside the staging area n.body = parse_body(ctx) { staging_free(n) return none } return staging_commit(n) # otherwise we will leak staging areas! return n # ^ can we move the data? # yes, if: # - we do not use arena-internal pointers (unlikely to work) # - we use relative pointers for (and only for) arena-internal pointers # - we use some kind of meta data to track and update arena-internal pointers # - all possibly allocated data in the arena has public structure definitions, # and code is generated after the staging_commit call to update those This would require some kind of new "own" keyword to be safe.... # the default "refstate" is memory, which is built in (and perhaps should not even have an identifier) refstate staging; func staging_alloc(rwref Arena, size = sizeof T) returns ownref[staging] T func staging_free(ownref[staging] T) func staging_commit(ownref[staging] T) return ownref T How to enforce that no refs point to inside the staged arena after a free? Perhaps add a new function for tracking this? func staging_track_incoming(rwref StagedSegment segment, rwref addr? T ref_to_arena) # ... but how to remove the ref safely? can we just set it to null? func staging_track_incoming(rwref StagedSegment segment, rwref addr? T ref_to_arena, addr? orig_addr) # ... better but still cannot do things like removing tree/list entries etc. We need to handle these two cases: ownref T a = ...; a.x = b.y # 1. Outgoing pointer c.z = a.b # 2. (a.b is owned by a) Incoming pointer alternative approach -------------------- An alternative approach is to take ownership of the arena (so no more allocations can happen), save the current arena free pointer, and then simply restore the free pointer value. With this approach, the arena works a like a stack with regards to staging: +--------+-----------------------+-----------------+----------+----------+ | Header | Arena data, committed | Staged, level 1 | Staged 2 | unused | +--------+-----------------------+-----------------+----------+----------+ ^ \ free pointer Staged "segments" can only be free'd as a whole (because the free pointer is changed). If the arena is splitted into multiple blocks, we need to take that into account as well (i.e. the staged segment may span multiple blocks). When freeing a segment that spans multiple blocks, all but the last blocks become unused and can be free'd. With this approach, we need to be able to allow outgoing refs from the staged segment to earlier segments of the arena, but deny incoming refs from earlier segments to the staged arena (or track them somehow, but in that case then modification/copying of the ref must be forbidden).