From 1804b3011ec8d63235f69e63f75a36da3f5c7aba Mon Sep 17 00:00:00 2001 From: Yogthos Date: Tue, 9 Jun 2026 00:04:11 -0400 Subject: [PATCH] compiler: compile set?/disj as plain fns (close the last Stage-1 fallback gap) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- src/jolt/compiler.janet | 1 - src/jolt/evaluator.janet | 9 ++------- src/jolt/host_iface.janet | 2 +- test/integration/conformance-test.janet | 6 ++++++ test/integration/fallback-zero-test.janet | 6 ++++-- 5 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/jolt/compiler.janet b/src/jolt/compiler.janet index d700472..abd27ca 100644 --- a/src/jolt/compiler.janet +++ b/src/jolt/compiler.janet @@ -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" diff --git a/src/jolt/evaluator.janet b/src/jolt/evaluator.janet index 1fa689e..32ce62c 100644 --- a/src/jolt/evaluator.janet +++ b/src/jolt/evaluator.janet @@ -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)) diff --git a/src/jolt/host_iface.janet b/src/jolt/host_iface.janet index ff1b37e..d2dbd9e 100644 --- a/src/jolt/host_iface.janet +++ b/src/jolt/host_iface.janet @@ -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) diff --git a/test/integration/conformance-test.janet b/test/integration/conformance-test.janet index 368d387..f400793 100644 --- a/test/integration/conformance-test.janet +++ b/test/integration/conformance-test.janet @@ -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])"] diff --git a/test/integration/fallback-zero-test.janet b/test/integration/fallback-zero-test.janet index 2d530a4..c5bcc50 100644 --- a/test/integration/fallback-zero-test.janet +++ b/test/integration/fallback-zero-test.janet @@ -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