Memory management / Access and aliasing
Moth is reference-semantic by default, copy-explicit and move-inferred. It omits explicit reference types and lifetime syntax, not references themselves.
Existing values are shared by default. Copies are explicit. ~ requests exclusive access to a mutable place.
Moth is reference-semantic by default, copy-explicit and move-inferred. It omits explicit reference types and lifetime syntax, not references themselves.
Existing values use shared, read-only access by default. Independent copies require explicit copy. Mutation requires explicit exclusive access to an existing mutable place.
Reference relationships are real source semantics. Moth does not expose reference type constructors or lifetime syntax.
These are source-semantic rules. Backends may use different scalar, handle, pointer or GC representations as long as they preserve the same observable behaviour.
src/compiler_frontend/value_mode.rssrc/compiler_frontend/ast/expressions/src/compiler_frontend/ast/statements/src/compiler_frontend/ast/field_access/src/compiler_frontend/hir/This is a navigation map, not a status declaration.
Term | Definition |
|---|---|
Value | A Moth language value. Not necessarily heap allocated. |
Place | Existing storage that can be read or mutated. |
Shared access / shared alias | Read-only source-semantic access to existing storage. |
Mutable alias / exclusive alias | Write-through mutation-capable access to existing storage. |
Retained reference | A stored reference that survives the immediate operation. |
Independent copy | A new value graph created by explicit |
Allocation family | The containing allocation plus projections that remain rooted in it. |
Alias binding | A binding that observes existing storage rather than creating an independent copy. |
Exclusive access | Mutation-capable access to one existing place. |
Move / inferred transfer | Compiler-inferred optional ownership transfer at a proven final-use point. |
Fresh value | A newly produced literal, template, constructor result or computed aggregate. |
Reads of existing values are shared. Ordinary immutable arguments are shared. Multiple shared reads may coexist. Shared access is read-only, doesn't imply ownership and can prevent mutation while live. Scalar values may be represented directly by a backend.
Shared access is a source-semantic relationship. It does not require every scalar to be heap allocated or represented by a pointer.
first = load_items()
second = first
inspect(first)
inspect(second)
second doesn't imply a clone. It's a shared read-only alias of the same source value.
For x = y:
copy y creates independenceShared and exclusive aliases use non-lexical, control-flow-sensitive activity.
items ~= {"a", "b"}
shared = items
inspect(shared)
change(~items) -- valid: shared has no later potential use
items ~= {"a", "b"}
shared = items
change(~items) -- invalid: shared is used again below
inspect(shared)
Lexical scope does not define alias activity. See Lifetime regions and escape validation when a retained alias escapes its original binding or region.
alias = source
An alias binding normally observes the same source-semantic storage or root. It doesn't create an independent value. It can participate in mutation-conflict analysis and optional transfer reasoning. An ownership-aware backend may optimise an alias into storage transfer when no later source use exists, but it must still behave as though source aliasing rules were respected.
Borrow-checker slot and alias states are analysis concepts, not source-visible runtime types.
alias ~= source
alias ~= source, or an equivalent typed mutable declaration from an existing place, creates an exclusive mutable alias.
A mutable declaration initialized from a fresh value creates an independent mutable slot:
count ~= 0
count = 1
~ on the declaration marks the binding as mutation-capable. Reassignment still uses =. Mutable binding syntax is different from call-site exclusive access. Mutability is an access property, not separate semantic type identity.
snapshot = copy source
copy place performs a semantic deep copy of the complete copyable runtime value graph reachable from the place.
Expressions can construct new values from shared inputs: constructing a result doesn't implicitly copy the inputs. Template interpolation reads inputs and produces a new output string.
-- conceptual: non-copyable external handle inside a graph
-- copy payload -- invalid when payload contains a non-copyable resource
update(~items)
~items.push(value) catch:
assert(false, "unexpected push failure")
;
The source form is ~place. The place must already exist, and the underlying binding must permit mutation. Exactly one exclusive access may be active at a time. Shared access conflicts with active exclusive mutation where their roots overlap. ~ isn't an explicit move. The compiler may later realise an eligible operation as ownership transfer.
update({"fresh"})
update(Vector2(x = 1.0, y = 2.0))
update([: generated])
These forms are valid for ordinary mutable parameters. Source ~ on a fresh value isn't:
update(~{"fresh"}) -- invalid
update(~Vector2(x = 1.0, y = 2.0)) -- invalid
Fresh values don't need source ~. A mutable parameter may accept them. HIR materialises a hidden local when borrow analysis needs a place. The hidden local isn't source-visible lifetime syntax. There is no special temporary-reference node category after materialisation.
Fresh-rvalue materialisation applies only to ordinary mutable parameters. It does not make temporaries valid mutable receivers.
~.~place.~value.method(...).value.method(...).Existing values stored in aggregates retain shared reference semantics by default. copy creates independent storage. At a proven final use, the compiler may realise the same source operation as an ownership transfer without changing source meaning.
This applies to structs, choices, collections, maps, tuples, templates and other aggregates.
copy is required when stored children need independent lifetime or independent mutable graphs.get returns a shared alias to stored data. The same map can't be mutated while that shared result remains live.remove removes an entry and returns the removed value under the normal lifetime and ownership rules; it does not define a general source-level consuming argument mode.set doesn't silently duplicate keys or values outside ordinary copy and transfer rules.Topology questions about retained edges, escapes and region owners belong to Lifetime regions and escape validation.
Reference relationships still exist as source semantics. Moth has no source-level:
& or &mut reference type constructorsThese are permanent design boundaries, not merely unimplemented features.
One visible name maps to one binding. This keeps alias roots, future-use reasoning and diagnostics easier to reason about. The full language no-shadowing rule remains in docs/language-overview.md.
x = y as an implicit clone.~ as a move marker.~T as a distinct semantic type.~ on a fresh value.copy may preserve mutable sharing with the source graph.group / into placement and destination lifetimes