持久队列对象在编译器中没有遵循正确的评估路径。
最简单的情况
user=> (def q (conj clojure.lang.PersistentQueue/EMPTY 1 2 3))
'user/q
user=> q
user=> (eval q)
CompilerException java.lang.ClassCastException: clojure.lang.PersistentQueue cannot be cast to java.util.List, compiling:(NO_SOURCE_PATH:4:1)
当嵌入持久队列时,也会遇到相同的异常
user=> (eval `(fn (link: ) ~q))
CompilerException java.lang.ClassCastException: clojure.lang.PersistentQueue cannot be cast to java.util.List, compiling:(NO_SOURCE_PATH:2:1)
Expected result 而不是
CompilerException java.lang.RuntimeException: Can't embed unreadable object in code: #, compiling:(NO_SOURCE_PATH:3:1)
由于 PersistentQueue 实现了 IPersistentCollection 和 IPersistentList,并且编译器中未明确提及,因此它陷入了与列表相同的编译路径。异常来自 FnExpr 中 emitValue 部分的 emitConstants 调用。PersistentQueue 未实现 java.util.List,因此在 emitListAsObjectArray (Compiler.java:4479) 中抛出异常。但是,实现 List 并不能解决这个问题,而是通过将所有评估过的持久队列编译为持久列表来遮掩这个问题。
第一种情况通过向 Compiler.eval() 的 IPersistentCollection 分支添加 && !(form instanceof PersistentQueue)
得到解决,允许持久队列自动降至 analyze (Compiler.java:6459) 中的 ConstantExpr 情况。嵌入情况通过向 ObjExpr 的 emitValue 的 IPersistentList 分支添加 && !(value instanceof PersistentQueue)
得到解决。
此错误还阻止了为 PersistentQueue 定义词法读取器,因为在将读取的对象传递到编译器时抛出了异常。
提供的补丁包含了上述两个更改,并对每个情况进行了测试,以说明该错误。
Clojure-dev 线程:https://groups.google.com/forum/#!topic/clojure-dev/LDUQfqjFg9w