compiler: interpreter fallback is deliberate-only (Stage 2 Task 3)
compile-and-eval wrapped the compile step in a blanket protect — any failure,
including a genuine compiler bug, silently fell back to the interpreter and
hid behind a correct-looking result. Now only the analyzer's deliberate punt
signal (jolt/uncompilable: …) falls back; any other compile-step error
propagates.
Tightening this surfaced one accidental dependency: pre-kernel overlay forms
(00-syntax's destructure defn) trigger a compile while ensure-analyzer is
still gated, and the missing analyzer crashed var-get with a cryptic indexing
error that the blanket catch happened to convert into the designed interpret
fallback. That path now punts explicitly ("jolt/uncompilable: analyzer not
built (pre-kernel bootstrap)").
New unit test stubs jolt.analyzer/analyze to prove a non-punt error
propagates while the punt channel still falls back.
Gate: conformance 269x3, fallback-zero, fixpoint stage1==2==3, self-host,
sci-bootstrap, staged-bootstrap, all specs+unit, suite 4046>=4034 (5 timeouts);
core-bench A/B neutral (4562 vs 4572 ms).
This commit is contained in:
parent
3e9fe8d0fb
commit
29fdd8ff7f
2 changed files with 61 additions and 4 deletions
41
test/unit/compile-fallback-test.janet
Normal file
41
test/unit/compile-fallback-test.janet
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
# Stage 2 Task 3: the compile path's interpreter fallback is DELIBERATE-ONLY.
|
||||
#
|
||||
# compile-and-eval used to wrap the compile step in a blanket protect — ANY
|
||||
# failure (including a genuine compiler bug) silently fell back to the
|
||||
# interpreter, hiding the bug behind a correct-looking result. Now only the
|
||||
# analyzer's deliberate punt signal ("jolt/uncompilable: …", raised for the
|
||||
# curated stateful/letrec set) may fall back; any other compile-step error
|
||||
# propagates. Verified here by stubbing jolt.analyzer/analyze.
|
||||
|
||||
(use ../../src/jolt/types)
|
||||
(use ../../src/jolt/api)
|
||||
(use ../../src/jolt/reader)
|
||||
(import ../../src/jolt/backend :as backend)
|
||||
|
||||
(def ctx (init))
|
||||
|
||||
# 1. A deliberate punt (letfn needs letrec IR) falls back and evaluates correctly.
|
||||
(assert (= 3 (backend/compile-and-eval ctx (parse-string "(letfn [(f [n] (+ n 1))] (f 2))")))
|
||||
"deliberate uncompilable punt falls back to the interpreter")
|
||||
|
||||
(assert (backend/analyzer-built? ctx) "analyzer built")
|
||||
(def analyze-var (ns-find (ctx-find-ns ctx "jolt.analyzer") "analyze"))
|
||||
(def real-analyze (var-get analyze-var))
|
||||
|
||||
# 2. A NON-punt compile error must propagate — even though the interpreter could
|
||||
# evaluate the form fine, it must NOT be silently used (that hides compiler bugs).
|
||||
(var-set analyze-var (fn [ctx form] (error "boom: simulated compiler bug")))
|
||||
(def r (protect (backend/compile-and-eval ctx (parse-string "(+ 1 2)"))))
|
||||
(assert (not (r 0)) "non-uncompilable compile error must propagate, not silently interpret")
|
||||
(assert (string/find "boom" (string (r 1))) "the original error is surfaced")
|
||||
|
||||
# 3. The punt marker is the one sanctioned fallback channel.
|
||||
(var-set analyze-var (fn [ctx form] (error "jolt/uncompilable: stubbed")))
|
||||
(assert (= 3 (backend/compile-and-eval ctx (parse-string "(+ 1 2)")))
|
||||
"uncompilable punt falls back to the interpreter")
|
||||
|
||||
# Restore the real analyzer and confirm the pipeline still works.
|
||||
(var-set analyze-var real-analyze)
|
||||
(assert (= 7 (backend/compile-and-eval ctx (parse-string "(+ 3 4)"))) "restored analyzer compiles")
|
||||
|
||||
(print "All compile-fallback tests passed!")
|
||||
Loading…
Add table
Add a link
Reference in a new issue