;; run-fieldread.ss — native record field-read gate (backend_scheme emit). ;; ;; When the inference types a keyword-lookup receiver as a record (it carries the ;; field-order :shape + :hint :struct), the back end reads the field by its static ;; slot via jrec-field-at instead of jolt-get. This gate pins the emit shape and ;; that the value matches jolt-get — for a declared field, a non-field key (no ;; bare path), and a default-arg form (no bare path). ;; ;; chez --script host/chez/run-fieldread.ss (import (chezscheme)) (load "host/chez/rt.ss") (set-chez-ns! "clojure.core") (load "host/chez/seed/prelude.ss") (load "host/chez/post-prelude.ss") (set-chez-ns! "user") (load "host/chez/host-contract.ss") (load "host/chez/seed/image.ss") (load "host/chez/compile-eval.ss") (define analyze (var-deref "jolt.analyzer" "analyze")) (define emit (var-deref "jolt.backend-scheme" "emit")) (define kw (lambda (n) (keyword #f n))) (define (evals src) (jolt-compile-eval (string-append "(do " src ")") "user")) (evals "(defrecord Vec3 [x y z])") (evals "(def a (->Vec3 10 20 30))") ;; emit (:KEY a [default]) with arg 0 marked as a Vec3 struct receiver. (define (mark-emit src) (let* ((ir (analyze (make-analyze-ctx "user") (jolt-ce-read src))) (a0 (jolt-nth (jolt-get ir (kw "args")) 0)) (marked (jolt-assoc a0 (kw "hint") (kw "struct") (kw "shape") (jolt-vector (kw "x") (kw "y") (kw "z")))) (args (jolt-get ir (kw "args"))) (args2 (jolt-assoc args 0 marked))) (emit (jolt-assoc ir (kw "args") args2)))) (define (run-emit scm) (eval (read (open-input-string scm)) (interaction-environment))) (define (has-sub? s sub) (let ((n (string-length s)) (m (string-length sub))) (let loop ((i 0)) (cond ((> (+ i m) n) #f) ((string=? (substring s i (+ i m)) sub) #t) (else (loop (+ i 1))))))) (define fails 0) (define total 0) (define (check label actual expected) (set! total (+ total 1)) (unless (equal? actual expected) (set! fails (+ fails 1)) (printf " FAIL ~a: got ~s expected ~s\n" label actual expected))) ;; a declared field -> bare-index path, value matches jolt-get (let ((e (mark-emit "(:y a)"))) (check "declared field uses jrec-field-at" (has-sub? e "jrec-field-at") #t) (check "field 1 -> static slot 1" (has-sub? e " 1 ") #t) (check "bare read == jolt-get" (run-emit e) (evals "(:y a)"))) ; 20 ;; first/last fields too (check "field x == jolt-get" (run-emit (mark-emit "(:x a)")) (evals "(:x a)")) ; 10 (check "field z == jolt-get" (run-emit (mark-emit "(:z a)")) (evals "(:z a)")) ; 30 ;; a key that is NOT a declared field -> no bare path, still correct (nil) (let ((e (mark-emit "(:w a)"))) (check "non-field key no jrec-field-at" (has-sub? e "jrec-field-at") #f) (check "non-field key == jolt-get" (run-emit e) (evals "(:w a)"))) ; nil ;; a default-arg form keeps jolt-get (the bare path is no-default only) (let ((e (mark-emit "(:y a 99)"))) (check "default-arg keeps jolt-get" (has-sub? e "jrec-field-at") #f)) (if (= fails 0) (begin (printf "fieldread gate: ~a/~a passed\n" total total) (exit 0)) (begin (printf "fieldread gate: ~a/~a passed (~a failed)\n" (- total fails) total fails) (exit 1)))