Status: SSOT (language-level), with implementation status notes.
This document defines the Nyash object lifecycle model: lexical scope, ownership (strong/weak), finalization (`fini()`), and what is (and is not) guaranteed across backends.
## Terms
- **Binding**: a local variable slot (created by `local`) that points to a value.
- **Strong reference**: an owning reference that contributes to keeping the object alive.
- **Weak reference**: a non-owning reference; it does not keep the object alive and may become dead.
- **Finalization (`fini`)**: a logical end-of-life hook. It is not “physical deallocation”.
## 0) Two-layer model (resource vs memory)
Nyash separates two concerns:
- **Resource lifecycle (deterministic)**: `fini()` defines *logical* end-of-life and must be safe and explicit.
- **Heap memory reclamation (non-deterministic)**: physical memory is reclaimed by the runtime implementation (typically reference counting). Timing is not part of the language semantics.
This split lets Nyash keep “箱理論” simple:
- Programs must use `fini()` (or sugar that guarantees it) to deterministically release external resources (fd/socket/native handles).
- Programs must not rely on GC timing for correctness.
## 1) Scope model (locals)
-`local` is block-scoped: the binding exists from its declaration to the end of the lexical block (`{ ... }`).
- Leaving a block drops its bindings immediately (including inner `{}` blocks).
- Dropping a binding reduces strong ownership held by that binding. It may or may not physically deallocate the object (depends on other strong references).
This is the “variable lifetime” rule. Object lifetime is defined below.
## 2) Object lifetime (strong / weak)
### Strong ownership
- A strong reference keeps the object alive.
- When the last strong reference to an object disappears, the object becomes eligible for physical destruction by the runtime.
- In typical implementations this is immediate (reference-counted drop) for acyclic graphs, but the language does not require immediacy.
### Weak references
Weak references exist to avoid cycles and to represent back-pointers safely.
Language-level guidance:
- Locals and return values are typically strong.
- Back-pointers / caches / parent links that would create cycles should be weak.
Required property:
- A weak reference never keeps the object alive.
Observable operations (surface-level; exact API depends on the box type):
Nyash allows object graphs; strong cycles can exist unless the program avoids them.
Policy:
- Programs should use **weak** references for back-pointers / parent links to avoid strong cycles.
- If a strong cycle exists, memory reclamation is not guaranteed (it may leak). This is allowed behavior in “no cycle collector” mode.
Important: weak references themselves do not require tracing GC.
- They require a runtime liveness mechanism (e.g., an `Rc/Weak`-style control block) so that “weak_to_strong” can succeed/fail safely.
### GC modes
GC is treated as an optimization/diagnostics facility, not as a semantic requirement. In practice, this means “cycle collection / tracing”, not “basic refcount drop”.
- **GC off**: reference-counted reclamation still applies for non-cyclic ownership graphs; strong cycles may leak.
- **GC on**: the runtime may additionally reclaim unreachable cycles eventually; timing is not guaranteed.
Invariant:
- Whether GC is on or off must not change *program meaning*, except for observability related to resource/memory timing (which must not be relied upon for correctness).
Nyash has an internal “ByRef” concept (MIR `RefGet/RefSet`) used to access and mutate fields through a **borrowed reference to a storage slot**.
Intended use cases:
- Field get/set lowering with visibility checks (public/private) and delegation (from/override).
- Passing a “mutable reference” to runtime helpers or plugin calls without copying large values.
SSOT constraints:
- ByRef is **non-owning**: it does not keep the target alive and does not affect strong/weak counts.
- ByRef is **non-escaping**: it must not be stored in fields/arrays/maps, returned, captured by closures, or placed into global registries.
- ByRef is **scope-bound**: it is only valid within the dynamic extent where it was produced (typically a single statement or call lowering).
- Using ByRef on **Dead/Freed** targets is an error (UseAfterFini / dangling ByRef).
These constraints keep “箱理論” simple: ownership is strong/weak; ByRef is a temporary access mechanism only.
## 7) Diagnostics (non-normative)
Runtimes may provide diagnostics to help validate lifecycle rules (example: reporting remaining strong roots or non-finalized objects at process exit). These diagnostics are not part of language semantics and must be default-off.
## 8) Implementation status (non-normative)
This section documents current backend reality so we can detect drift as bugs.
- **Block-scoped locals** are the language model (`local` drops at `}`), but the *observable* effects depend on where the last strong reference is held.
- **WeakRef** (Phase 285A0+): VM backend fully supports `weak <expr>` and `weak_to_strong()`. LLVM harness also supports this surface as of Phase 285LLVM-1.4.
- **WASM backend** currently treats MIR `WeakNew/WeakLoad` as plain copies (weak behaves like strong). This does not satisfy the SSOT weak semantics yet (see also: `docs/guides/wasm-guide/planning/unsupported_features.md`).
- **Leak Report** (Phase 285): `NYASH_LEAK_LOG={1|2}` prints exit-time diagnostics showing global roots still held (modules, host_handles, plugin_boxes). See `docs/reference/environment-variables.md`.
- Conformance gaps (any backend differences from this document) must be treated as bugs and tracked explicitly; do not "paper over" differences by changing this SSOT without a decision.
See also:
-`docs/reference/language/variables-and-scope.md` (binding scoping and assignment resolution)
-`docs/reference/boxes-system/memory-finalization.md` (design notes; must not contradict this SSOT)
## 9) Validation recipes (non-normative)
WeakRef behavior (weak_to_strong must fail safely):