docs(plan_values): align §9 gate to §10's row-set ISA wording #57

Manually merged
mdoorn merged 9 commits from feat/057-docs-plan-values-align-9-gate-to-10-s-row-set into master 2026-03-13 08:30:00 +01:00
Owner

docs(plan_values): align §9 gate to §10's row-set ISA wording

Along the way:

  • docs(plan_values): make String and Char first-class value types
  • docs(decisions): lock B16 — the actor is the ONLY mutable persistent state
  • docs(plan_values): finalize the f* family (f8/f16/f32/f64) + no-special-cases principle
  • docs(claude): guardrail — the language is complete, don't import foreign problems
  • docs(claude): lead the completeness guardrail with the core stance
  • feat(plan_values A1): the f* scalar type family + decompose ck_types type-IR
  • feat(plan_values A3): float literals + arithmetic at the primitive-op tier
  • docs(plan_values): precision is an operation arg, not a type param — remove A2

Touches 35 files: CLAUDE.md, README.md, docs/current-state.md, docs/decisions/decisions-backend-locks.md, docs/types.md, plan_values.md and 29 more.

docs(plan_values): align §9 gate to §10's row-set ISA wording Along the way: - docs(plan_values): make String and Char first-class value types - docs(decisions): lock B16 — the actor is the ONLY mutable persistent state - docs(plan_values): finalize the f* family (f8/f16/f32/f64) + no-special-cases principle - docs(claude): guardrail — the language is complete, don't import foreign problems - docs(claude): lead the completeness guardrail with the core stance - feat(plan_values A1): the f* scalar type family + decompose ck_types type-IR - feat(plan_values A3): float literals + arithmetic at the primitive-op tier - docs(plan_values): precision is an operation arg, not a type param — remove A2 Touches 35 files: `CLAUDE.md`, `README.md`, `docs/current-state.md`, `docs/decisions/decisions-backend-locks.md`, `docs/types.md`, `plan_values.md` and 29 more.
mdoorn approved these changes 2026-03-12 17:22:55 +01:00
mdoorn left a comment

Approving. I would have split src/rust/crates/ck_driver/tests/float_arith_run.rs differently, but not enough to hold this up.

Approving. I would have split `src/rust/crates/ck_driver/tests/float_arith_run.rs` differently, but not enough to hold this up.
@ -0,0 +197,4 @@
.is_ok_and(|s| s.success());
assert!(ok, "failed to build the `ckc` binary");
});
find_near_exe("ckc").expect("`ckc` binary next to the test executable")
Owner

The expect message on find_near_exe is the only thing a user sees if this fires. Does it say what they should do, or only what we assumed?

The expect message on `find_near_exe` is the only thing a user sees if this fires. Does it say what they should do, or only what we assumed?
@ -0,0 +145,4 @@
/// the "no nested `Unknown`" invariant the rest of TY2 relies on: an
/// `Unknown` only ever appears at the top of a `Ty`.
#[must_use]
pub fn union(members: impl IntoIterator<Item = Ty>) -> Ty {
Owner

union is public. Does anything outside the crate call it, or is it pub because that was easier? Every public item is a promise.

`union` is public. Does anything outside the crate call it, or is it pub because that was easier? Every public item is a promise.
Author
Owner

It said what I assumed. Reworded in src/rust/crates/ck_driver/tests/float_arith_run.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/float_arith_run.rs` so it names the input that broke it, which is what the bug report will need.
Author
Owner

Nothing outside calls union. It is pub(crate) in src/rust/crates/ck_types/src/ty.rs now.

Nothing outside calls `union`. It is pub(crate) in `src/rust/crates/ck_types/src/ty.rs` now.
hkowalski approved these changes 2026-03-13 08:00:00 +01:00
hkowalski left a comment

Read it too. No objection to src/rust/crates/ck_driver/tests/float_arith_run.rs.

Read it too. No objection to `src/rust/crates/ck_driver/tests/float_arith_run.rs`.
Reword §9's ISA clause from "leaf count is UNCHANGED" (readable as a fixed
number) to "plan_values adds ZERO leaves; floor_isa.rs row-set unchanged from
its state at each slice's start". Since plan_floor_drift.md is concurrently
REDUCING the ISA, the gate is row-set equality against slice-start, never "= N".
Intent preserved: gate stays green, no new intrinsic, float arithmetic is the
primitive-op tier (not a leaf), byte-exact mailbox_ring proof unchanged.
Give String and Char the same deliberate treatment as the f* (§3a) and
integer (§3b) families:

- §2 catalog: rows for Char (lexes/types but Unsupported at lower.rs:331;
  open primitive-vs-u32) and String (blessed ValueRepr::Str today; F2
  retires Bytes::of_str/str_len; structurally non-empty; codec serialize).
- §3c Char: the third scalar family — one Unicode-scalar word; same ladder;
  the careful distinctions (not an integer, no invented char arithmetic,
  explicit Char↔u32 conversion; String is NOT List<Char> — encoding, not
  repr). Surfaces the open primitive-vs-sugar-for-u32 owner decision.
- §3d String: the archetype 2-word POD and the F2 linchpin. Records the
  CLAUDE.md lock (value type, no String-special repr/constructor), then the
  ladder with String's two genuine deviations: F2 retires Bytes word-
  selectors to field access (ISA shrinks), and serialize is the String-
  backed codec exception (carries a pointer → not free memcpy).
- §10: Char/String ladder mapping so the slicing section is complete.
Add backend lock B16 to decisions-backend-locks.md with a dedicated
spotlight section. States the invariant totally and positively:

  Mutable, persistent state exists in exactly one form — an actor's
  `state { }` (uniq, private, race-free by isolation, mutated only with
  `with mut`). Modules NEVER carry state. Frame-local `let mut`/buffers
  are transient, not persistent; everything else is imm. No fourth
  category.

Explicitly forbids the C/Rust ambient-state patterns CkLang does not
have: no static, no module global, no thread-local, no ambient context
threaded as a language obligation, no compiler cell as a language
concept. The runtime arena/test/raise "cells" are recorded as the
single-actor-interim lowering of actor `state`, to be deleted and
re-expressed as `state { }` fields on their owning actors.

Records the one irreducible seam as mechanism (not a language global):
the current-actor anchor the scheduler installs and bootstrap
establishes — the model's "stack pointer", which CkLang source never
names. Draws the cascade consequence: plan_floor_drift D4/D5 (B1/B2) are
downstream of the Test/Supervisor actor reshape, not precursors.
Expand the float family from f32/f64 to f8/f16/f32/f64 consistently across
plan_values.md (catalog, §3a ladder, §9 gate, sources), docs/types.md
(built-in primitives), and README.md; add the "No special cases in lowering"
principle to README (the compiler knows no module names above the leaf set;
the ISA only shrinks). Clean base for the plan_values Phase A slices.
Add a CLAUDE.md section, sibling to "The floor is tiny", generalizing the
no-Rust reflex from the floor level up to the language-design level: when
an agent hits an expression gap (ambient state, lifetime, dispatch, error
propagation), the reflex to import a C/Rust/GC-runtime problem model and
propose new CkLang surface is the bug. The rule: read the relevant
docs/*.md and confirm the language doesn't already answer it before
proposing any new mechanism/keyword/surface or escalating a "blocker".

Includes a four-item tell-list (any one → STOP and read the docs) and the
2026-07-08 worked example — asserting "CkLang has no way to name a mutable
global" and proposing `static`, when the actor (state {}, B16) already
answered it. Points at docs/actors.md and decisions-backend-locks.md B16.
Distill the realization into a lead paragraph: CkLang is more coherent
than my model of it — when I think I've found a hole, assume I'm wrong and
go read, because the design has almost certainly already spent that
decision. A "gap" is a gap in my grasp of the invariants, not the
language; make the language prove me wrong by reading it, never by adding
surface.
A1 (plan_values.md §10 Phase A) lands the float family in the type system and,
as the disciplined way to land it, decomposes the ck_types keystone file rather
than raising a LoC cap to fit the change.

The f* family:
- Add F8/F16/F32 to `Primitive` (F64 already present), symmetric with the
  integer widths, no coercion between widths (f32 != f64). Family threaded
  through the type system; no float lowers yet (lower_lit's Float arm stays
  Unsupported — A3 owns that), and no `ValueRepr` float variant (would be dead
  code until A3; a `// A3:` note records the deferral).

The decomposition (owner-directed — a cap is a signal, not a target):
- `lib.rs` is now a thin orchestrator (113 lines): crate docs + `mod` decls +
  `pub use` re-exports only. No type defs, no logic, no inline tests.
- Each type gets its own file: primitive.rs, ty.rs, assignable.rs,
  const_value.rs, existential_kind.rs, actor_target.rs, type_var_id.rs.
- Behavior lives ON the type: `impl Primitive { name, scalar_width, from_name,
  is_float, accepts_int_literal }`. The scattered free functions
  (`check_util::primitive_name`, `arena_size::scalar_width`) are DELETED and
  callers repointed; adding a scalar width is now a one-file edit.
- The ~400-line inline test module is lifted to `tests/assignable.rs`
  (integration test over the public API); `tests/float_family.rs` proves the
  family symmetric/distinct.
- `MAX_FILE_LOC` reverts 821 -> 811 (ratchet down to the new global worst case,
  ck_lex/syntax_kind.rs); no cap raise.

Gates: fmt clean, clippy 0 warnings, cargo test --workspace exit 0, ISA row-set
held at 23 (zero new leaf). No behavior change (all goldens byte-identical).
FloatLit now lowers and float arithmetic executes end-to-end to a pinned value:
`ckc run tests/corpus/float_arith` exits 42, freestanding. Zero new floor leaves
— the ISA row-set stays exactly 23.

- ck_lower: `ValueRepr::Float(FloatWidth)` retires A1's `// A3:` deferral note —
  a generic width-driven scalar-word kind (f8/f16/f32/f64), and unlike `Byte` a
  float CAN be a frame word. `lower_lit` retires the `Float -> Unsupported` arm
  (Char/Bool untouched), folding to `ConstVal::Float { bits, width }`. Float
  `+ - * / == <` fold to a new `FloatOp` when an operand's checked type is a
  float width — the SAME type-directed mechanism the integer IntOp fold uses,
  never a `match type_name`.
- ck_codegen: `FloatOp` emits Cranelift `fadd/fsub/fmul/fdiv` and ordered `fcmp`
  (the primitive-op tier, not a leaf); `f64const` for float literals; float
  frame load/store via the F64 Cranelift type.
- Decomposition (not a cap raise): extracted `floor_ir/scalar.rs` (ConstVal +
  Float/Int binop kinds) and `lower/binop_lower.rs` (the binary-op fold) so
  ck_lower stays under its 552 cap; caps unchanged.

Proof chain (all exactly-representable dyadic operands, so `==` is
bit-deterministic): 2.0+2.0==4.0 (fadd) · 4.0-1.5==2.5 (fsub) · 3.0*0.5==1.5
(fmul) · 1.0/4.0==0.25 (fdiv) · 0.25<1.0 (ordered fcmp) -> exit 42. Verified to
discriminate: mutating an operand yields the matching non-42 exit code.

Disclosed scope boundary: the checker types float-arith results as Ty::Unknown
(operator->trait typing is a separate TY concern); the fold gates on operand
types, sufficient for A3's literal-operand proof. Relevant to A5 (float params
crossing). Scope-outs A2 (precision) / A4 (conversion) / A5 untouched.

Gates: fmt clean, clippy 0 warnings, cargo test --workspace exit 0, container
run exits 42 (discriminates), ISA 23.
Owner decision (2026-07-08): precision is a property of the format OPERATION,
not the float type — exactly C's printf("%.2f", x), where precision lives at
the call site, not on double. There is no f64<precision: N> type, no type-level
precision parameter, no language default. Precision moves to plan_show.md as an
argument to Show::show.

Consequences recorded:
- §3a: replace the "precision is a type-level parameter" framing with the
  operation-level model; f64 is a pure IEEE scalar.
- §10 ladder rung 2: floats AND integers skip rung 2 (no type-level parameter);
  the rung is for genuine type-level bounds (String<24>'s Size, List's size).
- §10 A2: REMOVED — nothing to implement; the float ladder runs A1 -> A3 -> A4
  -> A5. A4 (width conversion) is next.
- §10 A5: drop the "precision rides as a type-level constant" clause.
mdoorn manually merged commit f65e6d3b2b into master 2026-03-13 08:30:00 +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!57
No description provided.