W14: pin crit-2 signal-return kont non-vacuously (test-only)
crit-2's failure mode discards every frame outside the signal site —
including the covering test's own assert — which is why the shipped test
"signal returns handler value to call site" passed vacuously pre-fix. A
plain assert pin would inherit that vacuity on regression.
Add suite gate-crit2-signal-return-kont with a side-effect sentinel: test 1
runs the core.md repros ((list "outer" (handler-bind ... (+ 1
(signal-condition 5))) "end") -> ("outer" 43 "end"); raise-continuable ->
143) then set!s a top-level flag; test 2 independently asserts the flag, so
a dropped continuation fails loudly even though test 1 would "pass". Third
test pins the shipped-test expression (51). 267 passed / 0 failed under
OCaml run_tests.
Test-only: no semantics edits, no push.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -161,3 +161,59 @@
|
||||
(quote (embed))
|
||||
(quote (param))
|
||||
(quote (track))))))
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; crit-2 [W1, critical] signal-return frame stored the saved kont under :f
|
||||
;; but the reader looked up "saved-kont" — the resume kont was always nil,
|
||||
;; so the handler value became the WHOLE program's result and every frame
|
||||
;; outside the signal site (including the covering test's own assert!) was
|
||||
;; silently discarded. The shipped test "signal returns handler value to
|
||||
;; call site" passed VACUOUSLY — the bug defeated its own test.
|
||||
;;
|
||||
;; A plain assert around the repro would inherit the same vacuity on
|
||||
;; regression (the dropped continuation includes the assert frame). So this
|
||||
;; pin uses a side-effect sentinel: test 1 runs the repro and then sets a
|
||||
;; flag; test 2 independently asserts the flag was reached. If crit-2
|
||||
;; regresses, test 1 still "passes" (vacuously) but test 2 FAILS.
|
||||
;; Repro (core.md).
|
||||
;; --------------------------------------------------------------------------
|
||||
(define *gate-crit2-after-signal* false)
|
||||
(define *gate-crit2-result* nil)
|
||||
(define *gate-crit2-rc-result* nil)
|
||||
|
||||
(defsuite
|
||||
"gate-crit2-signal-return-kont"
|
||||
(deftest
|
||||
"continuable signal resumes at the raise site"
|
||||
(do
|
||||
(set!
|
||||
*gate-crit2-result*
|
||||
(list
|
||||
"outer"
|
||||
(handler-bind
|
||||
(((fn (c) true) (fn (c) 42)))
|
||||
(+ 1 (signal-condition 5)))
|
||||
"end"))
|
||||
(set!
|
||||
*gate-crit2-rc-result*
|
||||
(handler-bind
|
||||
(((fn (c) true) (fn (c) (+ c 100))))
|
||||
(+ 1 (raise-continuable 42))))
|
||||
(set! *gate-crit2-after-signal* true)
|
||||
(assert= *gate-crit2-result* (list "outer" 43 "end"))
|
||||
(assert= *gate-crit2-rc-result* 143)))
|
||||
(deftest
|
||||
"non-vacuity sentinel: the continuation after the signal actually ran"
|
||||
(do
|
||||
(assert
|
||||
*gate-crit2-after-signal*
|
||||
"continuation dropped — crit-2 regressed (previous test passed vacuously)")
|
||||
(assert= *gate-crit2-result* (list "outer" 43 "end"))
|
||||
(assert= *gate-crit2-rc-result* 143)))
|
||||
(deftest
|
||||
"handler value feeds the arithmetic frame, not the program result"
|
||||
(assert=
|
||||
(handler-bind
|
||||
(((fn (c) true) (fn (c) (* c 10))))
|
||||
(+ 1 (signal-condition 5)))
|
||||
51)))
|
||||
|
||||
Reference in New Issue
Block a user