当您给如下变量进行类型注解时
(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,这会导致编译器认为它没有,并且类型无法推断。
我不太确定在编译器中的哪个位置,但我在下面的位置找到了它: https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/Compiler.java#L1341
我们可以看到编译器首先检查了 .hasJavaClass
if(e instanceof MaybePrimitiveExpr && e.hasJavaClass() && ((MaybePrimitiveExpr)e).canEmitPrimitive())