2024年 Clojure 工作状态调查!中分享您的想法。

欢迎!请参阅关于页面了解一些有关如何运作的信息。

+1 评分

关闭

当使用哈希函数的情况语句发生哈希冲突时,它们会嵌入一个`condp`表达式。然而,这个表达式直接插入常量,而不进行引号引用。如果任何常量中包含一个符号,则这些会导致错误。

例如

(case 'a #{a} 1 :foo 2 a 3)

导致:`无法在当前上下文中解析符号:a`

这可以通过展开宏来看到

(macroexpand '(case 'a #{a} 1 :foo 2 a 3))
(let* [G__151 (quote a)] (case* G__151 0 1 (throw (java.lang.IllegalArgumentException. (clojure.core/str "No matching clause: " G__151))) {0 [-1640525200 (clojure.core/condp clojure.core/= G__151 #{a} 1 a 3 (throw (java.lang.IllegalArgumentException. (clojure.core/str "No matching clause: " G__151))))], 1 [:foo 2]} :compact :hash-equiv #{0}))

这显示了`case*`的调用。第一个和第二个参数是0,1。当将这些参数与`clojure.core/shift-mask`结合使用时,`'#{a}`和`'a'`都将哈希到`0`。

第五个参数包含一个哈希码到值的映射,其中包含

{0 [-1640525200 (clojure.core/condp clojure.core/= G__151
                  #{a} 1
                  a 3
                  (throw (java.lang.IllegalArgumentException. (clojure.core/str "No matching clause: " G__151))))],
 1 [:foo 2]}

这个`condp`块为`#{a}`和`a`包含未引用的值,因此它们将尝试错误地与当前绑定值(如果存在)匹配。

由于`case`将每个测试常量解释为文本(明确表示它们不需要加引号),因此这些值在嵌入的Clojure表达式(如这里的`condp`)中应该加引号。

以下是一个建议的修补程序以修正生成的代码

diff --git a/src/clj/clojure/core.clj b/src/clj/clojure/core.clj
index 0dba6fcd..b21d4556 100644
--- a/src/clj/clojure/core.clj
+++ b/src/clj/clojure/core.clj
@@ -6690,7 +6690,7 @@ fails, attempts to require sym's namespace and retries."
                       (next ks) (next vs))
                     m))
         assoc-multi (fn [m h bucket]
-                      (let [testexprs (apply concat bucket)
+                      (let [testexprs (mapcat (fn [kv] [(list 'quote (first kv)) (second kv)]) bucket)
                             expr `(condp = ~expr-sym ~@testexprs ~default)]
                         (assoc m h expr)))
         hmap (reduce1
diff --git a/test/clojure/test_clojure/control.clj b/test/clojure/test_clojure/control.clj
index 92846ad3..f3fe436b 100644
--- a/test/clojure/test_clojure/control.clj
+++ b/test/clojure/test_clojure/control.clj
@@ -421,7 +421,14 @@
          :b 1
          :c -2
          :d 4294967296
-         :d 3))
+         :d 3)
+    (are [result input] (= result (case input
+                                    #{a} :set
+                                    :foo :keyword
+                                    a :symbol))
+         :symbol 'a
+         :keyword :foo
+         :set '#{a}))
   (testing "test warn for hash collision"
     (should-print-err-message
      #"Performance warning, .*:\d+ - hash collision of some case test constants; if selected, those entries will be tested sequentially..*\r?\n"
已标记为关闭,备注:自1.11.0-alpha4发布

1 个回答

0 评分
by

https://clojure.atlassian.net/browse/CLJ-2679 创建了一个新工单

by
谢谢Fogus!
这个补丁看起来怎么样?(如果不是,那么我很感兴趣了解)
...