请在 2024年Clojure状态调查 中分享您的想法!

欢迎!请查阅 关于 页面以了解有关如何工作的更多信息。

+1
编译器

由Ambrose Bonnaire-Sergeant (@ambrosebs)发现。

给定这个错综复杂的示例

(let [f +
      p (promise)
      d (delay (f) @@p)]
 (deliver p d)
 @d)

执行时,它失败

1. Unhandled java.lang.NullPointerException
   Cannot invoke "clojure.lang.IFn.invoke()" because "this.f" is null

这两种情况下都会发生相同的错误

(let [f +
      p (promise)
      d (lazy-seq (f) (first @p))]
  (deliver p d)
  (first d))

这两种情况的原因是,局部变量清理在与它首次调用之后立即将 this.f = null; 赋值给,所以当递归调用再次达到 (f) 时,引用已经为nil。

(binding [*compiler-options* {:disable-locals-clearing false}]
  (clj-java-decompiler.core/decompile
   (defn repro []
     (let [f +
        p (promise)
        d (delay (f) @@p)]
    (deliver p d)
    @d))))

=>

@Override
public Object invoke() {
    final Object f = this.f;
    this.f = null;
    ((IFn)f).invoke();
    final IFn fn = (IFn)__deref.getRawRoot();
    final IFn fn2 = (IFn)__deref.getRawRoot();
    final Object p = this.p;
    this.p = null;
    final Object invoke = fn2.invoke(p);
    this = null;
    return fn.invoke(invoke);
}

这是错误还是未定义的行为?

1 答案

+2

在我看来,这似乎是一个错误。已记录为 https://clojure.atlassian.net/browse/CLJ-2861

...