Closure-routed run: ck run on a project dir (helloWorld runs its lifecycle) #37
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/037-closure-routed-run-ck-run-on-a-project-dir-hel"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closure-routed run: ck run on a project dir (helloWorld runs its lifecycle)
Along the way:
Touches 13 files:
docs/impl/08-diagnostics.md,docs/impl/lowering/01-cranelift-emit.md,docs/impl/resolution/C2-provider-table.md,docs/impl/runtime/substrate-and-supervisor.md,src/rust/crates/ck_driver/src/compile.rs,src/rust/crates/ck_driver/src/lib.rsand 7 more.Nothing outside calls
run_project. It is pub(crate) insrc/rust/crates/ck_driver/src/run.rsnow.Split. The decision in
repo_rootis a pure function now, with its own tests insrc/rust/crates/ck_driver/tests/log_interface_run.rs.A clear improvement. The change in
src/rust/crates/ck_driver/src/run.rsis the part I would have argued for.@ -71,0 +101,4 @@/// (most often: it does not exist); otherwise [`RunError`] if any stage fails. A/// non-zero *program* exit is reported in [`RunOutcome::exit_code`], not as an/// error.pub fn run_project(run_projectis public. Does anything outside the crate call it, or is it pub because that was easier? Every public item is a promise.@ -0,0 +136,4 @@/// The repo root, four parents above this crate's manifest dir/// (`<repo>/src/rust/crates/ck_driver`).fn repo_root() -> PathBuf {repo_rootreads, decides and writes in one function. The deciding part is the piece I would want to test on its own. Worth splitting?, at src/rust/crates/ck_driver/tests/log_interface_run.rs:139.`ck run` on a project whose app entry is fn main() -> Unit { StdoutLog::info("hello, world") } now prints `hello, world` from inside the container — the first time a CkLang program produces output. Independently verified with an arbitrary string ("orchestrator was here 42" → that exact stdout), proving the literal flows through, not a hardcoded write. Lowering (ck_lower/codegen.rs): Lit::Str -> a read-only data symbol holding the unescaped bytes (read_str_bytes/decode_escapes over the lexer's escape table), value = (ptr, len). emit_intrinsic_call is general: callee must resolve to Ref(Resolution::Item(Fn)), the driver-supplied IntrinsicTargets table maps the FnId -> (module,op), and codegen's intrinsic_target maps (module,op) -> floor symbol (StdoutLog::* -> ck_rt_write_stdout) — no call-site name special-casing; intrinsic-ness comes only from the fn's `= intrinsic` identity + the target map. The floor symbol is imported and called with (ptr,len). Unit fn main runs the body then exits 0. compile_project_object gains a fn-main entry path (prefers actor if present). Driver builds IntrinsicTargets (FnId -> (module,op)) by re-scanning the closure's intrinsic-bodied fns. ck_lower still depends only on ck_hir/ck_span/ck_types. Provider privacy (E0302): a `mod X provides E` module is its own header, so its operation fns are public (docs/effects.md "neither is hidden"); link.rs promotes a provider module's top-level fns to Public. Doc-backed; follow-up tracked to narrow this to op-fns only (so future private helpers stay private) and to add run-path type-checking (the fn-main path is resolve-only today). Gates: fmt 0, clippy --all-targets -D warnings 0, cargo test workspace 0 fail (stdout_run 2 real-docker two-string, closure_run 3, F2 42/9, actor_lifecycle, meta, corpus reconciliation, architecture R1-R4, ck_resolve 89, linkchecker), build clean, 0 leaked containers.A call to an effect's interface op now compile-time-resolves to its bound provider and prints: `ck run` on `fn main() -> Unit { Log::info("hi") }` prints `hi` via Log -> StdoutLog::info -> ck_rt_write_stdout. Independently verified with an arbitrary string through the interface. Driver (compile.rs) builds the C2 provider table over the closure and an OpId->FnId dispatch map via the locked tree-search: OpId -> (EffectId, op-name) via interner.op_data; exactly one provider -> use its fn for that op; zero -> E0503; two-or-more -> E0504 (config tie-break NOT implemented — config.dev.ck's `Log <- StdoutLog` is not consumed; Log resolves only by sole-impl). Never a silent pick. Codegen's emit_intrinsic_call now accepts a Resolution::Op(OpId) callee (mapped via the driver-supplied OpDispatch) as well as the concrete Resolution::Item(Fn) form, then runs the unchanged FnId->(module,op)->floor symbol path. ck_lower still depends only on ck_hir/ck_span/ck_types. helloWorld's Log::info resolves to Op(OpId) and will dispatch unambiguously to the sole StdoutLog provider once its actor body is executed (next slice). Gates: fmt 0, clippy --all-targets -D warnings 0, cargo test workspace 0 fail (log_interface_run 2 real-docker, stdout_run 2, closure_run 3, F2 42/9, actor_lifecycle, meta, corpus reconciliation, architecture R1-R4, linkchecker), build clean, 0 leaked containers. Diagnostics E0502/E0503/E0504 documented.