sbcl - how to muffle "undefined variable" warning?

common-lisp, sbcl, suppress-warnings

Solution

I'm not sure whether you'll be able to muffle this type of warning specifically, at least by class name. By tracing `warn`, we can get some idea of what SBCL is doing. For instance, look what happens in the redefinition case:

* (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` is getting called with the class `sb-kernel:redefinition-with-defun` and arguments as the class designator, and so the warning that is signaled has a somewhat specific class type. Being able to muffle based on a particular class type makes the muffling easier.

Now, look what happens in the case of the undefined variable:

* (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` is being called with a format string and some arguments, so the warning that is being signaled is just a `simple-warning`. Now, you can still do something to muffle it, but it's a bit more complicated.

According to the SBCL manual section, 3.1.1 Controlling Verbosity, `sb-ext:muffle-conditions` is just using the `muffle-warning` restart. Because the undefined variable warning is just a `simple-warning`, and we probably don't want to muffle all `simple-warning`s, we'll need to be a bit sneaky and inspect the condition using a handler specified by handler bind. Since we've seen the arguments that `warn` is getting called with, we can be very specific in what we catch. We can recognize these warnings with `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"))))

Now we can wrap the compilation forms in an appropriate `handler-bind`. For instance, let's look at `(compile nil (lambda () x))` with and without a handler:

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

We've succeeded in compiling the function and muting the undefined variable warning. However, be aware that you can't simply wrap `defun`s in this. E.g.,

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

However, if you `eval` the same `defun` (but I'm not saying that you should), the warning is muffled:

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

I'm not sure exactly why this is, but I hope that someone may be able to elaborate in the comments. I suspect that this comes from SBCL compiling forms on the REPL, meaning that the body of the `defun` is getting compiled before the entire form is run, and so the compilation is happening before the handler is in place.

Problem

I can't figure out how to do it with `sb-ext:muffle-conditions`. I want to do something like this: ``` (declaim #+sbcl(sb-ext:muffle-conditions sb-kernel:redefinition-warning)) ``` Except I want to muffle the "undefined variable" warning instead of redefinition, of course. If anyone knows what parameter this is or has a link to the documentation/various options for `sb-ext:muffle-conditions`, please share :) Thanks

Original source