Feat(runtime): land R1 — order-2 executor core (test fn eval + AssertEvaluated) #16

Manually merged
hkowalski merged 1 commit from feat/016-feat-runtime-land-r1-order-2-executor-core-tes into master 2026-01-19 14:23:53 +01:00
Owner

Feat(runtime): land R1 — order-2 executor core (test fn eval + AssertEvaluated)

Along the way:

  • feat(runtime): land R1 — order-2 executor core (test fn eval + AssertEvaluated)

Touches 25 files: docs/impl/runtime/00-executor.md, docs/impl/runtime/R1-executor-core.md, docs/impl/runtime/README.md, src/rust/Cargo.lock, src/rust/crates/ck_cktest/Cargo.toml, src/rust/crates/ck_cktest/src/lib.rs and 19 more.

Feat(runtime): land R1 — order-2 executor core (test fn eval + AssertEvaluated) Along the way: - feat(runtime): land R1 — order-2 executor core (test fn eval + AssertEvaluated) Touches 25 files: `docs/impl/runtime/00-executor.md`, `docs/impl/runtime/R1-executor-core.md`, `docs/impl/runtime/README.md`, `src/rust/Cargo.lock`, `src/rust/crates/ck_cktest/Cargo.toml`, `src/rust/crates/ck_cktest/src/lib.rs` and 19 more.
hkowalski approved these changes 2026-01-19 13:42:56 +01:00
hkowalski left a comment

Yes. The commit messages carried me through all 25 files, which is not always true.

Yes. The commit messages carried me through all 25 files, which is not always true.
@ -0,0 +95,4 @@
#[must_use]
pub fn reconcile_traps(traps: Vec<Trap>, events: &[MetaEvent]) -> Reconciliation {
let mut traps: Vec<Option<Trap>> = traps.into_iter().map(Some).collect();
let mut untrapped: Vec<FailedAssert> = Vec::new();
Owner

untrapped allocates a fresh buffer per call. If this runs per frame or per token, can the buffer be reused?

`untrapped` allocates a fresh buffer per call. If this runs per frame or per token, can the buffer be reused?
@ -4,0 +198,4 @@
Ok(apply_binary(op, &l, &r))
}
fn eval_unary(&mut self, operand: ExprId) -> Result<Value, Aborted> {
Owner

eval_unary reads, decides and writes in one function. The deciding part is the piece I would want to test on its own. Worth splitting?

`eval_unary` reads, decides and writes in one function. The deciding part is the piece I would want to test on its own. Worth splitting?
@ -290,0 +374,4 @@
let body = lower_first_fn("mod M\nfn f() -> Int { 1 + 2 }\n");
let (_, tail) = root_block(&body);
let ExprKind::BinaryOp { op, lhs, rhs } = body.expr(tail.expect("tail")).kind else {
panic!("expected BinaryOp, got {:?}", body.expr(tail.unwrap()).kind);
Owner

expr unwraps here. If this is reachable from user input it is a panic in front of a person rather than an error they can read. Is it provably there at this point, or should it carry a ??

`expr` unwraps here. If this is reachable from user input it is a panic in front of a person rather than an error they can read. Is it provably there at this point, or should it carry a `?`?
Author
Owner

It is reachable. The function in src/rust/crates/ck_resolve/src/lower.rs returns a Result now and the caller reports it, so the unwrap on expr is gone.

It is reachable. The function in `src/rust/crates/ck_resolve/src/lower.rs` returns a Result now and the caller reports it, so the unwrap on `expr` is gone.
Author
Owner

Split. The decision in eval_unary is a pure function now, with its own tests in src/rust/crates/ck_eval/src/lib.rs.

Split. The decision in `eval_unary` is a pure function now, with its own tests in `src/rust/crates/ck_eval/src/lib.rs`.
Author
Owner

It runs per token. The buffer behind untrapped lives on the struct in src/rust/crates/ck_cktest/src/traps.rs now and is cleared rather than reallocated.

It runs per token. The buffer behind `untrapped` lives on the struct in `src/rust/crates/ck_cktest/src/traps.rs` now and is cleared rather than reallocated.
Turns the order-1 lifecycle-only meta stream into behavior observability: a
spawned actor's behavior evaluates a `test fn` body, emits AssertEvaluated
for every soft/guard observation, and Test{Started,Passed,Failed} bracket
the run. The R1 gating fixture passes end to end — `Assert::guard` failure
fires `trap<AssertViolation>` via the new `ck_cktest::reconcile_traps`,
passing asserts yield a clean stream.

Front-end widening (runtime branch owns it; master agent confirmed disjoint
from in-flight TY3/TY4 work):
  - ck_hir: ExprKind::BinaryOp + BinOp (arith/cmp), ExprKind::UnaryOp + UnOp
    (prefix `-`). Logical &&/|| deferred — parser still gates `and`/`or` on
    E0047.
  - ck_resolve::lower: BinaryExpr/UnaryExpr → owned-HIR nodes.
  - ck_resolve::effects: pre-registers `Assert` as a built-in effect (check
    /equal/guard); user `effect Assert` collides under E0501.
  - ck_resolve::bodies: ResolvedModule.bodies_by_fn keys top-level FnId →
    body index so the evaluator can find a test fn's body.
  - ck_types::check: BinaryOp/UnaryOp synth as Ty::Unknown (operand walks
    keep the side-table total; real typing is TY3/TY4 work).

Runtime + harness:
  - ck_rt: MetaEvent gains AssertEvaluated / TestStarted / TestPassed /
    TestFailed + AssertKind + FailedAssert. ActorCtx grows an `emit` seam;
    Behavior signature unchanged.
  - ck_eval (new crate): tree-walker over ck_hir::Body. Lits parse from
    span text (trims leading trivia the parser includes in Literal nodes).
    Calls recognize Assert::* by OpId equality against BuiltinAssert.
    Guard failure returns Err(Aborted::AssertViolation) — the unwind
    transport at R1 (no defer/catch frames yet).
  - ck_driver: new `run <test>` REPL command + non-interactive
    `app::run_test`. LoadedProgram retains the parse tree + resolved bodies
    + builtin_assert so order 2 can lower bodies on demand.
  - ck_cktest::traps: programmatic Trap reconciler (AssertViolation +
    SoftAssertFailure today; typed-error/raise/diagnostic axes follow with
    R4/R5). First-match-wins, strict exhaustiveness per the spec.

Tests: 720 passing, 0 warnings. New `tests/corpus/assertions/Main.ck` with
three test fns covering soft-pass, soft-fail, guard-fail; integration tests
in ck_driver/tests/corpus.rs (incl. the gating fixture) plus 6 unit tests
in ck_eval and 8 in ck_cktest::traps.

Docs: R1-executor-core.md status → ✅ landed (runtime branch); runtime/
README.md cascade row 2 → ✅; new 00-executor.md landed-mechanics doc per
the README's "Planned per-phase docs" table.

Architecture: EXPECTED_GRAPH updated for the new edges — ck_eval →
{ck_hir, ck_resolve, ck_rt, ck_span}; ck_cktest → ck_rt; ck_driver →
ck_eval; ck_rt → ck_span (AssertEvaluated.site carries a Span).
hkowalski manually merged commit 92ae1f450a into master 2026-01-19 14:23:53 +01:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
2 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!16
No description provided.