From f227cce8462defea4c7973fabdbb75e848b4d3de Mon Sep 17 00:00:00 2001 From: Brummel Date: Sun, 10 May 2026 14:50:42 +0200 Subject: [PATCH] =?UTF-8?q?floats=20iter=201:=20JOURNAL=20=E2=80=94=20iter?= =?UTF-8?q?ation=20close=20(schema=20layer)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/JOURNAL.md | 90 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/docs/JOURNAL.md b/docs/JOURNAL.md index ffc01d3..08169dc 100644 --- a/docs/JOURNAL.md +++ b/docs/JOURNAL.md @@ -13082,3 +13082,93 @@ the wrong section. A future widening would either (a) constrain the test to scan only §Data-model + ParamMode block, or (b) extract the JSON-schema blocks from §Data-model into a machine-readable file the test consumes. Queued. + +## 2026-05-10 — Iteration Floats.1: schema layer + +Added `Literal::Float { bits: u64 }` as the fifth `Literal` AST +variant. Bits are an IEEE-754 binary64 bit pattern, serialised as +a 16-character lowercase hex *string* in canonical JSON +(`{"bits":"","kind":"float"}`) via a private `hex_u64` serde +helper module on `ast.rs`. Routing the field through the JSON +string path bypasses `serde_json::Number::to_string` (not +bit-stable across `serde_json` versions for floats) and lets NaN / +±Inf survive canonicalisation at all — they collapse to `null` as +JSON numbers and are silently lost. + +`Float` is now registered as a primitive type name in +`primitives.rs`; the lockstep test was extended with explicit +`assert!` lines after the loop because the loop's `assert_eq!` +passes vacuously when both functions return `false` / `None` for +a missing primitive. + +Adding the variant broke Rust's enum exhaustiveness at eight +downstream `match Literal { … }` source sites and at two +drift-test exhaustive matches. Each got either the permanent +semantic arm (`ailang-check` typecheck: +`Literal::Float { .. } => Type::float()`) or a named-iteration +`unimplemented!("Floats milestone iter N: ")` arm +(`ailang-codegen` → iter 4, `ailang-surface` print → iter 2, +`ailang-prose` → iter 5). The arms are honest about which +iteration owns the semantics, so future debugging starts with the +right pointer rather than a generic `unreachable!`. + +The two drift-test exhaustive matches +(`spec_mentions_every_literal_variant`, +`design_md_anchors_every_literal_variant`) were extended along +with their exemplars and the corresponding spec anchors: +`crates/ailang-core/specs/form_a.md` gained a `` `FLOAT` `` atom +form line; `docs/DESIGN.md` gained a `{ "kind": "float", "bits": +"<16-lowercase-hex>" }` line in the Literal JSON-schema block at +line 1866. The drift tests are *the enforcement mechanism* of the +schema invariant — the original 5-iter plan put DESIGN.md changes +in iter 5, but the drift-test break revealed that schema-layer +DESIGN.md anchors belong in iter 1 (where they get added together +with the AST variant they document). Iter 5 retains the §"Float +semantics" subsection and the line-2033 "supported primitive +types" list update — those depend on later iterations being +shipped. + +Bit-stability tests pin the A1 / A5 spec guarantees: `-0` ≠ `+0` +at the canonical-bytes level (distinct hex strings); NaN bits +preserved; ±Inf bits preserved; serde round-trip is bit-exact for +the saturating boundary values (zero, sign-bit-only, 1.5, qNaN, +±Inf, all-ones). + +Pre-existing `def_hash` regression hashes (`db33f57cb329935e` for +`sum.sum`, `b082192bd0c99202` for `IntList`) stayed GREEN — +adding a `Literal` variant does not perturb any pre-existing +canonical bytes, because `serde(tag = "kind")` keeps the +discriminator-only-on-construct path. + +Rustdoc warning count stayed at the milestone-open baseline (1 +pre-existing warning on `desugar`). Two new private-intra-doc-link +warnings introduced by the initial `Literal::Float` doc-comment +(`[`hex_u64`]` linking to a private mod) were caught by the +acceptance gate and fixed inline by dropping the link form in +favour of a plain-text `hex_u64` reference. + +Per-task commits: + +- `ec28111` floats iter 1.1: Literal::Float variant + canonical hex serde + drift-test anchors +- `93fe2da` floats iter 1.1 fixup: trim form_a.md FLOAT bullet to match neighbour style +- `aa5b88e` floats iter 1.2: register Float as a primitive type name +- `93bae2d` floats iter 1.2 fixup: replace iter-N comment with durable rationale for explicit assertions +- `7c95a69` floats iter 1.3: bit-stability tests for Literal::Float +- `1a4e2f0` floats iter 1.4: refresh canonical.rs 'no floats' doc comment +- `b2d3182` floats iter 1.4 fixup: drop intra-doc-links to private hex_u64 (rustdoc baseline preservation) + +Known debt deliberately deferred to later iterations of this +milestone: + +- Surface lex / parse / print round-trip for `1.5`, `1.5e3`, + `1e10` — iter 2. +- Typecheck widening of `+`/`-`/`*`/`/`/`<`/`<=`/`>`/`>=` from + monomorphic Int to polymorphic-`{Int, Float}` — iter 3. +- Codegen Float lowering paths (`fadd`/`fsub`/`fmul`/`fdiv`, + `fcmp o*`/`une`, `sitofp`, `@llvm.fptosi.sat.i64.f64`, + `fcmp uno` for `is_nan`, hex-float literal constants for + `nan`/`inf`/`neg_inf`) — iter 4. +- Prose round-trip for Float literals — iter 5. +- DESIGN.md §"Float semantics" subsection (A5 determinism + contract) and line-2033 "supported primitive types" list + update — iter 5.