SBCL中的"explicit-check"声明标识符是什么意思?

What is the meaning of the "explicit-check" declaration identifier in SBCL?

在 SBCL 的 y-or-n-p 的源代码中,我看到 (declare (explicit-check)):

(defun y-or-n-p (&optional format-string &rest arguments)
  "..."
  (declare (explicit-check))
  (flet ((print-query ()
           (apply #'maybe-print-query "(y or n)" format-string arguments)))
    (loop (print-query)
          (case (query-read-char)
            ((#\y #\Y) (return t))
            ((#\n #\N) (return nil))
            (t (clarify-legal-query-input "y" "n"))))))

explicit-check 是做什么的?它未在 HyperSpec, so it is probably implementation-defined. However, I don't see any mention of explicit-check in the SBCL manual.

中列为标准声明标识符

根据代码中的以下来源,似乎 explicit-check 表示与函数参数关联的类型,如 FTYPE 声明所声明的那样,不会自动检查,而是显式检查(手动)。

这避免了当某些函数 f 将一个参数 a 的类型分派给签名由 FTYPE 声明的专用函数 f_a 时进行冗余检查。如果 f_a 中没有 explicit-checka 的类型将被检查两次,一次是在类型分配期间,一次是在进入函数时。

  • src/compiler/ir1-translators.lisp

    ;;; Check a new global function definition for consistency with
    ;;; previous declaration or definition, and assert argument/result
    ;;; types if appropriate. This assertion is suppressed by the
    ;;; EXPLICIT-CHECK attribute, which is specified on functions that
    ;;; check their argument types as a consequence of type dispatching.
    ;;; This avoids redundant checks such as NUMBERP on the args to +, etc.
    ;;; FIXME: this seems to have nothing at all to do with adding "new"
    ;;; definitions, as it is only called from IR1-CONVERT-INLINE-EXPANSION.
    
  • src/compiler/ctype.lisp

    (warn "Explicit-check without known FTYPE is meaningless")
    
  • package-data-list.lisp-expr

    ;; Advice to the compiler that it doesn't need to assert types.
    "EXPLICIT-CHECK"
    
  • src/compiler/ir1tran.lisp

    ;; EXPLICIT-CHECK by itself specifies that all argument and
    ;; result types are checked by the function body.
    

y-or-n-p 的上下文中,目的是只检查一次类型。它可以尽早完成,例如在 y-or-n-p 的开头,这将调用不检查其类型的“不安全”函数,但这里不是这种情况。

相反,该函数是用 defknown 定义的,它执行以下代码:

(setf (info :function :type name) type-to-store)

(参见 src/compiler/knownfun.lisp

如果我没记错的话,这与为函数声明 FTYPE 的效果相同(ftypedefknown 都设置了此信息槽)。

但是,这里 y-or-n-p 不需要检查类型本身,因为它主要委托给另一个函数,即 maybe-print-query。该函数调用 format,它也被声明为 explicit-check.

它做的第一件事是 etypecheck destination 参数,以便将对 format 的调用分派给 %format 的不同调用,这in turn 也会根据下一个参数的类型(控制字符串格式化函数)分支到不同的结果。

所以在实践中,检查参数的类型与调度交织在一起,因为有很多极端情况需要考虑。

首先检查输入类型然后传递给不安全函数的自上而下的方法也需要执行一些复杂的类型检查步骤,然后它仍然需要根据参数类型进行分派。这可能就是为什么类型检查会延迟到代码到达不同的特殊情况时才进行的原因。