Files
rose-ash/spec/tests/test-gate-pins.sx
giles dafd0de8f2 W14: pin K09/K11/K39 landed W5 special-form fixes (test-only)
Three dc7aa709 fixes shipped without pinning tests:
- K09: R7RS longhand (unquote-splicing X) now splices (was silent zero-splice)
- K11: guard re-raise sentinel gensym'd — a user value shaped like
  (list '__guard-reraise__ X) is data, not a forged re-raise
- K39: (do ((fn (x) x) 5) 99) -> 99, not a misparsed Scheme do-loop

Add suites gate-K09-longhand-unquote-splicing, gate-K11-guard-reraise-forgeable,
gate-K39-do-iife-head to spec/tests/test-gate-pins.sx with exact reprs from
plans/sx-review/core.md. 261 passed / 0 failed under OCaml run_tests.

Test-only: no semantics edits, no push.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 23:19:48 +00:00

122 lines
4.9 KiB
Plaintext

;; ==========================================================================
;; test-gate-pins.sx — W14 regression pins for the review's landed fixes
;;
;; The quick-wins batch (commit dc7aa709 + siblings) landed real semantics
;; fixes but shipped WITHOUT pinning tests, so a regression would pass
;; silently. This file pins each confirmed-and-fixed finding with a minimal
;; repro lifted from the review lane files (plans/sx-review/*.md). One suite
;; per finding.
;;
;; TEST-ONLY: no semantics edits. If a pin fails, the fix regressed — do NOT
;; relax the assertion; investigate the evaluator/primitive change.
;; NB: assert= uses `=` (not `equal?`); compare lists with `=`.
;; ==========================================================================
;; --------------------------------------------------------------------------
;; K18 [W7, high] expt silently wrapped at 63-bit int — now promotes to float
;; like +/*. Repro (core.md): (expt 2 62) -> -4611686018427387904 (wrapped);
;; (expt 2 100) -> 0. Fixed: both are positive floats.
;; --------------------------------------------------------------------------
(defsuite
"gate-K18-expt-overflow"
(deftest
"small integer exponents stay exact"
(do
(assert= (expt 2 0) 1)
(assert= (expt 2 10) 1024)))
(deftest
"expt 2^62 does not wrap to a negative int"
(assert (> (expt 2 62) 0)))
(deftest
"expt 2^100 does not wrap to zero"
(assert (> (expt 2 100) 0)))
(deftest
"expt 2^100 promotes to float"
(assert (number? (expt 2 100)))))
;; --------------------------------------------------------------------------
;; K20 [W7, high] contains? did not support dicts in the real runtime —
;; (contains? {:a 1} :a) threw "contains?: 2 args", contradicting its :doc
;; ("Dicts: key check"). Fixed: dict key membership works; lists/strings
;; unchanged. Repro (core.md).
;; --------------------------------------------------------------------------
(defsuite
"gate-K20-contains-dict"
(deftest
"contains? finds a present dict key"
(assert (contains? {:a 1 :b 2} :a)))
(deftest
"contains? reports a missing dict key as false"
(assert (not (contains? {:a 1 :b 2} :zz))))
(deftest
"contains? still works on list membership"
(do
(assert (contains? (list 10 20 30) 20))
(assert
(not (contains? (list 10 20 30) 99)))))
(deftest
"contains? still works on string substrings"
(assert (contains? "hello" "ell"))))
;; --------------------------------------------------------------------------
;; K09 [W5, high] R7RS longhand (unquote-splicing X) silently no-spliced —
;; only shorthand ,@/`splice-unquote` was recognized, so the longhand
;; serialized literally (zero-splice). Fixed: aliased to splice-unquote.
;; Repro (core.md).
;; --------------------------------------------------------------------------
(defsuite
"gate-K09-longhand-unquote-splicing"
(deftest
"longhand unquote-splicing splices a list"
(assert=
(quasiquote
(1
(unquote-splicing (list 2 3))
4))
(list 1 2 3 4)))
(deftest
"longhand unquote-splicing of an empty list contributes nothing"
(assert=
(quasiquote (0 (unquote-splicing (list)) 9))
(list 0 9)))
(deftest
"shorthand splice-unquote still works"
(assert=
(quasiquote (a (splice-unquote (list 2 3)) z))
(list (quote a) 2 3 (quote z)))))
;; --------------------------------------------------------------------------
;; K11 [W5, high] guard re-raise sentinel was a plain forgeable symbol — a
;; body/clause legitimately returning (list '__guard-reraise__ X) was
;; misread as a re-raise of X. Fixed: sentinel gensym'd per execution, so a
;; user value with that head is returned as data. Repro (core.md).
;; --------------------------------------------------------------------------
(defsuite
"gate-K11-guard-reraise-forgeable"
(deftest
"body value shaped like the sentinel is returned as data"
(assert=
(guard (e (true "caught")) (list (quote __guard-reraise__) "hi"))
(list (quote __guard-reraise__) "hi")))
(deftest
"clause returning the forged sentinel is not re-raised"
(assert=
(guard
(e (true (list (quote __guard-reraise__) "forged")))
(error "boom"))
(list (quote __guard-reraise__) "forged"))))
;; --------------------------------------------------------------------------
;; K39 [W5, med] `do` misparsed a first form whose head is a list (an IIFE)
;; as a Scheme do-loop binding spec. Repro (core.md): (do ((fn (x) x) 5) 99)
;; threw "first: expected list, got 5"; expected 99. Fixed: `do` is begin.
;; --------------------------------------------------------------------------
(defsuite
"gate-K39-do-iife-head"
(deftest
"do with an IIFE first form returns the last form (not a do-loop)"
(assert= (do ((fn (x) x) 5) 99) 99))
(deftest
"do with a single IIFE form returns its value"
(assert= (do ((fn () 42))) 42)))