当你对一个如下的变量进行类型标注
(def ^{:tag 'long} k 100)
并在一个let
绑定中引用它
(let [i k]
(+ i 10))
这个绑定无法正确推断它是一个原始类型,尽管它在类型提示中是这样的
(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).
这看起来可能是由于编译器中的一个错误引起的,在这个错误中,由于某种原因,变量k的表达式被标记为不拥有Java类,尽管它确实拥有一个
(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}}
正如我们所看到的,.hasJavaClass
返回false,而.getJavaClass
返回Long/TYPE
类。
我怀疑编译器在调用.getJavaClass
之前会先检查.hasJavaClass
,由于.hasJavaClass
在这里错误地返回false,它会导致编译器认为它没有Java类,而且无法推断类型。
我不是很清楚这个问题在编译器中的具体位置,但我找到了以下信息:https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/Compiler.java#L1341
我们在这里可以看到编译器首先检查.hasJavaClass
。
if(e instanceof MaybePrimitiveExpr && e.hasJavaClass() && ((MaybePrimitiveExpr)e).canEmitPrimitive())