diff --git a/TODO.org b/TODO.org index 25ab769a..95a4021a 100644 --- a/TODO.org +++ b/TODO.org @@ -1966,9 +1966,13 @@ CLOSED: [2026-09-25] The five predicates are the checker's five, with =integer?= in place of =copyable?=, and the section no longer names =(Handle $t)= or =pool-new=. -** TODO plan.org's Data model section still describes move-only containers -"Owning containers move rather than copy on assignment" and "one =Vec= field -makes it move-only" predate the repeal, under which everything copies. +** DONE plan.org's Data model section still describes move-only containers +CLOSED: [2026-09-25] +The Data model section says assignment copies a container's header and the +copies alias one buffer. The memory tiers and the classes section name the pool +and generational handles as a library over a =Vec=, and the classes gate that +named =Handle= is replaced by what classes are as built. The milestone record +of what was frozen is left as history. ** DONE plan.org cites the wrong mechanism for jank's relinking bug CLOSED: [2026-09-25] diff --git a/plan.org b/plan.org index 1498b8da..e8b45efd 100644 --- a/plan.org +++ b/plan.org @@ -5,7 +5,7 @@ Two documents are normative and are settled ahead of implementation. Anything in this plan that contradicts them is out of date. - [[file:spec-memory.md][spec-memory.md]] — ownership, the four container types, - copies and moves, assignable places, generics without type classes, function + copies, assignable places, generics without type classes, function values. - [[file:spec-conditions.md][spec-conditions.md]] — the six hard cases of conditions/restarts: what ~signal~ returns, no-handler behaviour, restart @@ -56,14 +56,15 @@ Allocators all the way down; ~malloc~ hidden behind them. | Tier | Strategy | Cost | |-----------+---------------------------------+----------------| | Frame | arena, bulk reset each frame | free | -| Entities | pool + generational handles | free | +| Entities | a pool over a ~Vec~, in a library | free | | Subsystem | region, freed wholesale | free | | Dev/REPL | leaks by design, reset on reload| dev only | - Allocator is part of the calling convention, so a refcounted allocator can be added later without a language change. -- Generational handles instead of pointers for cross-references: a stale - reference is detectable, not undefined behaviour. +- Generational handles instead of pointers for cross-references, written as a + library over a ~Vec~ rather than provided by the language: a stale reference + is detectable, not undefined behaviour. - Symbols and code live in a permanent arena that only grows. ** Why no persistent collections @@ -93,8 +94,8 @@ world. |-------------+-----------------+------------------+------| | ~[n T]~ | inline, n items | copies | no | | ~[T]~ | ptr+len | copies the view | no | - | ~(Vec T)~ | ptr+len+cap | *moves* | yes | - | ~(Map K V)~ | open addressing | *moves* | yes | + | ~(Vec T)~ | ptr+len+cap | copies the header | yes | + | ~(Map K V)~ | open addressing | copies the header | yes | ~Vec~ and ~Map~ are monomorphic on element type and record their allocator. Not a Lua-style array/hash hybrid — that is what makes Lua's layout and performance unpredictable. @@ -105,17 +106,15 @@ world. returns ~(Option V)~; ~put~ is the ~()~-returning upsert. See spec-memory.md for the deferred move-aware operations. - Operations: ~get~, ~put~, ~remove~, ~push~, ~pop~, ~at~, ~length~, ~update~. - Copying is explicit: ~(clone m)~, and owning containers move rather than copy on - assignment. No ~!~ convention — nothing is immutable, so it + An independent copy is explicit: ~(clone m)~. Assignment copies a container's + header, and the two headers alias one buffer. No ~!~ convention — nothing is immutable, so it would carry no information. No ~assoc~; it only existed as the copy-returning form. - ~const~ qualifier on references and slices: compile-time contract that a callee will not mutate. Zero runtime cost. -- Value structs copy on assignment — but only *value* structs. Ownership is - structural: a struct is a value type iff every field is, so one ~Vec~ field - makes it move-only. This is what keeps "copies on assignment" from meaning a - shallow copy that aliases owned storage. Deep copies are always explicit: - ~(clone x)~. Value structs are the snapshot / undo / replay story; they need no - separate type. +- Structs copy on assignment. A struct with a ~Vec~ field copies the header, so + the two copies alias one buffer, as in Odin. Deep copies are always explicit: + ~(clone x)~. Structs without owning fields are the snapshot / undo / replay + story; they need no separate type. - Literals live in read-only memory. - *A global's initialiser may be computed.* A value the linker can write goes into the image and costs nothing to start; anything else is stored at @@ -156,8 +155,8 @@ identity, extensibility, and live schema changes: Classes require a managed allocation strategy and runtime class/shape metadata, but *not necessarily a tracing GC*. The initial likely choices are a world or -session arena, pool allocation behind generational ~(Handle T)~ values, or an -explicitly owned region. A small tracing GC confined to class instances remains +session arena, a library pool behind generational handles, or an explicitly +owned region. A small tracing GC confined to class instances remains an option if cyclic graphs prove burdensome; it never changes ~struct~ layout or the C ABI. @@ -187,8 +186,11 @@ surprising lazy mutation on field access: #+end_src The precise class syntax, inheritance, storage strategy, and migration API are -not frozen. Do not add classes until ordinary ~struct~, ~Handle~, and reload -semantics are working. +not frozen. What is built is narrower than this section: a ~defclass~ instance +is a dyn map with its class in the object header, and a redefined class +migrates each instance lazily at its next touch, so nothing enumerates live +instances (TODO.org, "defclass is a named dyn map with a shape tag" and "A +redefined defclass migrates its instances lazily"). Immutability also serves the optimiser: a value known never to be mutated can be copied into registers and stack-allocated freely. Mutability is what forces heap