Common Lisp 中的动态非本地退出

Dynamic non-local exits in Common Lisp

根据 Common Lisp the Language,第 2 版Dynamic Non-Local Exits 部分,

Intervening exits are abandoned as soon as the transfer of control is initiated; in the case of a throw, this occurs at the beginning of the "second pass" mentioned in the implementation note. It is an error to attempt a transfer of control to an exit whose dynamic extent has ended.

在 SBCL 1.3.4 中,但是:

(catch 'a
  (catch 'b
    (unwind-protect
         (throw 'a "returning from protected")
      (throw 'b "returning from unwind"))))
;; => "returning from unwind"

这似乎不正确。不应该在执行受保护和清理表单之间取消退出点 'b,从而使上述行为非法吗?同一页上的划船示例表明了相同的情况。

SBCL 的行为似乎与替代提案相对应:

...the abandoning of exits should be lumped in with the evaluation of unwind-protect cleanup clauses and the undoing of dynamic bindings and catch tags, performing all in reverse order of establishment.

这只是 CLtL2 与最终规格不同的问题吗?

正如 jkiiski 所解释的,该行为在 ANSI Common Lisp 中未定义。该示例在 CLHS 的 UNWIND-PROTECT 部分中引用:

;;; The following has undefined consequences because the catch of B is 
;;; passed over by the first THROW, hence portable programs must assume 
;;; its dynamic extent is terminated.  The binding of the catch tag is not
;;; yet disestablished and therefore it is the target of the second throw.
 (catch 'a
   (catch 'b
     (unwind-protect (throw 'a 1)
       (throw 'b 2))))