core: staged-bootstrap kernel tier; move second/peek/subvec/mapv/update to Clojure
First fractal turn of the multi-stage bootstrap (jolt-tzo). The Clojure part of clojure.core is now loaded in ordered tiers under jolt-core/clojure/core/. The kernel tier (00-kernel: second/peek/subvec/mapv/update) holds the structural fns the self-hosted compiler itself uses; in compile mode it is bootstrap-compiled into clojure.core BEFORE the analyzer is built, so the analyzer binds those names to the Clojure definitions instead of a not-yet-defined forward ref. That removes the circularity that previously forced these to stay in Janet — the five core-* primitives and their init-core! entries are gone. Mechanism: backend/bootstrap-load-source (generalized from compile-load) builds a source string into a target ns via the bootstrap; rebuild-compiler! recompiles the self-hosted compiler against the current core (the rail for future turns, exercised by the new fixpoint test). api/load-core-overlay! walks the ordered tiers, bootstrap-loading kernel tiers and self-hosting the rest. Also fixes a latent evaluator bug surfaced by the move: a fn rebinds current-ns to its defining ns while it runs and restores on normal return, but a fn that THREW unwound past its own restore, leaking the ns. try now restores the try-entry ns on the catch/finally path, so referred-symbol resolution survives a caught error (this was breaking is/testing in the suite harness after any thrown assertion). Net battery +3 (3913 -> 3916); conformance 218/218 in all three modes; binary builds and runs the embedded tiers.
This commit is contained in:
parent
a6f0f8a6fe
commit
28ef15ea4b
9 changed files with 233 additions and 117 deletions
|
|
@ -25,7 +25,11 @@
|
|||
# running thread (Janet OS threads can't be interrupted), so `(deref (future
|
||||
# (sleep 1)))` re-raises the unresolved-`Thread/sleep` error — a documented
|
||||
# platform gap, not a regression in any previously-working behavior.
|
||||
(def baseline-pass 3913)
|
||||
# Raised 3913 -> 3916 with the staged-bootstrap kernel tier: the evaluator now
|
||||
# restores current-ns when a fn throws across a try boundary (a leaked ns used to
|
||||
# break referred-symbol resolution after a caught error), and subvec's index
|
||||
# coercion (NaN/float/ratio) is faithful — net +3.
|
||||
(def baseline-pass 3916)
|
||||
# A file is "clean" when it ran with zero failures AND zero errors.
|
||||
(def baseline-clean-files 45)
|
||||
# Per-file wall-clock budget (seconds). Normal files finish in well under 1s;
|
||||
|
|
|
|||
63
test/integration/staged-bootstrap-test.janet
Normal file
63
test/integration/staged-bootstrap-test.janet
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
# Staged-bootstrap soundness (jolt-vcx, under epic jolt-tzo).
|
||||
#
|
||||
# The self-hosted compiler's structural deps — second/peek/subvec/mapv/update —
|
||||
# now come from the Clojure kernel tier (jolt-core/clojure/core/00-kernel.clj),
|
||||
# bootstrap-compiled into clojure.core BEFORE the analyzer is built. This pins
|
||||
# the two properties that make that safe:
|
||||
#
|
||||
# 1. Compile mode: the analyzer (which itself calls second/peek/subvec/mapv)
|
||||
# compiles analyzer-exercising forms correctly — the exact case that broke
|
||||
# when `second` was a plain overlay fn: (first {:a 1}) / (key (first ...)).
|
||||
# 2. Bootstrap FIXPOINT: rebuilding the compiler (rebuild-compiler!) against the
|
||||
# now Clojure-defined core still yields a correct compiler. This is the
|
||||
# soundness gate for every future fractal turn (S2 -> S3).
|
||||
|
||||
(use ../../src/jolt/api)
|
||||
(import ../../src/jolt/backend :as backend)
|
||||
|
||||
(var failures 0)
|
||||
|
||||
# Each probe is a jolt boolean expression; compared with jolt's own `=`.
|
||||
(def probes
|
||||
["(= 2 (second [1 2 3]))"
|
||||
"(= nil (second [1]))"
|
||||
"(= 3 (peek [1 2 3]))"
|
||||
"(= 1 (peek (list 1 2 3)))"
|
||||
"(= nil (peek []))"
|
||||
"(= [2 3] (subvec [1 2 3 4 5] 1 3))"
|
||||
"(= [3 4 5] (subvec [1 2 3 4 5] 2))"
|
||||
"(= [2 3 4] (mapv inc [1 2 3]))"
|
||||
"(= [11 22 33] (mapv + [1 2 3] [10 20 30]))"
|
||||
"(= {:a 2} (update {:a 1} :a inc))"
|
||||
"(= {:a 1 :b 1} (update {:a 1} :b (fnil inc 0)))"
|
||||
# Regression: these run the analyzer's own second/map-pair path in compile mode.
|
||||
"(= [:a 1] (first {:a 1}))"
|
||||
"(= :a (key (first {:a 1})))"
|
||||
"(= 1 (val (first {:a 1})))"
|
||||
"(= 3 (let [[a b] [1 2]] (+ a b)))"
|
||||
"(= 3 (loop [i 0 acc 0] (if (< i 3) (recur (inc i) (+ acc i)) acc)))"])
|
||||
|
||||
(defn- run-probes [ctx label]
|
||||
(each prog probes
|
||||
(def got (protect (eval-string ctx prog)))
|
||||
(unless (and (got 0) (= (got 1) true))
|
||||
(++ failures)
|
||||
(printf "FAIL [%s] %s => %s" label prog
|
||||
(if (got 0) (string/format "%q" (got 1)) (string "ERR:" (got 1)))))))
|
||||
|
||||
# Interpret mode: kernel tier interpreted, no analyzer involved.
|
||||
(run-probes (init {}) "interpret")
|
||||
|
||||
# Compile mode: kernel tier bootstrap-compiled, analyzer built against it.
|
||||
(def cctx (init {:compile? true}))
|
||||
(run-probes cctx "compile")
|
||||
|
||||
# Fixpoint: rebuild the compiler against the current (Clojure-defined) core and
|
||||
# re-run. A correct compiler recompiled on the language it just defined stays
|
||||
# correct.
|
||||
(backend/rebuild-compiler! cctx)
|
||||
(run-probes cctx "compile+rebuilt")
|
||||
|
||||
(if (pos? failures)
|
||||
(do (printf "staged-bootstrap: %d failure(s)" failures) (os/exit 1))
|
||||
(print "staged-bootstrap: all probes passed (interpret, compile, compile+rebuilt)"))
|
||||
Loading…
Add table
Add a link
Reference in a new issue