compiler: compile set?/disj as plain fns (close the last Stage-1 fallback gap)

Stage 1 jolt-g3h. set? and disj were special-cased in all three "can't compile"
lists (host_iface special-names, compiler.janet uncompilable-heads, evaluator
special-symbol? + handlers) — but they're pure value-production with callable
core vars (core-set?/core-disj), and those vars are byte-for-byte equivalent to
the evaluator handlers. Removed them from all three lists + dropped the now-dead
evaluator handler arms, so they're ordinary clojure.core fns everywhere: the
analyzer compiles (set? x)/(disj s x) as normal var calls instead of punting to
the interpreter.

Verified identical results in default AND JOLT_MUTABLE builds (no representation
sensitivity — sets are phs in both, unlike vector?/list? which collapse).

With this, the self-hosted analyzer's compile-path fallback set equals the frozen
intentional stateful set (Task 2) — it's now a strict superset of the bootstrap
compiler's compilable surface, so the Janet bootstrap is retireable (Stage 2).

fallback-zero: set?/disj moved to must-compile (31 now), set! into must-punt.
Gate: conformance 267x3 (+5 set?/disj cases), lazy-infinite 44/44, suite 4034/67,
fixpoint, self-host, sci, specs+unit green.
This commit is contained in:
Yogthos 2026-06-09 00:04:11 -04:00
parent 3da5a5fcfa
commit 1804b3011e
5 changed files with 13 additions and 11 deletions

View file

@ -163,7 +163,6 @@
"find-ns" "all-ns" "the-ns" "find-var" "intern" "resolve"
"ns-resolve" "ns-aliases" "ns-imports" "ns-interns"
"alter-var-root" "alter-meta!" "reset-meta!" "locking" "new"
"disj" "set?"
# Definitional/host macros that mutate context or build runtime
# values the emitter doesn't model.
"defrecord" "defprotocol" "definterface" "reify" "proxy"

View file

@ -26,7 +26,6 @@
(= name "var-get") (= name "var-set") (= name "var?")
(= name "alter-var-root") (= name "find-var") (= name "intern")
(= name "alter-meta!") (= name "reset-meta!")
(= name "disj") (= name "set?")
(= name "satisfies?")
(= name "protocol-dispatch") (= name "register-method") (= name "make-reified")
(= name "prefer-method") (= name "remove-method") (= name "remove-all-methods")
@ -1142,12 +1141,8 @@
val (eval-form ctx bindings (in form 3))
ns (ctx-find-ns ctx (if (struct? ns-name) (ns-name :name) ns-name))]
(ns-intern ns (if (struct? sym-name) (sym-name :name) sym-name) val))
"disj" (let [s (eval-form ctx bindings (in form 1))
ks (map |(eval-form ctx bindings $) (tuple/slice form 2))]
(if (set? s)
(apply phs-disj s ks)
(error "disj expects a set")))
"set?" (set? (eval-form ctx bindings (in form 1)))
# set?/disj are plain clojure.core fns now (core-set?/core-disj) — no longer
# special-cased here, the analyzer, or compiler.janet (jolt-g3h).
"protocol-dispatch" (let [proto-sym (in form 1)
method-sym (in form 2)
obj (eval-form ctx bindings (in form 3))

View file

@ -74,7 +74,7 @@
"defmacro" "fn*" "let*" "loop*" "recur" "throw" "try" "set!" "var"
"locking" "eval" "instance?" "defmulti" "defmethod" "deftype" "new"
"." "var-get" "var-set" "var?" "alter-var-root" "find-var" "intern"
"alter-meta!" "reset-meta!" "disj" "set?" "satisfies?"
"alter-meta!" "reset-meta!" "satisfies?"
"protocol-dispatch" "register-method" "make-reified" "prefer-method"
"remove-method" "remove-all-methods" "get-method" "methods"
# ns-management forms dispatched by the interpreter (not core vars)

View file

@ -56,6 +56,12 @@
["empty set literal" "true" "(empty? #{})"]
["set literal count" "3" "(count #{1 2 3})"]
["set literal in let" "true" "(let [x 5] (= #{5 6} #{x (inc x)}))"]
# set?/disj compile as plain fns now (jolt-g3h), not special forms
["set? true" "true" "(set? #{1 2 3})"]
["set? false" "false" "(set? [1 2])"]
["disj one" "#{1 3}" "(disj #{1 2 3} 2)"]
["disj many" "#{1}" "(disj #{1 2 3} 2 3)"]
["disj absent" "#{1 2}" "(disj #{1 2} 5)"]
["keyword as fn" "1" "(:a {:a 1})"]
["map fn over coll" "(quote (1 3))" "(map {:a 1 :b 3} [:a :b])"]

View file

@ -40,14 +40,16 @@
"(try (inc 1) (catch :default e e))"
# def + calls into core
"(def answer 42)" "(map inc [1 2 3])" "(reduce + 0 [1 2 3])"
"(get {:a 1} :a)" "(vec (range 5))"])
"(get {:a 1} :a)" "(vec (range 5))"
# set?/disj are plain fns now, not special forms (jolt-g3h)
"(set? #{1 2})" "(disj #{1 2 3} 2)"])
# --- Intentional fallback (sanity sample): these SHOULD punt to the interpreter.
# Not the frozen set (that's Task 2) — just a few to confirm the boundary holds
# in the punt direction so the harness can't pass by compiling everything.
(def must-punt
["(ns foo.bar)" "(defmacro m [x] x)" "(require (quote [clojure.string]))"
"(set? #{1 2})" "(letfn [(f [n] (g n)) (g [n] (f n))] (f 1))"])
"(set! *warn-on-reflection* true)" "(letfn [(f [n] (g n)) (g [n] (f n))] (f 1))"])
(var fails @[])
(each s must-compile