Steel Bank Common Lisp

"The value NIL is not of type SB-KERNEL:LAYOUT"

Reported by Rthaduthd Anthnhkrc on 2009-11-04
6
This bug affects 1 person
Affects Status Importance Assigned to Milestone
SBCL
Medium
Unassigned

Bug Description

;; From Alexandria; not interesting wrt. this report.
(defun remove-from-plist (plist &rest keys)
  (loop for (key . rest) on plist by #'cddr
        do (assert rest () "Expected a proper plist, got ~S" plist)
        unless (member key keys :test #'eq)
        collect key and collect (first rest)))

(defclass my-class (standard-class)
  ())

(defmethod sb-mop:validate-superclass ((class my-class) (super-class standard-class))
  t)

(defclass my-object ()
  ((id :type integer :reader id-of))
  (:metaclass my-class))

(defmethod initialize-instance ((class my-class) &rest initargs &key name direct-superclasses)
  (let ((my-object-class (find-class 'my-object)))
    (if (or (eq name 'my-object)
            (some (lambda (class) (subtypep class my-object-class))
                  direct-superclasses))
        (call-next-method)
        (apply #'call-next-method class
               :direct-superclasses (cons my-object-class direct-superclasses)
               (remove-from-plist initargs :direct-superclasses)))))

#| Seems deleted code (?) triggers the problem somehow. Also, it does not happen when the first argument is
   (CLASS T). I've understood (I think ..) that dispatch based on the first argument for SVUC is "pointless", but
   should code like this cause trouble nonetheless? |#
(defmethod sb-mop:slot-value-using-class ((class my-class) (object my-object) eslotd)
  (if nil
      (setf (slot-value object 'id) 42)
      (call-next-method)))

(eval-when (:compile-toplevel :load-toplevel :execute)
  (unintern 'test)
  (unintern 'a)
  (unintern 'test-a))

(defclass test ()
  ((a :reader test-a))
  (:metaclass my-class))

I'm not sure what's wrong (and it's 4 in the morning.. :P). The deletion of code might confuse the compiler or the expansion of the DEFCLASS form or something.

SBCL 1.0.32.11 (I've confirmed it happening with 1.0.29 also).

Linux blackbox 2.6.30-2-amd64 #1 SMP Fri Sep 25 22:16:56 UTC 2009 x86_64 GNU/Linux

(:HUNCHENTOOT-NO-SSL :ASDF :SB-THREAD :ANSI-CL :COMMON-LISP :SBCL :SB-DOC
 :SB-TEST :SB-LDB :SB-PACKAGE-LOCKS :SB-UNICODE :SB-EVAL :SB-SOURCE-LOCATIONS
 :IEEE-FLOATING-POINT :X86-64 :UNIX :ELF :LINUX :LARGEFILE :GENCGC
 :STACK-GROWS-DOWNWARD-NOT-UPWARD :C-STACK-IS-CONTROL-STACK :LINKAGE-TABLE
 :COMPARE-AND-SWAP-VOPS :UNWIND-TO-FRAME-AND-CALL-VOP :RAW-INSTANCE-INIT-VOPS
 :STACK-ALLOCATABLE-CLOSURES :STACK-ALLOCATABLE-VECTORS
 :STACK-ALLOCATABLE-LISTS :STACK-ALLOCATABLE-FIXED-OBJECTS :ALIEN-CALLBACKS
 :CYCLE-COUNTER :COMPLEX-FLOAT-VOPS :FLOAT-EQL-VOPS :INLINE-CONSTANTS
 :OS-PROVIDES-DLOPEN :OS-PROVIDES-PUTWC :OS-PROVIDES-SUSECONDS-T)

Hi,

 status confirmed
 importance medium
 tags clos mop pcl
 done

Lars Rune Nøstdal <email address hidden> writes:

> (defclass my-object ()
> ((id :type integer :reader id-of))
> (:metaclass my-class))
>
> [...]
> (defmethod initialize-instance ((class my-class) &rest initargs &key name direct-superclasses)
> (defmethod sb-mop:slot-value-using-class ((class my-class) (object my-object) eslotd)
> (if nil
> (setf (slot-value object 'id) 42)
> (call-next-method)))

As mentioned on IRC, this is not conforming MOP code: here you
instantiate MY-CLASS, then you define methods that are applicable to
objects of MY-CLASS; however, "Portable methods on specified generic
functions specialized to portable metaobject classes must be defined
before any instances of those classes (or any subclasses) are created,
either directly or indirectly by a call to make-instance." The main
workaround for you is I think to use (find-class 'my-object nil) in your
initialize-instance method.

That said, I was able to reduce this to a test case which I think is
legal MOP code and fails in the same way:

(defclass my-class (standard-class)
  ())
(defmethod sb-mop:validate-superclass
    ((class my-class) (super-class standard-class))
  t)
(defvar *foo*)
(defmethod sb-mop:slot-value-using-class
    ((class my-class) (object standard-object) eslotd)
  (if *foo*
      (setf (slot-value object 'id) 42)
      (call-next-method)))
(defclass my-object ()
  ((id :type integer :reader id-of))
  (:metaclass my-class)))
(defclass test (my-object)
  ((a :reader test-a))
  (:metaclass my-class))

I've Cced this to Pascal to ask him to check that the above is actually
legal MOP; in parallel, I'm working on a diagnosis and possible cure for
SBCL.

Cheers,

Christophe

Changed in sbcl:
importance: Undecided → Medium
status: New → Confirmed

Hi,

 status inprogress
 done

> That said, I was able to reduce this to a test case which I think is
> legal MOP code and fails in the same way:
>
> (defclass my-class (standard-class)
> ())
> (defmethod sb-mop:validate-superclass
> ((class my-class) (super-class standard-class))
> t)
> (defvar *foo*)
> (defmethod sb-mop:slot-value-using-class
> ((class my-class) (object standard-object) eslotd)
> (if *foo*
> (setf (slot-value object 'id) 42)
> (call-next-method)))
> (defclass my-object ()
> ((id :type integer :reader id-of))
> (:metaclass my-class)))

That's actually all you need; the TEST defclass below is unnecessary.

> (defclass test (my-object)
> ((a :reader test-a))
> (:metaclass my-class))
>
> I've Cced this to Pascal to ask him to check that the above is actually
> legal MOP

Pascal has confirmed in mail that hasn't made it to this bug log that my
code above is legal.

> in parallel, I'm working on a diagnosis and possible cure for
> SBCL.

Here's roughly what I think is going on:

* Fairly early in a class' life, we compute its slots, and at the same
  time store in each of the resulting effective slot definitions three
  effective method functions, one for each relevant
  accessor (reader/writer/boundp). At the point when we do this, the
  class' wrapper is not yet available, because we need to compute the
  slots to have the wrapper.

* To generate the effective accessor method functions, we need to be
  able to call the appropriate method function of the relevant svuc
  generic functions. This might involve the fast-method-function
  calling convention, about which I know nothing except that it might
  involve a pv-table if slots of specialized arguments are accessed.

* The pv table stores a cache mapping wrappers to permutation vector;
  a permutation vector itself is a mapping from classes to slot
  locations, and is I think irrelevant to the rest of this.

* The error from this code comes from attempting to generate the
  accessor information for the ID slot in TEST. [ (SETF
  *BREAK-ON-SIGNALS* 'ERROR) to see this ]

* The error comes from GET-METHOD-FUNCTION, whose WRAPPERS argument is
  a bunch of WRAPPERS, but with NIL for the second argument.

* This is all happening in the context of class finalization, but the
  class is not yet finalized (so calling e.g. FIND-SLOT-DEFINITION
  loses)

* We can avoid this confusion if we don't finalize the class (or rather
  the accessors, but in practice that means the class) this early; we do
  need to compute whether the slot accessor will be std-p, but we don't
  need the method function; we can instead store in the slotd something
  which lazily updates the method function with the actual final value.

I think. The above isn't massively clear, but the patch that I'm
testing is actually already shorter than all that.

Christophe

Changed in sbcl:
status: Confirmed → In Progress

Hi,

 status fixcommitted
 done

I have committed a fix for this as sbcl-1.0.32.12. Please let me know
if anything odd happens with your MOP-using code, because the fix while
small was marginally invasive.

Cheers,

Christophe

Changed in sbcl:
status: In Progress → Fix Committed
Changed in sbcl:
status: Fix Committed → Fix Released
To post a comment you must log in.
This report contains Public information  Edit
Everyone can see this information.

Other bug subscribers