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.
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.
src/backends/js/src/backends/wasm/src/backends/wasm/hir_to_lir/ownership.rssrc/backends/wasm/lir/src/backends/wasm/runtime/ValidatedMemoryPlan is the single final physical authority for ownership decisions and count transitions. Backend consumes it rather than rediscovering or selecting strategy.copy, alias or ~ semantics.CompilerError.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 requiredcheck 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 is scoped to one target/profile physical variant, never to the whole project.CompilerError.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 inputsThe accepted family strategy outcomes are:
Stack
Inline
Affine
InferredRegion
DeclaredRegion
REC
HostGCExact 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:
HostGC is rejected for a capable full-control release variant.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.
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 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.
Where garbage collection is used, it represents an already-legal topology:
discharge_if_owned may be a no-op.The representation can use host garbage collection, Wasm GC or a runtime tracing collector. Exact current target support belongs in the progress matrix.
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.
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.
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.
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.
| 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 |
Unsupported interfaces must receive structured import diagnostics identifying:
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.
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.
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.
ValidatedMemoryPlan supplies each family's planned persistent-edge obligation transitions, including created and removed edges, affine-root reclassification and transition fusion. Wasm encodes those transitions at their planned semantic commit points.discharge_if_owned satisfies planned affine cleanup obligations. It destroys a family only where the plan says individual destruction is legal and no other obligation remains.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.
transfer moves affine cleanup responsibility to another path.discharge satisfies one affine cleanup obligation.destroy physically destroys one allocation family.bulk reclaim ends a region or declared region.discharge_if_owned is the only conceptual permanent name for the conditional operation.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:
ValidatedMemoryPlan as the single final physical authority, including allocation-family layout, selected representation, cleanup plans, destruction plans, REC decisions and physical coalescing decisionsAdvisory drop sites never form final backend input. The current Wasm path that consumes them remains explicitly labelled implementation debt above.
Backends must not:
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.
Allowed optimisations include:
Transition fusion stays plan-owned and is not a backend optimisation. Backends may only encode the transitions after plan validation.
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.
~ as an unconditional move.