当你为如下变量类型进行注解
(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())