DEFCLASS slot names and symbol macros

Bug #539540 reported by Nikodemus Siivola on 2010-03-16
10
This bug affects 2 people
Affects Status Importance Assigned to Milestone
SBCL
Medium
Unassigned

Bug Description

CLHS says of DEFCLASS slot names: "The slot-name argument is a symbol that is syntactically valid for use as a variable name."

In SBCL:
CL-USER> (define-symbol-macro foo 42)
FOO
CL-USER> (let ((foo 23)) foo)
23
CL-USER> (defclass test-1 () (foo))
; Evaluation aborted.
CL-USER> (princ (nth-value 1 (ignore-errors (eval '(defclass test-1 () (foo))))))
In DEFCLASS TEST-1, the slot name FOO is a constant.
#<SB-INT:SIMPLE-PROGRAM-ERROR {10037A24B1}>
CL-USER> (symbol-macrolet ((bar 42))
           (let ((bar 13))
             bar))
13
CL-USER> (princ (nth-value 1 (ignore-errors (eval
                                             `(symbol-macrolet ((bar 42))
                                                (defclass test-1 () (bar)))))))
In DEFCLASS TEST-1, the slot name BAR is a constant.
#<SB-INT:SIMPLE-PROGRAM-ERROR {100396F291}>

In other words: symbol macro names are valid variable names, and should be also valid slot names.

tags: added: pcl
Changed in sbcl:
assignee: nobody → Roman Marynchak (roman-marynchak)
status: Confirmed → In Progress

This issue is quite interesting.

The function to look at is CHECK-SLOT-NAME-FOR-DEFCLASS, from src/pcl/defclass.lisp. With the workaround, it looks like this:

    (cond ((not (symbolp name))
           (slot-name-illegal "not a symbol"))
          ((keywordp name)
           (slot-name-illegal "a keyword"))
      + ((or (eq name t) (eq name nil))
      + (slot-name-illegal "a constant"))
       - ((constantp name env)
       - (slot-name-illegal "a constant"))

...

The workaround idea is to check only for T and NIL, and allow other constants to be used as slots names. The disadvantage is that there will be a failure within WITH-SLOTS when trying to bind these slot names (they will conflict with the constants names which are also visible there). However, when one does not use WITH-SLOTS, things work well.

There is the possibility for the better fix, where a symbol is checked to have a constant expansion, but it is not declared with DEFCONSTANT. Also, note that symbols which have the expansion set with define-symbol-macro or symbol-macrolet do not conflict with the same slots names within WITH-SLOTS.

The question now is: should we prefer a failure within DEFCLASS or a failure within WITH-SLOTS, when a slot name is also a constant's name? Note that

(defclass foo () (bar))

(defconstant bar 1)

always works fine (with the workaround and without it), but

(defconstant bar 1)

(defclass foo () (bar))

fails without the above workaround. Both of them eventually fail when trying (with-slots (bar) (make-instance 'foo) bar).

Please share your opinions! For some additional info, please see the talk on #sbcl: http://ccl.clozure.com/irc-logs/sbcl/2010-11/sbcl-2010.11.14.txt

Changed in sbcl:
assignee: Roman Marynchak (roman-marynchak) → nobody
status: In Progress → Confirmed

Roman Marynchak <email address hidden> writes:

> The workaround idea is to check only for T and NIL, and allow other
> constants to be used as slots names.

I don't like this idea at all. What's special about T and NIL?

> The question now is: should we prefer a failure within DEFCLASS or a
> failure within WITH-SLOTS, when a slot name is also a constant's name?

Within DEFCLASS: prefer reporting undefined code early if possible.

Why is the right answer not to replace
  (constantp name env)
with
  (eq (info :variable :kind name) :constant)
?

Christophe

The approach with (eq (info :variable :kind name) :constant) resolves the issue perfectly, and the above original examples (with symbol-macrolet etc.) work fine, while constants (including user-defined) are reported to be the wrong slots names. The only problem is that I have seen a test failure while testing the patched system:

Invalid exit status: package-locks.impure.lisp.

As for now, I cannot say what is wrong there, this problem needs some additional investigation.

The problem occurs also with symbols that have been declared constant variables, and with keywords:

cl-user> (defclass c () (example))

debugger invoked on a SB-INT:SIMPLE-PROGRAM-ERROR in thread #<THREAD "repl-thread" RUNNING {100A85B1C3}>: In DEFCLASS C, the slot name EXAMPLE is a constant.
; Evaluation aborted on #<sb-int:simple-program-error "~@<In DEFCLASS ~S, the slot name ~S is a constant.~@:>" {1006CD9753}>.
cl-user> (defclass d () (:example))

debugger invoked on a SB-INT:SIMPLE-PROGRAM-ERROR in thread #<THREAD "repl-thread" RUNNING {100A85B1C3}>: In DEFCLASS D, the slot name :EXAMPLE is a keyword.
; Evaluation aborted on #<sb-int:simple-program-error "~@<In DEFCLASS ~S, the slot name ~S is a keyword.~@:>" {10072AC063}>.

All the symbols are syntactically valid for use as a variable name. I can't fathom what this clause was intended to mean.

Including keywords, which are symbols that name automatically defined constant variables.

Also, the only places where slot names are used, are in slot-value where they are not evaluated, and in with-slots where they are not evaluated either, therefore they don't need to be free to be bound.

One can give a free name for the variables in with-slots:

CL-USER> (defclass e () ((:e :initarg :e)))
#<STANDARD-CLASS E>
CL-USER> (with-slots ((e :e)) (make-instance 'e :e 42) e)
42
CL-USER>

so there's no reason to constrain slot names.

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

Other bug subscribers