Opened 13 years ago

Closed 13 years ago

#299 closed defect (invalid)

CCL does not resolve forward-referenced accessors defined by defstruct in the same compilation unit

Reported by: hans Owned by: gb
Priority: major Milestone:
Component: Compiler Version:
Keywords: Cc:


When compiling a file that uses a setf accessor defined by defstruct before the defstruct is seen, CCL fails to resolve the reference.

hertha 322_> cat foo.lisp
(defpackage "FOO" (:use "CL"))

(in-package "FOO")

(defun test ()
  (let ((tpl (make-template :mode 1)))
    (setf (template-mode tpl) 2)
    (template-mode tpl)))

(defstruct template mode)

hertha 323_> ccl -n
Welcome to Clozure Common Lisp Version 1.2-r9260M-trunk  (DarwinPPC32)!
? (load (compile-file "foo.lisp"))
;Compiler warnings for "/private/tmp/foo.lisp" :
;   In FOO::TEST: Undefined function SETF::|FOO::TEMPLATE-MODE|

Change History (1)

comment:1 Changed 13 years ago by gb

  • Resolution set to invalid
  • Status changed from new to closed

This is not a bug (at least, not in the lisp). You can't portably forward-reference SETF of structure slots, though it might "work" in some implementations (unless and until those implementations change the artifact that you're depending on.)

Something of the form (SETF (FOO x) y) macroexpands into (FUNCALL #'(SETF FOO) (y x)) if SETF has no other knowledge of how to expand the form. That allows forward references to SETFable accessors of the slots of STANDARD-INSTANCEs to work, since it is well-defined that the :ACCESSOR name slot option in DEFCLASS creates a slot-writer function whose name is (SETF name).

In contrast, you don't know whether something like

(setf (aref a i) v)

expands into

(funcall #'(setf aref) i a)


(internal::%aset a i)

or any number of other plausible things. All that the spec says is that SETF works with AREF and that SETF generally expands into code that observes order-of-evaluation issues (more than my examples did); the details are quite rightly left to the implementation. AREF is "known to SETF"; whether that's because #'(SETF AREF) is defined or whether DEFSETF or DEFINE-SETF-EXPANDER or some other mechanism has been used to make AREF "known to SETF" is unspecified, and user code should not depend on implementation artifacts.

The spec likewise says that while processing a toplevel DEFSTRUCT, the compiler must arrange that the accessors for writable slots in the structure are "known to SETF." The spec doesn't dictate the mechanism by which a structure slot-accessor is mad known to SETF; there are again lots of options and lots of tradeoffs involved. Defining a function named (SETF accessor) is one of several ways in which the accessor can be made known to SETF; it's neither a partuclarly good nor a particularly bad way, in and of itself. User code should be able to depend on (SETF accessor) working after the DEFSTRUCT form has been processed, but should not assume that it knows how (SETF accessor) is implemented and in particular should not assume that it's implemented by means of a function named (SETF accessor).

Using (SETF accessor) before the structure which defines accessor can only "work" (for some value of "work") if the user knows how DEFSTRUCT will arrange for the accessor to work with SETF; in particular, it will "work" if DEFSTRUCT arranges for there to be a function named (SETF accessor) - as is incidentally the case in at least one implementation - and will fail in many other implementations that implement SETF on a struct accessor in some other way.

There are several arguments against referencing structure accessors before the structure is defined. All of those arguments apply to using SETF on those accessors before the structure is defined, but SETF can only "work" by accident.

Note: See TracTickets for help on using tickets.