compiler: fix compile-mode correctness (conformance 87->218/218)
Running the conformance suite under compile mode surfaced many forms that silently miscompiled (the hybrid fallback only catches compile-time errors, not wrong results). Fixes: - Global var resolution now mirrors the interpreter's resolve-var: current ns (which holds refers) then clojure.core, instead of interning an empty var in the current ns. This was the big one — every core fn not in core-renames (iterate, update, subvec, reductions, ...) derefed to nil from a user ns. - Map literals evaluate their keys and values (were emitted as quoted data); build via build-map-literal, mirroring the interpreter (struct, or phm when a key is a collection). - Vector literals build a mode-appropriate jolt vector via make-vec (pvec when immutable, array when mutable) instead of a bare Janet tuple, so compiled and interpreted vectors share one representation (type-strict ops like rseq rejected tuples). - Core fn values resolve dynamically from the runtime env instead of a hand-maintained table that had drifted: core-apply mapped to Janet's native apply (rejects pvec tails), core-some mapped to core-some?. Removed the table and the bogus some rename. - analyze-form throws uncompilable on interpreter special forms it doesn't implement and on definitional/host macros (deftype, defprotocol, reify, binding, letfn, read-string, regex/tagged literals, ...), so they fall back to the interpreter instead of miscompiling — including nested in compiled forms. conformance-test.janet now runs every case under both interpreter and compiler so compile-mode correctness is guarded in CI.
This commit is contained in:
parent
acf724b638
commit
6297a65617
2 changed files with 137 additions and 132 deletions
|
|
@ -299,27 +299,38 @@
|
|||
["map literal in fn" "6" "(do (defn mk [a b] {:sum (+ a b)}) (:sum (mk 2 4)))"]
|
||||
])
|
||||
|
||||
(var pass 0)
|
||||
(def fails @[])
|
||||
(each [name expected actual] cases
|
||||
(def ctx (init))
|
||||
(def prog (string "(= " expected " " actual ")"))
|
||||
(def res (protect (eval-string ctx prog)))
|
||||
(cond
|
||||
(not= (res 0) true)
|
||||
(array/push fails [name "ERROR" (string (res 1))])
|
||||
(= (res 1) true)
|
||||
(++ pass)
|
||||
# not equal: re-eval actual alone to show what we got
|
||||
(let [got (protect (eval-string (init) actual))]
|
||||
(array/push fails [name "MISMATCH"
|
||||
(string "want=" expected
|
||||
" got=" (if (= (got 0) true) (string/format "%q" (got 1)) (string "ERR:" (got 1))))]))))
|
||||
# Run every case under a given context factory and return the failures. The same
|
||||
# cases run under both the interpreter and the compiler: results must match real
|
||||
# Clojure semantics either way, so the compile path (hybrid: hot compiles,
|
||||
# unsupported forms fall back to the interpreter) must not diverge.
|
||||
(defn- run-cases [opts]
|
||||
(def fails @[])
|
||||
(each [name expected actual] cases
|
||||
(def ctx (init opts))
|
||||
(def prog (string "(= " expected " " actual ")"))
|
||||
(def res (protect (eval-string ctx prog)))
|
||||
(cond
|
||||
(not= (res 0) true)
|
||||
(array/push fails [name "ERROR" (string (res 1))])
|
||||
(= (res 1) true)
|
||||
nil
|
||||
(let [got (protect (eval-string (init opts) actual))]
|
||||
(array/push fails [name "MISMATCH"
|
||||
(string "want=" expected
|
||||
" got=" (if (= (got 0) true) (string/format "%q" (got 1)) (string "ERR:" (got 1))))]))))
|
||||
fails)
|
||||
|
||||
(printf "\n=== CONFORMANCE: %d/%d passed ===" pass (length cases))
|
||||
(unless (empty? fails)
|
||||
(print "\n--- Failures ---")
|
||||
(each [name kind detail] fails
|
||||
(printf "[%s] %s: %s" kind name detail)))
|
||||
(defn- report [label fails]
|
||||
(printf "=== CONFORMANCE (%s): %d/%d passed ===" label (- (length cases) (length fails)) (length cases))
|
||||
(unless (empty? fails)
|
||||
(print "--- Failures ---")
|
||||
(each [name kind detail] fails
|
||||
(printf "[%s] %s: %s" kind name detail))))
|
||||
|
||||
(def interp-fails (run-cases {}))
|
||||
(report "interpret" interp-fails)
|
||||
(def compile-fails (run-cases {:compile? true}))
|
||||
(report "compile" compile-fails)
|
||||
(print)
|
||||
(when (pos? (length fails)) (os/exit 1))
|
||||
(when (or (pos? (length interp-fails)) (pos? (length compile-fails)))
|
||||
(os/exit 1))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue