TYPE-ERROR with special variable

Bug #1791059 reported by Guillaume LE VAILLANT
6
This bug affects 1 person
Affects Status Importance Assigned to Milestone
SBCL
Fix Released
Undecided
Unassigned

Bug Description

When using cl-ledger (https://github.com/ledger/cl-ledger) with SBCL 1.4.11 (GNU/Linux x86-64), I get a TYPE-ERROR that looks related to a special variable. The error appears neither with previous versions of SBCL, nor with CCL, nor with ECL.

If I enter the following:

(ql:quickload "cl-ledger")
(ledger:register-report "/path-to-cl-ledger/doc/sample.dat")

I get the following error:

debugger invoked on a TYPE-ERROR in thread
#<THREAD "main thread" RUNNING {10005505B3}>:
  The value
    #<unknown immediate object, lowtag=#b1, widetag=#x61 {61}>
  is not of type
    NUMBER
  when binding SB-KERNEL::X

Type HELP for debugger help, or (SB-EXT:EXIT) to exit from SBCL.

restarts (invokable by number or by possibly-abbreviated name):
  0: [ABORT] Exit debugger, returning to top level.

(SB-KERNEL:TWO-ARG-+ #<unknown immediate object, lowtag=#b1, widetag=#x61 {61}> 1) [external]
0]

Apparently the problem comes from the CALCULATE-TOTALS function in transforms/totals.lisp:

(defun calculate-totals (xact-series &key (set-amount nil) (set-total nil)
                        (displayed-amount-setter nil) &allow-other-keys)
  ...some forms...
  (let ((running-total 0)
        (running-cost-total 0)
        (*value-expr-series-offset* 0)
        *value-expr-last-xact*)
    (map-fn
     'transaction
     #'(lambda (xact)
         (incf *value-expr-series-offset*)
         ...some forms...
         (setf *value-expr-last-xact* xact))
     xact-series)))

According to the backtrace, the (incf *value-expr-series-offset*) form is the one causing the error:

Backtrace for: #<SB-THREAD:THREAD "main thread" RUNNING {10005505B3}>
0: (SB-KERNEL:TWO-ARG-+ #<unknown immediate object, lowtag=#b1, widetag=#x61 {61}> 1) [external]

The *value-expr-series-offset* is declared in another file as:

(defvar *value-expr-series-offset* 0)

I noticed that adding a SPECIAL declaration in CALCULATE-TOTALS makes the error disappear:

(defun calculate-totals (xact-series &key (set-amount nil) (set-total nil)
                        (displayed-amount-setter nil) &allow-other-keys)
  ...some forms...
  (let ((running-total 0)
        (running-cost-total 0)
        (*value-expr-series-offset* 0)
        *value-expr-last-xact*)
    (declare (special *value-expr-series-offset*))
    (map-fn
     'transaction
     #'(lambda (xact)
         (incf *value-expr-series-offset*)
         ...some forms...
         (setf *value-expr-last-xact* xact))
     xact-series)))

The other special variable (*value-expr-last-xact*) doesn't trigger any error whether there is a declaration or not.

Revision history for this message
Paul F. Dietz (paul-f-dietz) wrote :

I'm not entirely familiar with SERIES, but map-fn returns an object that has calls to that lambda in it, right? If so, that binding of the special variable will have long since been lost.

* (declaim (special *s*))

(*S*)
* (setf *s* :bad)

:BAD
* (funcall (let ((*s* :good)) (lambda () *s*)))

:BAD

This is different from lexical bindings:

* (funcall (let ((s :good)) (lambda () s)))

:GOOD

Revision history for this message
Guillaume LE VAILLANT (guillaume-le-vaillant) wrote :

I'm not familiar with the Series library either, but apparently it can indeed delay the computation of the result until a time when it is explicitly asked. So the code of the CALCULATE-TOTALS function was not correct.

However, even if the execution of the lambda form happens after the temporary binding for *value-expr-series-offset* has disappeared, shouldn't the toplevel binding be used instead of signalling an error?

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

In e5d332a8c24700fa972527ca85e56477d80f795f.
But yeah, cl-ledger seems to be doing not what it thinks to be doing.

Changed in sbcl:
status: New → Fix Committed
Changed in sbcl:
status: Fix Committed → Fix Released
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.