sbcl - 如何消除“未定义变量”警告?

5

我无法使用sb-ext:muffle-conditions完成它。我想做类似于以下的操作:

(declaim #+sbcl(sb-ext:muffle-conditions sb-kernel:redefinition-warning))

当然,除了重新定义之外,我希望能够消除“未定义变量”的警告。

如果有人知道这是哪个参数,或者有sb-ext:muffle-conditions的文档/各种选项的链接,请分享 :) 谢谢

1个回答

4

我不确定你是否能够特定地消除这种警告,至少通过类名来实现。通过追踪 warn,我们可以了解SBCL正在做什么。例如,在重新定义的情况下会发生什么:

* (trace warn)

(WARN)
* (defun foo () nil)

FOO
* (defun foo () nil)
  0: (WARN SB-KERNEL:REDEFINITION-WITH-DEFUN :NAME FOO :NEW-FUNCTION
           #<FUNCTION FOO {10041FA989}> :NEW-LOCATION
           #S(SB-C:DEFINITION-SOURCE-LOCATION
              :NAMESTRING NIL
              :TOPLEVEL-FORM-NUMBER NIL
              :PLIST NIL))
STYLE-WARNING: redefining COMMON-LISP-USER::FOO in DEFUN
  0: WARN returned NIL
FOO

warn 函数被调用时,传入的参数是类 sb-kernel:redefinition-with-defun 的类表示符,所以发出的警告具有相对特定的类类型。基于特定类类型的消除可以更轻松地进行。

现在,看看在未定义变量的情况下会发生什么:

* (defun foo2 () x)
  0: (WARN "undefined ~(~A~): ~S" :VARIABLE X)

; in: DEFUN FOO2
;     (BLOCK FOO2 X)
; 
; caught WARNING:
;   undefined variable: X
  0: WARN returned NIL
; 
; compilation unit finished
;   Undefined variable:
;     X
;   caught 1 WARNING condition
FOO2

warn被调用时会带有一个格式字符串和一些参数,所以发出的警告只是一个simple-warning。现在,您仍然可以采取一些措施来压制它,但这有点复杂。

根据SBCL手册第 3.1.1 节sb-ext:muffle-conditions只使用muffle-warning重启。由于未定义变量警告只是一个simple-warning,我们可能不想压制所有simple-warning,因此我们需要通过处理程序绑定来进行一些诡计并检查条件。由于我们已经看到了warn调用时使用的参数,因此我们可以非常具体地捕获它们。我们可以使用undefined-variable-warning-p来识别这些警告:

(defun undefined-variable-warning-p (w)
  (let ((control (simple-condition-format-control w))
        (arguments (simple-condition-format-arguments w)))
    (and (= 2 (length arguments))
         (eq :variable (first arguments))
         (string= control "undefined ~(~A~): ~S"))))

现在我们可以用合适的handler-bind包装编译表单。例如,我们来看一下带有和没有处理程序的(compile nil (lambda () x)):
CL-USER> (compile nil '(lambda () x))
; 
; caught WARNING:
;   undefined variable: X
; 
; compilation unit finished
;   Undefined variable:
;     X
;   caught 1 WARNING condition
#<FUNCTION (LAMBDA ()) {1003AA4F89}>
T
T

CL-USER> (handler-bind
             ((simple-warning 
               #'(lambda (w) 
                   (when (undefined-variable-warning-p w)
                     (invoke-restart 'muffle-warning)))))
           (compile nil '(lambda () x)))
#<FUNCTION (LAMBDA ()) {1003B737E9}>
NIL
NIL

我们成功编译了该函数并消除了未定义变量的警告。但是请注意,您不能仅仅将defun包装在其中。例如,
CL-USER> (handler-bind
             ((simple-warning 
               #'(lambda (w) 
                   (when (undefined-variable-warning-p w)
                     (invoke-restart 'muffle-warning)))))
           (defun some-function () x))

; in: DEFUN SOME-FUNCTION
;     (DEFUN SOME-FUNCTION () X)
; --> PROGN EVAL-WHEN SB-IMPL::%DEFUN SB-INT:NAMED-LAMBDA FUNCTION 
; ==>
;   (BLOCK SOME-FUNCTION X)
; 
; caught WARNING:
;   undefined variable: X
; 
; compilation unit finished
;   Undefined variable:
;     X
;   caught 1 WARNING condition
SOME-FUNCTION

然而,如果你eval同样的defun(但我不是说你应该这么做),那么警告就会被消除:

CL-USER> (handler-bind
             ((simple-warning 
               #'(lambda (w) 
                   (when (undefined-variable-warning-p w)
                     (invoke-restart 'muffle-warning)))))
           (eval '(defun some-other-function () x)))
SOME-OTHER-FUNCTION

我不确定为什么会这样,但我希望有人能在评论中详细解释。我怀疑这是由于SBCL在REPL上编译表单,这意味着在运行整个表单之前编译defun的主体部分,因此编译是在处理程序就位之前进行的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接