prometeu-runtime/files/Borrow Mutate - Compiler GC.md
2026-03-24 13:40:35 +00:00

4.7 KiB

Mini Spec — Borrow/Mutate Views on a GC Heap (Prometeu)

This document defines an optional “Rust-flavored” access discipline for heap objects without using RC/HIP. It is compatible with a GC-based heap, first-class functions (closures), and deterministic coroutines.

Goals

Preserve the feel of borrow (shared read) and mutate (exclusive write).

Prevent common aliasing bugs in user code.

Keep the runtime simple and GC-friendly.

Avoid restrictions that would block closures/coroutines.

Non-goals

Not a full Rust borrow checker.

Not a memory management mechanism (GC owns lifetimes).

Not a general concurrency/locking system.

  1. Concepts 1.1 Heap handle

A heap object is referenced by a handle. Handles can be stored, copied, captured, and returned freely (GC-managed).

1.2 Views

A view is a temporary capability derived from a handle:

BorrowView: shared read-only view

MutView: exclusive mutable view

Views are stack-only and lexically scoped.

  1. Core Rules (Language Semantics) 2.1 Creating views

Views can be created only by dedicated constructs:

borrow(h) { ... } produces a BorrowView for the duration of the block

mutate(h) { ... } produces a MutView for the duration of the block

2.2 View locality

Views must never escape their lexical scope:

A view cannot be:

stored into heap objects

captured by closures

returned from functions

yielded across coroutine suspension points

stored into globals

(Think: “views are ephemeral; handles are persistent.”)

2.3 Access permissions

Within a view scope:

BorrowView permits reads only

MutView permits reads and writes

Outside a view scope:

direct field access is not permitted; you must open a view scope again.

2.4 Exclusivity

At any program point, for a given handle h:

Multiple BorrowView(h) may coexist.

Exactly one MutView(h) may exist, and it excludes all borrows.

This is enforced at compile time (required) and optionally at runtime in debug mode (see §5).

2.5 Re-entrancy

Within a mutate(h) scope:

nested borrow(h) is allowed (it is trivially compatible)

nested mutate(h) is disallowed

Within a borrow(h) scope:

nested mutate(h) is disallowed

  1. Interaction with Closures and Coroutines 3.1 Closures

Closures may capture handles freely.

Closures must not capture views.

If code inside borrow/mutate creates a closure, it may capture the handle, but not the view.

3.2 Coroutines

Coroutines may store handles in their stacks/locals.

Views must not live across suspension:

A coroutine cannot yield while a view is active.

A coroutine cannot sleep while a view is active.

This rule guarantees that view lifetimes remain local and deterministic.

  1. Compiler Requirements (Static Enforcement)

The compiler enforces this feature by tracking view lifetimes as lexical regions.

Minimum checks:

A view cannot be assigned to a variable with heap lifetime.

A view cannot appear in a closure capture set.

A view cannot be returned.

A view cannot be live at FRAME_SYNC, YIELD, SLEEP, or any safe-point boundary defined by the language.

Exclusivity rules per handle region (borrow vs mutate overlaps) must hold.

If a rule is violated: compile error (fatal).

  1. Optional Runtime Checking (Debug Mode)

Runtime checking is optional and intended for debugging or untrusted bytecode scenarios.

5.1 Object header borrow-state

Each heap object may maintain:

readers: u16

writer: bool

Runtime actions:

entering borrow increments readers if no writer

leaving borrow decrements readers

entering mutate requires readers==0 and writer==false, then sets writer=true

leaving mutate sets writer=false

Violations trap with a specific diagnostic.

5.2 Build profiles

Release builds may omit this entirely (zero overhead).

Debug builds may enable it.

  1. Lowering to Bytecode (Suggested)

This feature does not require dedicated opcodes in the minimal design.

Recommended lowering strategy:

borrow/mutate are compile-time constructs.

Field reads/writes lower to existing heap access opcodes (FIELD_GET/FIELD_SET or equivalent), but only permitted while a view is considered “active” by the compiler.

In debug-runtime-check mode, the compiler may insert explicit enter/exit markers (or syscalls) to enable runtime enforcement.

  1. Rationale

GC removes the need for RC/HIP, but aliasing mistakes still exist. Views provide a clean and didactic discipline:

Handles: long-lived, capturable, GC-managed.

Views: short-lived, local, safe access windows.

This keeps the VM simple while enabling expressive language features (closures, coroutines) without lifetime headaches.

  1. Summary

Borrow/Mutate are access disciplines, not memory management.

Views are stack-only and non-escapable.

Closures and coroutines freely use handles, but never views.

Enforcement is compile-time, with optional runtime checks in debug mode.