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