2024 Clojure 状态调查中分享您的看法!

欢迎!请在关于页面查看有关如何使用此页面的更多信息。

+1
编译器

当您给如下变量进行类型注解时

(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())
据我所知,原始类型注解仅在局部绑定中起作用。不支持从变量类型注解中推断它们。我不知道这一点在哪里有文档记录,所以仍然有益于挖掘。
找到了,已作为答案发布

2 个回答

+1
 

文档中只说支持"局部上下文"进行原始类型提示

https://clojure.org/reference/java_interop#primitives

对于原始类型提示,不支持var(返回类型)上的标签。

+1
 

无论这里的类型提示如何,vars始终是对象,在示例用法中它将是一个装箱值(因此不是原始值)。但是,如果标记为^:const和一个原始类型,编译器将在let中将var内联为一个原始类型long,在这种情况下它将是原始类型。

(set! *unchecked-math* :warn-on-boxed)

(def ^{:tag 'long :const true} k 100)

(let [i k] (+ i 10)))

不会警告。:const通常使用不当,但这是其很好的用途。

...