Memory management / Runtime and backend memory lowering

Different targets and profiles may receive different allocation and GC representations. The compiler-owned memory planner selects them per physical variant, and no backend chooses different language semantics.

Runtime and backend memory lowering

Design contract

Different targets and profiles may receive different allocation, region, counted, handle and ownership representations. The compiler-owned memory planner selects them per physical variant, backend lowering encodes the result, and no target may receive different language semantics.

Every backend must preserve shared read-only access, exclusive mutation, explicit copy behaviour, affine transfer safety, lifetime-topology legality, reactive liveness and identical observable results.

Collector-free release guarantee. Backends declare whether they support collector-free release lowering. A backend that advertises full memory control must lower every accepted topology in a release build without a tracing or reachability collector, and must not silently fall back to one. There is no source-visible or project-visible no-GC mode: this is a backend capability, not a language mode.

GC is a permitted runtime representation for a statically legal lifetime topology on debug profiles and GC-native backends. It is not a mechanism for accepting invalid retained edges, lateral lifetime promotion, unowned escapes or cross-region cycles.

Main backend paths

Backend-neutral semantics

Planning order

Physical memory strategy is chosen after target assignment, not before it.

validated HIR
    -> borrow and last-use analysis
    -> local allocation-family and retained-edge constraints
    -> exported lifetime and retention summaries
    -> project/package summary instantiation
    -> complete backend-neutral lifetime-topology validation
    -> non-lexical interval, frontier and epoch completion
    -> backend-neutral memory requirements
    -> target-affinity analysis and partition
    -> target-contract validation
    -> per-physical-variant family/layout refinement
    -> revalidate affected refined family-edge facts
    -> target/profile-aware compiler-owned memory planning
    -> ValidatedMemoryPlan
    -> backend lowering
    -> collector-free artefact verification where required

check runs through creation and validation of the ValidatedMemoryPlan. It stops before backend lowering and output emission.

Final physical ownership decisions and count transitions come only from the ValidatedMemoryPlan. Lowering starts after that plan has been validated.

ValidatedMemoryPlan contract

A ValidatedMemoryPlan belongs to one target/profile physical-variant scope. It is not:

Its conceptual contents are:

ValidatedMemoryPlan
    physical variant identity and capability inputs
    post-refinement allocation-family graph
    one selected physical family plan for every reachable family
    region plans and complete exits
    declared-region plans and complete exits
    planned affine-responsibility transitions
    planned retained-edge obligation transitions
    hidden-destination plans
    cleanup plans
    destruction plans
    REC layout and counter decisions
    physical coalescing decisions
    normalised memory-plan identity inputs

The accepted family strategy outcomes are:

Stack
Inline
Affine
InferredRegion
DeclaredRegion
REC
HostGC

Exact Rust enum decomposition remains open. Stack or inline placement may be separate from heap cleanup strategy. No accepted outcome may be omitted from the conceptual contract.

Before publication, plan validation proves:

  1. Every reachable post-refinement family has exactly one selected physical plan.
  2. Every plan family belongs to this physical-variant scope.
  3. Every retained-edge transition refers to valid direct post-refinement families.
  4. Every family preserves its one validated semantic lifetime owner.
  5. Every affine obligation reaches a planned discharge or safe transfer on every relevant path.
  6. Every inferred region has complete exits.
  7. Every declared region has complete bulk exits.
  8. Declared-region-owned families are never individually releasable and never REC-selected.
  9. Every REC family is acyclic and has a valid counter layout.
  10. Every REC family keeps its validated fallback region.
  11. Every destruction plan processes outgoing obligations exactly once.
  12. Every projection representation preserves or recovers the allocation-family base.
  13. Every hidden destination satisfies its validated destination and retained-edge constraints.
  14. Every physical coalescing decision preserves the original semantic topology.
  15. HostGC is rejected for a capable full-control release variant.
  16. Normalised plan identity is deterministic for identical inputs.

Physical coalescing occurs before final plan validation and fingerprinting. It may retain storage slightly longer, but it never widens semantic topology, changes outlives, changes source legality or changes diagnostics.

Physical memory strategies

The memory planner selects one strategy per validated allocation family. Backends realise the plan; they do not choose semantics.

Arenas, handles, slabs, pools, grouped drop lists and hybrid representations are permitted for valid topology. Source-visible RC, weak references, finalizers and hidden unrestricted dynamic sharing are not.

Semantic intervals and physical coalescing

Semantic lifetime analysis chooses the narrowest proven useful interval for each allocation family. Physical memory planning may choose a slightly wider storage interval to reduce allocation and cleanup overhead, but that choice stays below the semantic proof.

Physical coalescing never widens semantic topology. It never changes an outlives relationship, makes an illegal retained edge legal or changes diagnostics. A coalesced arena or grouped drop may keep storage available longer than the semantic interval while preserving the same validated lifetime facts.

Garbage-collected representation

Where garbage collection is used, it represents an already-legal topology:

The representation can use host garbage collection, Wasm GC or a runtime tracing collector. Exact current target support belongs in the progress matrix.

Profiles and backend capability

Profiles differ only in analysis effort and physical representation, never in source legality.

A project builder may verify after reachability and memory planning that a physical artefact contains no tracing runtime. That is an artefact property reported by the build, not a source mode or a project-wide language contract.

Build-owned target and lifecycle planning

The build system owns reachability, target partition, page/request/mount/frame roots and page runtime topology. Backends consume explicit plans rather than selecting them.

See docs/build-system-design.md for project and link orchestration.

Moth-linked page memory and imported components

Each page may own one runtime and memory shared by its linked Moth Wasm variants. Imported WIT components are separate runtimes with component-private memory and communicate only through the closed value boundary. This is build architecture, not source memory semantics. The one-page/one-memory rule applies to Moth-linked Wasm variants, not arbitrary imported components. Imported components are not members of Moth page memory or the Moth lifetime graph and do not need to declare a GC strategy to Moth.

WIT value-only V1 profile

Moth imports general external Wasm libraries as WIT-described Wasm Components. The V1 component boundary is value-only. Every supported argument is lowered from a shared read of a Moth value into an independent component value, and every result is lifted into an independent Moth result graph. No Moth alias, lifetime owner, ownership state, or destruction responsibility crosses the component boundary.

The component may use any private allocation or garbage-collection strategy. That strategy is not part of the Moth binding contract and does not require a GC declaration.

Fixed facts

| Fact | WIT value-only V1 | |---|---| | Parameter source access | Shared read | | Boundary crossing | Independent semantic value | | Retained Moth reference | Never | | Result provenance | Independent result graph | | Result aliases an argument | Never | | Source call consumes argument | Never | | Destruction responsibility crosses | Never | | External lifetime owner | Component-private | | Cross-runtime allocation cycle | Impossible by contract |

Supported V1 WIT value families

Rejected V1 WIT surfaces

Unsupported interfaces must receive structured import diagnostics identifying:

Required importer metadata

The importer requires:

The boundary classification generates the fixed memory facts. The importer must not infer them from a generic Fresh result marker, and binding authors must not restate them per function. No uses_gc field is required.

Restricted host-binding profile

Existing Core, Builder, JavaScript-provider and curated platform bindings use a restricted host-binding profile.

Restricted host-binding V1 permits mutable access only to opaque foreign handles. Ordinary Moth values cross by value as non-retained inputs. A host operation that conceptually changes ordinary Moth data returns a fresh or independent result instead of mutating Moth storage through the boundary.

Deferred resource-capable profiles

A future resource-capable profile requires a separate complete design for identity, transfer, teardown, callbacks, async suspension, reentrancy, cycles and failures. It is not part of V1.

JavaScript lowering

Wasm lowering

Current implementation debt

src/backends/wasm/hir_to_lir/ownership.rs is the ownership projection seam, and Wasm LIR includes conditional DropIfOwned plus reserved retained-edge transition support. The current direct path from borrow-checker advisory drop sites to DropIfOwned remains scaffolding: final lowering must consume a validated memory plan and support affine, inferred-region, declared-region and REC strategies. Treat the present implementation as migration debt, not as the accepted architecture.

Runtime ownership metadata

Planned discharge and retained-edge transitions

Fact consumption

Borrow facts and validated lifetime topology are validation context and assertion material only. They do not supply final physical ownership, cleanup or count decisions.

Backends may consume:

Advisory drop sites never form final backend input. The current Wasm path that consumes them remains explicitly labelled implementation debt above.

Backends must not:

Reactive and mounted-fragment liveness

Live reactive subscriptions and mounted fragments can keep state observable. Ownership-aware lowering must not free a source or template instance while its builder lifecycle or reactive subscription keeps it observable. A GC representation may realise those already-proven lifecycle and observability facts with reachability, but reachability does not define the semantic lifetime. Finer invalidation granularity may improve performance but not lifetime semantics. Runtime fragment and reactive mount behaviour remain separate concerns.

Backend specialisation

Allowed optimisations include:

Transition fusion stays plan-owned and is not a backend optimisation. Backends may only encode the transitions after plan validation.

Requirements:

Imprecision requirements

When a backend can't place precise individual cleanup for a value, the allocation remains under its inferred or declared region owner and is reclaimed at that owner's validated exit.

Conservative retention is valid. Semantic divergence isn't. In a capable full-control release build, imprecision must not reintroduce a tracing collector, and unknown external retention is never a collector fallback. A missing physical strategy after successful topology validation is CompilerError.

What backends must not do

Common mistakes

Related reading