When you type hint a var such as:
(def ^{:tag 'long} k 100)
And refer to it in a let
binding:
(let [i k]
(+ i 10))
The binding is unable to properly infer that it is a primitive type as it was hinted:
(set! *unchecked-math* :warn-on-boxed)
(def ^{:tag 'long} k 100)
(let [i k]
(+ i 10))
;;=> Boxed math warning, unchecked_add(java.lang.Object,long).
It looks like it might be caused by a bug in the Compiler where for some reason, the expression of the var k is marked as not having a java class, even though it has one:
(defmacro inspect-local []
(println
(into {}
(map (fn[[k v]]
[k {:tag (.-tag v)
:class (.getJavaClass v)
:primitive? (.isPrimitive (.getJavaClass v))
:has-java-class? (.hasJavaClass v)}]))
&env)))
(let [i k]
(inspect-local)
(+ i 10))
;;=> {i {:tag nil, :class long, :primitive? true, :has-java-class? false}}
As we can see, .hasJavaClass
returns false, but .getJavaClass
returns the Long/TYPE
class.
I suspect that the compiler checks for .hasJavaClass
before calling .getJavaClass
, and since .hasJavaClass
is erroneously false here, it would cause the compiler to think it isn't, and that the type can't be inferred.
I'm not too sure where in the Compiler this is, but I found the following: https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/Compiler.java#L1341
Where we see the compiler is first checking for .hasJavaClass
:
if(e instanceof MaybePrimitiveExpr && e.hasJavaClass() && ((MaybePrimitiveExpr)e).canEmitPrimitive())