memory-model: reframe arenas as static frame layout (FORTRAN), not bump heap #38

Manually merged
hkowalski merged 8 commits from feat/038-memory-model-reframe-arenas-as-static-frame-la into master 2026-02-17 10:45:49 +01:00
Owner

memory-model: reframe arenas as static frame layout (FORTRAN), not bump heap

Along the way:

  • Actor behavior execution: spawn body runs — helloWorld prints "hello, world"
  • scripts/deploy.sh: install the compiler as ckc on PATH
  • scripts/clean.sh: remove run artifacts (work dirs + ck containers)
  • docs: current-state.md — helloWorld runtime climb handoff
  • .claude/commands: /orchestrate — slice-by-slice build orchestrator
  • ckc run: human progress output; rename ck binary → ckc
  • S1: user function application with parameters (runtime)

Touches 44 files: .claude/commands/orchestrate.md, README.md, current-state.md, docs/actors.md, docs/configuration.md, docs/editor.md and 38 more.

memory-model: reframe arenas as static frame layout (FORTRAN), not bump heap Along the way: - Actor behavior execution: spawn body runs — helloWorld prints "hello, world" - scripts/deploy.sh: install the compiler as `ckc` on PATH - scripts/clean.sh: remove run artifacts (work dirs + ck containers) - docs: current-state.md — helloWorld runtime climb handoff - .claude/commands: /orchestrate — slice-by-slice build orchestrator - ckc run: human progress output; rename `ck` binary → `ckc` - S1: user function application with parameters (runtime) Touches 44 files: `.claude/commands/orchestrate.md`, `README.md`, `current-state.md`, `docs/actors.md`, `docs/configuration.md`, `docs/editor.md` and 38 more.
Author
Owner

It said what I assumed. Reworded in src/rust/crates/ck_driver/tests/actor_lifecycle.rs so it names the input that broke it, which is what the bug report will need.

It said what I assumed. Reworded in `src/rust/crates/ck_driver/tests/actor_lifecycle.rs` so it names the input that broke it, which is what the bug report will need.
hkowalski approved these changes 2026-02-17 08:00:00 +01:00
hkowalski left a comment

Happy with this. I ran the suite against the branch and it is green, src/rust/crates/ck_driver/tests/actor_lifecycle.rs included.

Happy with this. I ran the suite against the branch and it is green, `src/rust/crates/ck_driver/tests/actor_lifecycle.rs` included.
@ -147,3 +147,3 @@
.arg(src)
.status()
.expect("spawn the `ck` binary");
.expect("spawn the `ckc` binary");
Owner

The expect message on expect is the only thing a user sees if this fires. Does it say what they should do, or only what we assumed?, at src/rust/crates/ck_driver/tests/actor_lifecycle.rs:149.

The expect message on `expect` is the only thing a user sees if this fires. Does it say what they should do, or only what we assumed?, at src/rust/crates/ck_driver/tests/actor_lifecycle.rs:149.
dferreira approved these changes 2026-02-17 10:40:49 +01:00
dferreira left a comment

Looked through src/rust/crates/ck_driver/tests/actor_lifecycle.rs as well. Fine by me.

Looked through `src/rust/crates/ck_driver/tests/actor_lifecycle.rs` as well. Fine by me.
An arena is the hard boundary around one actor's static frame layouts —
not a heap, not primarily a bump allocator. Bindings are fixed (offset,
size) slots the compiler computes; the only dynamic state is the length
counter on capacity-reserved collections. Compiler does alloc/free, no
borrow checker (no surface pointer arithmetic → nothing to police).

- arenas.md rewritten: arena-as-boundary definition; static frame layout;
  three nested bounded regions (call stack / call-slots / actor instances)
  with total-system-RAM-computable-before-run; stack discipline replaces
  the scratch-reset; bounded mailbox + unified three-boundary overflow;
  backpressure-as-buffering rejected; no-pointers safety claim.
- README.md: arena definition up top; static layout / three regions /
  bounded mailbox moved to Committed (2026-06-30); pillar + M2 row reframed.
- M1: bump-allocator → arena boundary + static frame layout.
- M2: retitled scratch-arena → state/stack split; return-is-the-free;
  call-slot free-list of fixed-shape frames.

Locked 2026-06-30. Open in spelling only: call-slot overflow policy and
full-mailbox-send behavior. Docs link checker green.
The compiled actor now RUNS its spawn body between the spawn and despawn
lifecycle events. ck_rt_run_actor takes a behavior fn pointer
(Option<unsafe extern "C" fn()>): it emits ActorSpawned(root)+ActorSpawned(Main),
calls the behavior if present, then emits ActorDespawned — the 3-event sequence
is unchanged in content/order (single_actor_lifecycle untouched; the in-memory
Runtime match test still holds), only the despawn moves to after the body runs.

Codegen lowers the actor's spawn body into a separate local extern "C" fn
(ck_actor_behavior), reusing the fn-main body path (emit_intrinsic_call +
OpDispatch + string -> ck_rt_write_stdout); build_actor_main passes its address
via func_addr (null iconst 0 when the body is empty). Resolver retains slot
bodies via a span-keyed ResolvedModule.slot_bodies side-channel (spawn slots
have no FnId) — additive, not a resolver-signature rewrite. compile.rs lowers
the spawn body in both the single-file and closure actor paths.

The one new unsafe is the fn-pointer call in entry.rs (confined, SAFETY-noted:
a codegen-emitted no-arg/no-return extern "C" fn). ck_lower/ck_driver stay
forbid(unsafe_code).

Independently verified: `ck run tests/corpus/helloWorld` (relative path, Docker)
-> exit 0, stdout "hello, world", the 60-byte Main lifecycle on the meta stream;
empty-body actors print nothing (trivial behavior preserved).

Gates: fmt 0, clippy --all-targets -D warnings 0, cargo test workspace 0 fail
(actor_behavior_run concrete+interface, all prior fixtures, corpus reconciliation,
ck_rt lifecycle-match, architecture R1-R4 (codegen.rs/compile.rs at the 809 cap
via extraction, not loosening), linkchecker), build clean, 0 leaked containers.

trap.md retirement + the formal helloWorld runtime #[test] are the next slice.
Adds a plain-shell deploy script (no Make) that installs the bootstrap compiler
as `ckc` under ~/.local/bin, deliberately distinct from the user's existing
`ck` (an unrelated interactive menu) — never clobbered.

It builds ckc + the ck_rt floor staticlib, installs the real binary plus
libck_rt.a side by side under ~/.local/libexec (the compiler links every program
against libck_rt.a located next to its own binary), and drops a thin `ckc`
launcher on PATH that defaults CK_STDLIB to the live repo stdlib so
`ckc run <app>` works from anywhere with no --stdlib and no shell-rc edit
(caller's CK_STDLIB / --stdlib still win). `release` default, `debug` arg for
fast iteration. Verified: `ckc run tests/corpus/helloWorld` -> "hello, world"
from both the repo root and an unrelated directory.
Each `ck run` leaves /tmp/ck-work/<container>/ (binary + object + meta.stream)
and a long-lived container; neither is auto-collected. clean.sh removes both:
it derives the container names from the per-container work dirs (plus the
default `ck`), checks which actually exist (docker rm -f is lenient on missing
containers — avoid phantom "removed" lines), removes those, then clears
/tmp/ck-work. Idempotent. `--build` also runs `cargo clean`.

Does NOT touch the deployed `ckc`, the unrelated `ck` menu, source, or git.
Verified: removes only the containers that exist, second run is a clean no-op.
Where we stand after compiling+running helloWorld end-to-end: how to
build/run/clean (ckc, deploy.sh, clean.sh), the pipeline + crate map, a precise
honest account of the NARROW executor surface that runs today (print-only:
intrinsic effect calls with string/int literals; no let/if/match/for/user-calls/
recursion; run path is resolve-only), the slice ledger (F1->actor-behavior),
known gaps/debt, and the next slices to write LARGER apps (R2 control flow + R3
user calls being the biggest unlocks). For tomorrow.
Captures the orchestrator-and-validator pattern that drove the helloWorld
runtime climb: derive the slice list from the cascade docs, pause for
sign-off, then loop pick → right-path pre-check → dispatch one clean
sub-agent → verify gates+acceptance yourself → land one commit per slice.
Encodes the steering correction (sub-agents start cold with self-contained
briefs; slices stay small enough to validate/correct/steer; orchestrator is
accountable, sub-agents responsible). Goal is $ARGUMENTS.
`ckc run` now narrates the compile→run pipeline to stderr (program stdout
stays a clean channel): `compiling…` / `compiled in <t>` + `executable
at:` + `0 warnings`/`0 errors`, then `running in alpine container:` with
`starting`/`reusing container <t>`, `running <bin> in container`, and
`finished execution in <t>`. The 0-warnings/0-errors summary is honest by
construction — a compile error returns early and prints diagnostics, and
CkLang has no warning severity.

New `ck_driver::progress` module owns wording/formatting (presentation);
`run.rs` owns sequencing + timing — presentation and orchestration stay
split. Duration formatting is unit-tested.

Rename the compiler binary `ck` → `ckc` everywhere: the `[[bin]]` target,
deploy.sh artifact path, the default container name (`DEFAULT_CONTAINER`
+ clean.sh `names` — a real fix, clean.sh would otherwise miss the new
default container), USAGE text, `ckc:` error prefixes, test binary
lookups, and command references across current-state.md and the docs tree.

Gates: workspace build + clippy --all-targets -D warnings + ck_driver
lib/architecture tests + docs link checker all clean; helloWorld runs
end-to-end on cold and reused containers.
An actor `spawn` body (and `fn main`) can now call a user-defined `fn`
that takes and uses parameters, executing end-to-end through the native
pipeline (HIR → Floor IR → Cranelift → binary → container).

One uniform call path for n>=0 parameters (0 is just the n=0 case; no
special parameterless path): a String param is the (ptr,len) pair, an Int
one i64. The driver walks the entry body's transitive same-module user-fn
callees, reads param types manifestly off the lifted signature (no
inference), and lowers each callee to its own Linkage::Local Cranelift
function (two-pass declare-then-define). Params bind via a LocalId->Values
slot map; a Ref-to-local arm threads them. Compile-time DI now resolves
over every user-fn body, not just the entry.

Out of scope (later slices): recursion + E0901 bounds, lambdas, String
concat, value-returning user fns (rejected with a clear error),
cross-module callees.

Files split to stay under the R3 LoC cap (ratchets worst-case down):
ck_lower/codegen.rs 807, ck_driver/compile.rs 780; new modules
codegen/{user_fn,strings}.rs, ck_driver/{user_fns,dispatch}.rs. This also
cleared the pre-existing rustfmt drift in compile.rs/run.rs.

Gates: cargo fmt --check clean; clippy --all-targets -D warnings 0/0;
cargo test --workspace all pass; acceptance user_fn_call_run prints
exactly `world` with the three-event Main lifecycle in the ckc container.
hkowalski manually merged commit c5a12eaa3a into master 2026-02-17 10:45:49 +01:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
3 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
northgale/cklang-bootstrap!38
No description provided.