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).
64 lines
3.1 KiB
Text
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!"))
|