core.contracts: fully passes, two general fixes

clojure.core.contracts (over core.unify) now runs its whole suite on jolt —
14/14 across contracts/constraints/with-constraints/provide tests. Two general
gaps fixed:

- Symbol and Keyword now report IFn (and Fn/Runnable/Callable) in the modeled
  class hierarchy, so a (class x)-dispatched multimethod with an IFn method
  matches a symbol or keyword, like the JVM (both implement IFn — they're
  callable). core.contracts' funcify* dispatches on (class constraint) and a
  bare predicate symbol must hit the IFn arm. Runtime, no re-mint.
- A live Var value spliced into a form by a macro (defcurry-from resolves a var
  and emits (~v l r)) now compiles: analyze treats a var-cell form as a
  :the-var reference by ns+name, the same node as (var ns/name), mirroring the
  existing spliced-namespace (~*ns*) case. analyzer.clj + host-contract.ss,
  re-mint (prelude stays byte-identical; only the analyzer image changes).

Listed in docs/libraries.md + the site.

make test green (+2 corpus rows, 0 new divergences), shakesmoke byte-identical.
This commit is contained in:
Yogthos 2026-06-27 14:32:57 -04:00
parent 2c5b7dd918
commit a83ff6ce40
6 changed files with 27 additions and 3 deletions

View file

@ -54,6 +54,9 @@ e.g. the [ring-app example](https://github.com/jolt-lang/examples/tree/main/ring
(unification, `run`/`fresh`/`conde`, finite domains).
* [math.combinatorics](https://github.com/clojure/math.combinatorics) — permutations,
combinations, subsets, selections, cartesian products, partitions.
* [core.contracts](https://github.com/clojure/core.contracts) — programming by
contract (`contract`/`with-constraints`/`provide`), over
[core.unify](https://github.com/clojure/core.unify).
* [tick](https://github.com/juxt/tick) — date/time over Jolt's `java.time`;
`#time/…` literals via `time-literals`.
* [transit-jolt](https://github.com/jolt-lang/transit-jolt) — Transit (JSON) read/write

View file

@ -74,6 +74,11 @@
;; reconstruct it by name at the call site.
(define (hc-ns-value? x) (jns? x))
(define (hc-ns-value-name x) (jns-name x))
;; a live Var value spliced into a form (a macro that does `(~v …)` with v a
;; resolved var) — the analyzer turns it into a :the-var reference by ns+name.
(define (hc-var-value? x) (var-cell? x))
(define (hc-var-value-ns x) (var-cell-ns x))
(define (hc-var-value-name x) (var-cell-name x))
;; --- form accessors ---------------------------------------------------------
(define (hc-char-code x) (char->integer x)) ; native Chez char -> codepoint
@ -463,6 +468,9 @@
(def-var! "jolt.host" "form-uuid?" hc-uuid?)
(def-var! "jolt.host" "form-ns-value?" hc-ns-value?)
(def-var! "jolt.host" "form-ns-value-name" hc-ns-value-name)
(def-var! "jolt.host" "form-var-value?" hc-var-value?)
(def-var! "jolt.host" "form-var-value-ns" hc-var-value-ns)
(def-var! "jolt.host" "form-var-value-name" hc-var-value-name)
(def-var! "jolt.host" "form-bigdec?" hc-bigdec?)
(def-var! "jolt.host" "form-bigdec-source" hc-bigdec-source)
(def-var! "jolt.host" "form-elements" hc-elements)

View file

@ -916,8 +916,14 @@
;; class-keyed multimethod / (isa? (class x) SomeClass) dispatches like the JVM.
;; (Object is supplied universally by class-isa?, so it need not be listed.)
(reg-class-supers! "clojure.lang.IFn" '("clojure.lang.Fn" "java.lang.Runnable" "java.util.concurrent.Callable"))
(reg-class-supers! "clojure.lang.Keyword" '("clojure.lang.Named" "java.lang.Comparable"))
(reg-class-supers! "clojure.lang.Symbol" '("clojure.lang.Named" "java.lang.Comparable"))
;; Keyword and Symbol implement IFn (they are callable: (:k m) / ('s m)), so a
;; (class x)-dispatched multimethod with an IFn method matches them, like the JVM.
(reg-class-supers! "clojure.lang.Keyword" '("clojure.lang.Named" "java.lang.Comparable"
"clojure.lang.IFn" "clojure.lang.Fn"
"java.lang.Runnable" "java.util.concurrent.Callable"))
(reg-class-supers! "clojure.lang.Symbol" '("clojure.lang.Named" "java.lang.Comparable"
"clojure.lang.IFn" "clojure.lang.Fn"
"java.lang.Runnable" "java.util.concurrent.Callable"))
(reg-class-supers! "java.lang.String" '("java.lang.CharSequence" "java.lang.Comparable"))
(reg-class-supers! "clojure.lang.PersistentHashSet" '("clojure.lang.APersistentSet" "clojure.lang.IPersistentSet" "clojure.lang.IPersistentCollection" "java.util.Set" "java.util.Collection" "java.lang.Iterable"))
(reg-class-supers! "clojure.lang.PersistentTreeSet" '("clojure.lang.APersistentSet" "clojure.lang.IPersistentSet" "clojure.lang.IPersistentCollection" "java.util.Set" "java.util.Collection" "java.lang.Iterable"))

View file

@ -125,7 +125,7 @@
(guard (e (#t #f))
(def-var-with-meta! "jolt.analyzer" "with-coll-meta" (letrec ((with-coll-meta (lambda (ctx form env node) (let fnrec8904 ((ctx ctx) (form form) (env env) (node node)) (let* ((m (jolt-invoke (var-deref "jolt.host" "form-coll-meta") form))) (if (jolt-nil? m) node (let* ((_a$8907 (var-deref "jolt.ir" "invoke")) (_a$8908 (jolt-invoke (var-deref "jolt.ir" "var-ref") "clojure.core" "with-meta")) (_a$8909 (let* ((_o$8905 node) (_o$8906 (jolt-invoke (var-deref "jolt.analyzer" "analyze") ctx m env))) (jolt-vector _o$8905 _o$8906)))) (jolt-invoke _a$8907 _a$8908 _a$8909)))))))) with-coll-meta) (let* ((_o$8910 (keyword #f "private")) (_o$8911 #t)) (jolt-hash-map _o$8910 _o$8911))))
(guard (e (#t #f))
(def-var! "jolt.analyzer" "analyze" (letrec ((analyze (case-lambda ((ctx form) (let fnrec8912 ((ctx ctx) (form form)) (analyze ctx form (jolt-invoke (var-deref "jolt.analyzer" "empty-env"))))) ((ctx form env) (let fnrec8913 ((ctx ctx) (form form) (env env)) (if (jolt-truthy? (jolt-invoke (var-deref "jolt.host" "form-literal?") form)) (jolt-invoke (var-deref "jolt.ir" "const") form) (if (jolt-truthy? (jolt-invoke (var-deref "jolt.host" "form-sym?") form)) (jolt-invoke (var-deref "jolt.analyzer" "analyze-symbol") ctx form env) (if (jolt-truthy? (jolt-invoke (var-deref "jolt.host" "form-vec?") form)) (jolt-invoke (var-deref "jolt.analyzer" "with-coll-meta") ctx form env (jolt-invoke (var-deref "jolt.ir" "vector-node") (let* ((_a$8915 (var-deref "clojure.core" "mapv")) (_a$8916 (lambda (p__99_) (let fnrec8914 ((p__99_ p__99_)) (analyze ctx p__99_ env)))) (_a$8917 (jolt-invoke (var-deref "jolt.host" "form-vec-items") form))) (jolt-invoke _a$8915 _a$8916 _a$8917)))) (if (jolt-truthy? (jolt-invoke (var-deref "jolt.host" "form-map?") form)) (jolt-invoke (var-deref "jolt.analyzer" "with-coll-meta") ctx form env (jolt-invoke (var-deref "jolt.ir" "map-node") (let* ((_a$8921 (var-deref "clojure.core" "mapv")) (_a$8922 (lambda (p) (let fnrec8918 ((p p)) (let* ((_o$8919 (analyze ctx (jolt-first p) env)) (_o$8920 (analyze ctx (jolt-invoke (var-deref "clojure.core" "second") p) env))) (jolt-vector _o$8919 _o$8920))))) (_a$8923 (jolt-invoke (var-deref "jolt.host" "form-map-pairs") form))) (jolt-invoke _a$8921 _a$8922 _a$8923)))) (if (jolt-truthy? (jolt-invoke (var-deref "jolt.host" "form-set?") form)) (jolt-invoke (var-deref "jolt.analyzer" "with-coll-meta") ctx form env (jolt-invoke (var-deref "jolt.ir" "set-node") (let* ((_a$8925 (var-deref "clojure.core" "mapv")) (_a$8926 (lambda (p__100_) (let fnrec8924 ((p__100_ p__100_)) (analyze ctx p__100_ env)))) (_a$8927 (jolt-invoke (var-deref "jolt.host" "form-set-items") form))) (jolt-invoke _a$8925 _a$8926 _a$8927)))) (if (jolt-truthy? (jolt-invoke (var-deref "jolt.host" "form-list?") form)) (jolt-invoke (var-deref "jolt.analyzer" "analyze-list") ctx form env) (if (jolt-truthy? (jolt-invoke (var-deref "jolt.host" "form-regex?") form)) (let* ((_o$8928 (keyword #f "op")) (_o$8929 (keyword #f "regex")) (_o$8930 (keyword #f "source")) (_o$8931 (jolt-invoke (var-deref "jolt.host" "form-regex-source") form))) (jolt-hash-map _o$8928 _o$8929 _o$8930 _o$8931)) (if (jolt-truthy? (jolt-invoke (var-deref "jolt.host" "form-inst?") form)) (let* ((_o$8932 (keyword #f "op")) (_o$8933 (keyword #f "inst")) (_o$8934 (keyword #f "source")) (_o$8935 (jolt-invoke (var-deref "jolt.host" "form-inst-source") form))) (jolt-hash-map _o$8932 _o$8933 _o$8934 _o$8935)) (if (jolt-truthy? (jolt-invoke (var-deref "jolt.host" "form-uuid?") form)) (let* ((_o$8936 (keyword #f "op")) (_o$8937 (keyword #f "uuid")) (_o$8938 (keyword #f "source")) (_o$8939 (jolt-invoke (var-deref "jolt.host" "form-uuid-source") form))) (jolt-hash-map _o$8936 _o$8937 _o$8938 _o$8939)) (if (jolt-truthy? (jolt-invoke (var-deref "jolt.host" "form-bigdec?") form)) (let* ((_o$8940 (keyword #f "op")) (_o$8941 (keyword #f "bigdec")) (_o$8942 (keyword #f "source")) (_o$8943 (jolt-invoke (var-deref "jolt.host" "form-bigdec-source") form))) (jolt-hash-map _o$8940 _o$8941 _o$8942 _o$8943)) (if (jolt-truthy? (jolt-invoke (var-deref "jolt.host" "form-ns-value?") form)) (let* ((_o$8944 (keyword #f "op")) (_o$8945 (keyword #f "the-ns")) (_o$8946 (keyword #f "name")) (_o$8947 (jolt-invoke (var-deref "jolt.host" "form-ns-value-name") form))) (jolt-hash-map _o$8944 _o$8945 _o$8946 _o$8947)) (if (jolt-truthy? (keyword #f "else")) (jolt-invoke (var-deref "jolt.analyzer" "uncompilable") "unsupported form") jolt-nil))))))))))))))))) analyze)))
(def-var! "jolt.analyzer" "analyze" (letrec ((analyze (case-lambda ((ctx form) (let fnrec8912 ((ctx ctx) (form form)) (analyze ctx form (jolt-invoke (var-deref "jolt.analyzer" "empty-env"))))) ((ctx form env) (let fnrec8913 ((ctx ctx) (form form) (env env)) (if (jolt-truthy? (jolt-invoke (var-deref "jolt.host" "form-literal?") form)) (jolt-invoke (var-deref "jolt.ir" "const") form) (if (jolt-truthy? (jolt-invoke (var-deref "jolt.host" "form-sym?") form)) (jolt-invoke (var-deref "jolt.analyzer" "analyze-symbol") ctx form env) (if (jolt-truthy? (jolt-invoke (var-deref "jolt.host" "form-vec?") form)) (jolt-invoke (var-deref "jolt.analyzer" "with-coll-meta") ctx form env (jolt-invoke (var-deref "jolt.ir" "vector-node") (let* ((_a$8915 (var-deref "clojure.core" "mapv")) (_a$8916 (lambda (p__99_) (let fnrec8914 ((p__99_ p__99_)) (analyze ctx p__99_ env)))) (_a$8917 (jolt-invoke (var-deref "jolt.host" "form-vec-items") form))) (jolt-invoke _a$8915 _a$8916 _a$8917)))) (if (jolt-truthy? (jolt-invoke (var-deref "jolt.host" "form-map?") form)) (jolt-invoke (var-deref "jolt.analyzer" "with-coll-meta") ctx form env (jolt-invoke (var-deref "jolt.ir" "map-node") (let* ((_a$8921 (var-deref "clojure.core" "mapv")) (_a$8922 (lambda (p) (let fnrec8918 ((p p)) (let* ((_o$8919 (analyze ctx (jolt-first p) env)) (_o$8920 (analyze ctx (jolt-invoke (var-deref "clojure.core" "second") p) env))) (jolt-vector _o$8919 _o$8920))))) (_a$8923 (jolt-invoke (var-deref "jolt.host" "form-map-pairs") form))) (jolt-invoke _a$8921 _a$8922 _a$8923)))) (if (jolt-truthy? (jolt-invoke (var-deref "jolt.host" "form-set?") form)) (jolt-invoke (var-deref "jolt.analyzer" "with-coll-meta") ctx form env (jolt-invoke (var-deref "jolt.ir" "set-node") (let* ((_a$8925 (var-deref "clojure.core" "mapv")) (_a$8926 (lambda (p__100_) (let fnrec8924 ((p__100_ p__100_)) (analyze ctx p__100_ env)))) (_a$8927 (jolt-invoke (var-deref "jolt.host" "form-set-items") form))) (jolt-invoke _a$8925 _a$8926 _a$8927)))) (if (jolt-truthy? (jolt-invoke (var-deref "jolt.host" "form-list?") form)) (jolt-invoke (var-deref "jolt.analyzer" "analyze-list") ctx form env) (if (jolt-truthy? (jolt-invoke (var-deref "jolt.host" "form-regex?") form)) (let* ((_o$8928 (keyword #f "op")) (_o$8929 (keyword #f "regex")) (_o$8930 (keyword #f "source")) (_o$8931 (jolt-invoke (var-deref "jolt.host" "form-regex-source") form))) (jolt-hash-map _o$8928 _o$8929 _o$8930 _o$8931)) (if (jolt-truthy? (jolt-invoke (var-deref "jolt.host" "form-inst?") form)) (let* ((_o$8932 (keyword #f "op")) (_o$8933 (keyword #f "inst")) (_o$8934 (keyword #f "source")) (_o$8935 (jolt-invoke (var-deref "jolt.host" "form-inst-source") form))) (jolt-hash-map _o$8932 _o$8933 _o$8934 _o$8935)) (if (jolt-truthy? (jolt-invoke (var-deref "jolt.host" "form-uuid?") form)) (let* ((_o$8936 (keyword #f "op")) (_o$8937 (keyword #f "uuid")) (_o$8938 (keyword #f "source")) (_o$8939 (jolt-invoke (var-deref "jolt.host" "form-uuid-source") form))) (jolt-hash-map _o$8936 _o$8937 _o$8938 _o$8939)) (if (jolt-truthy? (jolt-invoke (var-deref "jolt.host" "form-bigdec?") form)) (let* ((_o$8940 (keyword #f "op")) (_o$8941 (keyword #f "bigdec")) (_o$8942 (keyword #f "source")) (_o$8943 (jolt-invoke (var-deref "jolt.host" "form-bigdec-source") form))) (jolt-hash-map _o$8940 _o$8941 _o$8942 _o$8943)) (if (jolt-truthy? (jolt-invoke (var-deref "jolt.host" "form-ns-value?") form)) (let* ((_o$8944 (keyword #f "op")) (_o$8945 (keyword #f "the-ns")) (_o$8946 (keyword #f "name")) (_o$8947 (jolt-invoke (var-deref "jolt.host" "form-ns-value-name") form))) (jolt-hash-map _o$8944 _o$8945 _o$8946 _o$8947)) (if (jolt-truthy? (jolt-invoke (var-deref "jolt.host" "form-var-value?") form)) (let* ((_a$8948 (var-deref "jolt.ir" "the-var")) (_a$8949 (jolt-invoke (var-deref "jolt.host" "form-var-value-ns") form)) (_a$8950 (jolt-invoke (var-deref "jolt.host" "form-var-value-name") form))) (jolt-invoke _a$8948 _a$8949 _a$8950)) (if (jolt-truthy? (keyword #f "else")) (jolt-invoke (var-deref "jolt.analyzer" "uncompilable") "unsupported form") jolt-nil)))))))))))))))))) analyze)))
(guard (e (#t #f))
(def-var-with-meta! "jolt.backend-scheme" "native-ops" (let* ((_o$7153 "+") (_o$7154 "+") (_o$7155 "-") (_o$7156 "-") (_o$7157 "*") (_o$7158 "*") (_o$7159 "/") (_o$7160 "/") (_o$7161 "<") (_o$7162 "<") (_o$7163 ">") (_o$7164 ">") (_o$7165 "<=") (_o$7166 "<=") (_o$7167 ">=") (_o$7168 ">=") (_o$7169 "=") (_o$7170 "jolt=") (_o$7171 "inc") (_o$7172 "jolt-inc") (_o$7173 "dec") (_o$7174 "jolt-dec") (_o$7175 "not") (_o$7176 "jolt-not") (_o$7177 "min") (_o$7178 "min") (_o$7179 "max") (_o$7180 "max") (_o$7181 "mod") (_o$7182 "modulo") (_o$7183 "rem") (_o$7184 "remainder") (_o$7185 "quot") (_o$7186 "quotient") (_o$7187 "vector") (_o$7188 "jolt-vector") (_o$7189 "hash-map") (_o$7190 "jolt-hash-map-fn") (_o$7191 "hash-set") (_o$7192 "jolt-hash-set") (_o$7193 "conj") (_o$7194 "jolt-conj") (_o$7195 "get") (_o$7196 "jolt-get") (_o$7197 "nth") (_o$7198 "jolt-nth") (_o$7199 "count") (_o$7200 "jolt-count") (_o$7201 "assoc") (_o$7202 "jolt-assoc") (_o$7203 "dissoc") (_o$7204 "jolt-dissoc") (_o$7205 "contains?") (_o$7206 "jolt-contains?") (_o$7207 "empty?") (_o$7208 "jolt-empty?") (_o$7209 "peek") (_o$7210 "jolt-peek") (_o$7211 "pop") (_o$7212 "jolt-pop") (_o$7213 "first") (_o$7214 "jolt-first") (_o$7215 "rest") (_o$7216 "jolt-rest") (_o$7217 "next") (_o$7218 "jolt-next") (_o$7219 "seq") (_o$7220 "jolt-seq") (_o$7221 "cons") (_o$7222 "jolt-cons") (_o$7223 "list") (_o$7224 "jolt-list") (_o$7225 "reverse") (_o$7226 "jolt-reverse") (_o$7227 "last") (_o$7228 "jolt-last") (_o$7229 "map") (_o$7230 "jolt-map") (_o$7231 "filter") (_o$7232 "jolt-filter") (_o$7233 "remove") (_o$7234 "jolt-remove") (_o$7235 "reduce") (_o$7236 "jolt-reduce") (_o$7237 "into") (_o$7238 "jolt-into") (_o$7239 "concat") (_o$7240 "jolt-concat") (_o$7241 "apply") (_o$7242 "jolt-apply") (_o$7243 "range") (_o$7244 "jolt-range") (_o$7245 "take") (_o$7246 "jolt-take") (_o$7247 "drop") (_o$7248 "jolt-drop") (_o$7249 "keys") (_o$7250 "jolt-keys") (_o$7251 "vals") (_o$7252 "jolt-vals") (_o$7253 "even?") (_o$7254 "jolt-even?") (_o$7255 "odd?") (_o$7256 "jolt-odd?") (_o$7257 "pos?") (_o$7258 "jolt-pos?") (_o$7259 "neg?") (_o$7260 "jolt-neg?") (_o$7261 "zero?") (_o$7262 "jolt-zero?") (_o$7263 "identity") (_o$7264 "jolt-identity") (_o$7265 "nil?") (_o$7266 "jolt-nil?") (_o$7267 "some?") (_o$7268 "jolt-some?") (_o$7269 "ex-info") (_o$7270 "jolt-ex-info") (_o$7271 "protocol-dispatch1") (_o$7272 "protocol-dispatch1") (_o$7273 "protocol-dispatch2") (_o$7274 "protocol-dispatch2") (_o$7275 "protocol-dispatch3") (_o$7276 "protocol-dispatch3")) (jolt-hash-map _o$7153 _o$7154 _o$7155 _o$7156 _o$7157 _o$7158 _o$7159 _o$7160 _o$7161 _o$7162 _o$7163 _o$7164 _o$7165 _o$7166 _o$7167 _o$7168 _o$7169 _o$7170 _o$7171 _o$7172 _o$7173 _o$7174 _o$7175 _o$7176 _o$7177 _o$7178 _o$7179 _o$7180 _o$7181 _o$7182 _o$7183 _o$7184 _o$7185 _o$7186 _o$7187 _o$7188 _o$7189 _o$7190 _o$7191 _o$7192 _o$7193 _o$7194 _o$7195 _o$7196 _o$7197 _o$7198 _o$7199 _o$7200 _o$7201 _o$7202 _o$7203 _o$7204 _o$7205 _o$7206 _o$7207 _o$7208 _o$7209 _o$7210 _o$7211 _o$7212 _o$7213 _o$7214 _o$7215 _o$7216 _o$7217 _o$7218 _o$7219 _o$7220 _o$7221 _o$7222 _o$7223 _o$7224 _o$7225 _o$7226 _o$7227 _o$7228 _o$7229 _o$7230 _o$7231 _o$7232 _o$7233 _o$7234 _o$7235 _o$7236 _o$7237 _o$7238 _o$7239 _o$7240 _o$7241 _o$7242 _o$7243 _o$7244 _o$7245 _o$7246 _o$7247 _o$7248 _o$7249 _o$7250 _o$7251 _o$7252 _o$7253 _o$7254 _o$7255 _o$7256 _o$7257 _o$7258 _o$7259 _o$7260 _o$7261 _o$7262 _o$7263 _o$7264 _o$7265 _o$7266 _o$7267 _o$7268 _o$7269 _o$7270 _o$7271 _o$7272 _o$7273 _o$7274 _o$7275 _o$7276)) (let* ((_o$7277 (keyword #f "private")) (_o$7278 #t)) (jolt-hash-map _o$7277 _o$7278))))
(guard (e (#t #f))

View file

@ -25,6 +25,7 @@
form-inst? form-inst-source form-uuid? form-uuid-source
form-bigdec? form-bigdec-source
form-ns-value? form-ns-value-name
form-var-value? form-var-value-ns form-var-value-name
form-macro? form-expand-1 resolve-global
form-sym-meta form-coll-meta host-intern! form-syntax-quote-lower
record-type? record-ctor-key form-position late-bind?
@ -724,4 +725,8 @@
;; a live namespace value spliced into a form (~*ns* in a macro) -> a
;; :the-ns leaf the back end reconstructs by name at the call site.
(form-ns-value? form) {:op :the-ns :name (form-ns-value-name form)}
;; a live Var value spliced into a form (a macro that resolves a var and
;; splices it, e.g. core.contracts' defcurry-from) -> a :the-var reference,
;; same as (var ns/name); the back end emits (jolt-var ns name).
(form-var-value? form) (the-var (form-var-value-ns form) (form-var-value-name form))
:else (uncompilable "unsupported form"))))

View file

@ -3365,4 +3365,6 @@
{:suite "symbols / construction" :label "(symbol nil name) equals (symbol name)" :expected "true" :actual "(= (symbol nil \"foo\") (quote foo))"}
{:suite "symbols / construction" :label "(symbol nil name) has no namespace" :expected "true" :actual "(nil? (namespace (symbol nil \"x\")))"}
{:suite "host-interop / instance? hierarchy" :label "ExceptionInfo is a RuntimeException" :expected "[true true true]" :actual "[(instance? RuntimeException (ex-info \"x\" {})) (instance? Exception (ex-info \"x\" {})) (instance? Throwable (ex-info \"x\" {}))]"}
{:suite "host-interop / class hierarchy" :label "Symbol and Keyword are IFn" :expected "[true true]" :actual "[(isa? clojure.lang.Symbol clojure.lang.IFn) (isa? clojure.lang.Keyword clojure.lang.IFn)]"}
{:suite "host-interop / class hierarchy" :label "(class sym)-dispatched multimethod hits an IFn method" :expected ":ifn" :actual "(do (defmulti cm1 class) (defmethod cm1 clojure.lang.IFn [_] :ifn) (cm1 (quote sym)))"}
]