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

欢迎!请在关于页面了解更多关于此工作方式的信息。

+1
in Compiler by

由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) 时,引用已经为空。

(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
by

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

...