jolt/test/integration/shape-transparency-test.janet
Yogthos c6b964c1b1 feat: general shape-record mechanism — consistent representation + transparency (jolt-t34 R1)
Removes ALL hardcoding. Every constant-key map literal in user code becomes a
shape-rec (a Janet tuple [descriptor v0 v1 ...]); the descriptor is interned
per key SET with a single canonical key order owned by the runtime
(types/shape-sort), so every site that builds or reads a shape agrees.

Consistency is the foundation: shape-recs are made UNCONDITIONALLY for
const-key user maps (emit-map), not gated on the inference — so a value's
representation always matches what the type system claims, which was the bug
that broke the earlier generalization (a ray built as a struct but bare-indexed
as a shape). Gated on :inline? so it applies to user data only, never to core
or the compiler's own IR-node maps.

Transparency layer (the shape-rec block now lives in types.janet, reachable by
core + evaluator + backend): get, assoc, dissoc, count, contains?, map?, first,
seq, the central realize-for-iteration normalizer (keys/vals/reduce-kv), eq-map-
pairs + eq-seqable (equality), pr-render (print), jolt-call (compiled IFn), and
the interpreter's coll-lookup all handle shape-recs. A new spec
(shape-transparency-test) asserts each op matches the equivalent struct map,
including nil/false values (which shape-recs store positionally — unlike
structs).

The ray tracer renders byte-identically (mean 122.04). Gate green with the flag
off, suite 4718. NOT yet faster — every field read currently takes the
descriptor path because the inference drops the complete :shape through joins
and containers; that's Round 2 (completeness preservation). All behind
JOLT_SHAPE (off by default).
2026-06-13 20:40:50 -04:00

64 lines
3.1 KiB
Text

# Shape-record transparency (jolt-t34 Round 1). A shape-rec is the compiler's
# cheap representation for a constant-key map literal — a Janet tuple
# [descriptor v0 v1 ...]. Every map operation must treat it EXACTLY like the
# equivalent struct map, so a shape value is transparent wherever it flows.
# These build shape-recs directly via the runtime (shape-for + tuple) and
# assert each op matches the struct map's behavior.
(use ../../src/jolt/types)
(use ../../src/jolt/core)
(var fails 0)
(defn check [label got want]
(if (deep= got want) (print " ok " label)
(do (++ fails) (printf " FAIL %s: got %j want %j" label got want))))
# {:a 1 :b 2} as a shape-rec and as a struct — they must be indistinguishable
(def SH (shape-for [:a :b]))
(def r (tuple SH 1 2))
(def s {:a 1 :b 2})
# --- access ------------------------------------------------------------------
(check "get hit" (core-get r :a nil) 1)
(check "get hit 2" (core-get r :b nil) 2)
(check "get miss" (core-get r :z :d) :d)
(check "count" (core-count r) 2)
(check "contains? hit" (core-contains? r :a) true)
(check "contains? miss" (core-contains? r :z) false)
(check "map?" (core-map? r) true)
# --- update (returns something that reads back correctly) --------------------
(check "assoc existing" (core-get (core-assoc r :a 9) :a nil) 9)
(check "assoc new key" (core-get (core-assoc r :c 3) :c nil) 3)
(check "assoc keeps others" (core-get (core-assoc r :c 3) :b nil) 2)
(check "dissoc" (core-get (core-dissoc r :a) :a :gone) :gone)
(check "dissoc keeps" (core-get (core-dissoc r :a) :b nil) 2)
# --- enumeration: entries via the central seq normalizer (what keys/vals/seq/
# reduce-kv in the overlay all flow through) — order-independent ---------------
(defn entryset [m] (sorted (map |(string/format "%j" $) (realize-for-iteration m))))
(check "entries match struct" (entryset r) (entryset s))
(check "count of seq" (length (core-seq r)) 2)
(check "first is a 2-entry" (length (core-first r)) 2)
# --- equality: a shape-rec equals the same struct map and vice versa ---------
(check "= shape vs struct" (jolt-equal? r s) true)
(check "= struct vs shape" (jolt-equal? s r) true)
(check "= shape vs shape" (jolt-equal? r (tuple (shape-for [:a :b]) 1 2)) true)
(check "not= diff value" (jolt-equal? r (tuple (shape-for [:a :b]) 1 9)) false)
(check "not= diff shape" (jolt-equal? r (tuple (shape-for [:a :b :c]) 1 2 3)) false)
(check "not= vs vector" (jolt-equal? r [1 2]) false)
# --- IFn: a map is callable as a key lookup ----------------------------------
(check "call as fn" (jolt-call r :a) 1)
(check "call as fn miss default" (jolt-call r :z :d) :d)
# --- nil/false values are present (shape-recs store them positionally) --------
(def rn (tuple (shape-for [:a :b]) nil false))
(check "nil value present" (core-contains? rn :a) true)
(check "nil value get" (core-get rn :a :d) nil)
(check "false value get" (core-get rn :b :d) false)
(check "count with nils" (core-count rn) 2)
(if (> fails 0)
(error (string "shape-transparency: " fails " failing check(s)"))
(print "\nShape transparency passed!"))