I finally moved one of my clojure projects from 1.10.0-RC1 to 1.10.0 and
encountered some very strange behaviour. Code that had previously compiled
fine under 1.8.0, 1.9.0, and 1.10.0-RC1 no longer compiled under 1.10.0 with
the following error:
Execution error (IllegalArgumentException) at bug.core$eval7278/
(form-init3641919424619236070.clj:49).
No matching ctor found for class clojure.reflect$typesym$fn__11912
Basically, symbols used inside of macros that had the new datafy protocol added
to their metadata caused this failure.
My use case that lead my to having this issue in the first place:
- I use macros in my project to help wrap Java APIs, or convert Java objects
to/from clojure data structures.
- In these macros I often use clojure.reflect to aid in adding Java type tags
to the code I am generating.
- New in 1.10.0, the symbols you get back from clojure.reflect now contain a
datafy protocol implementation in their metadata.
(Note that in 1.10.0-RC1 there was no datafy protocol implementation
returned in the metadata)
- The strange exception above occurs when the returned symbol from
clojure.reflect is used within a macro
I narrowed down a minimalist test case to help illustrate what I am saying:
(require 'clojure.reflect)
;; returns the symbol int, which now in 1.10.0 includes a datafy protocol
implementation in the metadata of the returned symbol
(defn example-reflect-type-return []
(->>
java.lang.String
(clojure.reflect/reflect)
(:members)
(filter #(= (:name %) 'length))
(first)
(:return-type)))
;; this macro fails on 1.10.0, but works in 1.10.0-RC1, 1.9.0, and 1.8.0
(defmacro has-the-bug []
`{:should-have-int-val '~(example-reflect-type-return)})
;; this macro works in 1.10.0, 1.9.0, and 1.8.0
;; (Basically I had to manually remove the datafy protocol implementation from
the symbol's metadata)
(defmacro this-works []
`{:should-have-int-val '~(with-meta
(example-reflect-type-return)
nil)})
Works under 1.10.0, 1.9.0, and 1.8.0:
(this-works) => {:should-have-int-val int}
Fails /only/ under 1.10.0:
(has-the-bug)
Failure error:
#error {
:cause "No matching ctor found for class clojure.reflect$typesym$fn__11912"
:via
[{:type java.lang.ExceptionInInitializerError
:message nil
:at [jdk.internal.reflect.NativeConstructorAccessorImpl newInstance0
"NativeConstructorAccessorImpl.java" -2]}
{:type java.lang.IllegalArgumentException
:message "No matching ctor found for class clojure.reflect$typesym$fn__11912"
:at [clojure.lang.Reflector invokeConstructor "Reflector.java" 288]}]
:trace
[[clojure.lang.Reflector invokeConstructor "Reflector.java" 288]
[clojure.lang.LispReader$EvalReader invoke "LispReader.java" 1317]
[clojure.lang.LispReader$DispatchReader invoke "LispReader.java" 853]
[clojure.lang.LispReader read "LispReader.java" 285]
[clojure.lang.LispReader read "LispReader.java" 216]
[clojure.lang.LispReader read "LispReader.java" 205]
[clojure.lang.RT readString "RT.java" 1874]
[clojure.lang.RT readString "RT.java" 1869]
[bug.core$eval7278 "form-init3641919424619236070.clj" 49]
[jdk.internal.reflect.NativeConstructorAccessorImpl newInstance0
"NativeConstructorAccessorImpl.java" -2]
[jdk.internal.reflect.NativeConstructorAccessorImpl newInstance
"NativeConstructorAccessorImpl.java" 62]
[jdk.internal.reflect.DelegatingConstructorAccessorImpl newInstance
"DelegatingConstructorAccessorImpl.java" 45]
[java.lang.reflect.Constructor newInstance "Constructor.java" 488]
[java.lang.Class newInstance "Class.java" 560]
[clojure.lang.Compiler$ObjExpr eval "Compiler.java" 4996]
[clojure.lang.Compiler eval "Compiler.java" 7175]
[clojure.lang.Compiler eval "Compiler.java" 7131]
[clojure.core$eval invokeStatic "core.clj" 3214]
[clojure.core$eval invoke "core.clj" 3210]
[clojure.main$repl$read_eval_print__9068$fn__9071 invoke "main.clj" 414]
[clojure.main$repl$read_eval_print__9068 invoke "main.clj" 414]
[clojure.main$repl$fn__9077 invoke "main.clj" 435]
[clojure.main$repl invokeStatic "main.clj" 435]
[clojure.main$repl doInvoke "main.clj" 345]
[clojure.lang.RestFn invoke "RestFn.java" 1523]
[clojure.tools.nrepl.middleware.interruptible_eval$evaluate$fn__1115 invoke
"interruptible_eval.clj" 87]
[clojure.lang.AFn applyToHelper "AFn.java" 152]
[clojure.lang.AFn applyTo "AFn.java" 144]
[clojure.core$apply invokeStatic "core.clj" 665]
[clojure.core$with_bindings_STAR_ invokeStatic "core.clj" 1973]
[clojure.core$with_bindings_STAR_ doInvoke "core.clj" 1973]
[clojure.lang.RestFn invoke "RestFn.java" 425]
[clojure.tools.nrepl.middleware.interruptible_eval$evaluate invokeStatic
"interruptible_eval.clj" 85]
[clojure.tools.nrepl.middleware.interruptible_eval$evaluate invoke
"interruptible_eval.clj" 55]
[clojure.tools.nrepl.middleware.interruptible_eval$interruptible_eval$fn__1160$fn__1163
invoke "interruptible_eval.clj" 222