(null (handler-case ...)) can evaluate to incorrect result

Bug #2054101 reported by Matt Kaufmann
6
This bug affects 1 person
Affects Status Importance Assigned to Milestone
SBCL
Won't Fix
Undecided
Unassigned

Bug Description

cyan:~% uname -a
Linux cyan 5.4.0-170-generic #188-Ubuntu SMP Wed Jan 10 09:51:01 UTC 2024 x86_6\
4 x86_64 x86_64 GNU/Linux
cyan:~% sbcl --version
SBCL 2.4.1
cyan:~%

The first two evaluations below give the expected result, but the
third does not. In fact the second and third should clearly be the
same -- they just call null in different, but equivalent, places.
This log was produced using SBCL 2.4.1 on Linux, but I also saw it
with SBCL 2.2.10 on MacOS.

* (let ((*my-most-positive-double-float* most-positive-double-float))
    (declare (special *my-most-positive-double-float*))
    (handler-case
        (* *my-most-positive-double-float*
           *my-most-positive-double-float*)
      (error () nil)))
NIL
* (null (let ((*my-most-positive-double-float*
               most-positive-double-float))
          (declare (special *my-most-positive-double-float*))
          (handler-case
              (* *my-most-positive-double-float*
                 *my-most-positive-double-float*)
            (error () nil))))
T
* (let ((*my-most-positive-double-float*
         most-positive-double-float))
    (declare (special *my-most-positive-double-float*))
    (null (handler-case
              (* *my-most-positive-double-float*
                 *my-most-positive-double-float*)
            (error () nil))))
NIL
*

Revision history for this message
Stas Boukarev (stassats) wrote :

It avoids calling unused functions.

Changed in sbcl:
status: New → Won't Fix
Revision history for this message
Matt Kaufmann (mkaufmann0) wrote :

I'd appreciate a bit more explanation. In particular, in "It avoids calling unused functions", what is "It" and how does that explain why merely moving the NULL test gives a different result here? Note that the last form evaluates to T in CCL, as I'd expect.

Revision history for this message
Stas Boukarev (stassats) wrote :

Unused functions are not evaluated and do not produce errors.

Revision history for this message
Douglas Katzman (dougk) wrote :

See also https://www.nhplace.com/kent/Papers/Exceptional-Situations-1990.html for background on errors in Lisp. You can't call a thing that could signal an error "just" to cause it do that, because normal code will not use the function in that way.

Revision history for this message
Stas Boukarev (stassats) wrote :

(defun f ()
  (null (* (eval 'most-positive-double-float)
           (eval 'most-positive-double-float))))

Compiling this might make it more clear:

(f) => nil. * is not called at all.

Revision history for this message
Matt Kaufmann (mkaufmann0) wrote :

Thank you both for the additional comments. I understand the distinction between normal and exceptional situations, and I have a solution to the problem that prompted me to file this (apparently buggy) bug report.

But for what it's worth, it still seems to me that the CL HyperSpec is violated in SBCL by the following variant of the example provided above, as I discuss below.

(defun f ()
  (declare (optimize (safety 3)))
  (null (* (eval 'most-positive-double-float)
           (eval 'most-positive-double-float))))

HyperSpec Section 12.1.4.3, "Rule of Float Underflow and Overflow", says that "An error of type floating-point-overflow or floating-point-underflow should be signaled if a floating-point computation causes exponent overflow or underflow, respectively." Section 1.4.2 says that when "An error should be signaled", then "Conforming code may rely on the fact that the error is signaled in safe code." I would expect the use of (safety 3) to make the above code safe. Yet (f) does not signal an error. Presumably that is because * is not called, but without an error being signaled by (f), I would expect Lisp call-by-value semantics to imply that actually, * should be called here. I suppose that must be where I'm mistaken, though I don't see why.

To post a comment you must log in.
This report contains Public information  
Everyone can see this information.

Other bug subscribers

Remote bug watches

Bug watches keep track of this bug in other bug trackers.