improve the SB-EXT:GC docstring(s)
[sbcl.git] / src / pcl / vector.lisp
1 ;;;; permutation vectors
2
3 ;;;; This software is part of the SBCL system. See the README file for
4 ;;;; more information.
5
6 ;;;; This software is derived from software originally released by Xerox
7 ;;;; Corporation. Copyright and release statements follow. Later modifications
8 ;;;; to the software are in the public domain and are provided with
9 ;;;; absolutely no warranty. See the COPYING and CREDITS files for more
10 ;;;; information.
11
12 ;;;; copyright information from original PCL sources:
13 ;;;;
14 ;;;; Copyright (c) 1985, 1986, 1987, 1988, 1989, 1990 Xerox Corporation.
15 ;;;; All rights reserved.
16 ;;;;
17 ;;;; Use and copying of this software and preparation of derivative works based
18 ;;;; upon this software are permitted. Any distribution of this software or
19 ;;;; derivative works must comply with all applicable United States export
20 ;;;; control laws.
21 ;;;;
22 ;;;; This software is made available AS IS, and Xerox Corporation makes no
23 ;;;; warranty about the software, its performance or its conformity to any
24 ;;;; specification.
25
26 (in-package "SB-PCL")
27
28 ;;;; Up to 1.0.9.24 SBCL used to have a sketched out implementation
29 ;;;; for optimizing GF calls inside method bodies using a PV approach,
30 ;;;; inherited from the original PCL. This was never completed, and
31 ;;;; was removed at that point to make the code easier to understand
32 ;;;; -- but:
33 ;;;;
34 ;;;; FIXME: It would be possible to optimize GF calls inside method
35 ;;;; bodies using permutation vectors: if all the arguments to the
36 ;;;; GF are specializers parameters, we can assign a permutation index
37 ;;;; to each such (GF . ARGS) tuple inside a method body, and use this
38 ;;;; to cache effective method functions.
39 \f
40 (declaim (inline make-pv-table))
41 (defstruct (pv-table (:predicate pv-tablep)
42                      (:copier nil))
43   (cache nil :type (or cache null))
44   (pv-size 0 :type fixnum)
45   (slot-name-lists nil :type list))
46
47 (defun make-pv-table-type-declaration (var)
48   `(type pv-table ,var))
49
50 ;;; Used for interning parts of SLOT-NAME-LISTS, as part of
51 ;;; PV-TABLE interning -- just to save space.
52 (defvar *slot-name-lists* (make-hash-table :test 'equal))
53
54 ;;; Used for interning PV-TABLES, keyed by the SLOT-NAME-LISTS
55 ;;; used.
56 (defvar *pv-tables* (make-hash-table :test 'equal))
57
58 ;;; ...and one lock to rule them. Lock because for certain (rare)
59 ;;; cases this lock might be grabbed in the course of method dispatch
60 ;;; -- and mostly this is already under the *world-lock*
61 (defvar *pv-lock*
62   (sb-thread:make-mutex :name "pv table index lock"))
63
64 (defun intern-pv-table (&key slot-name-lists)
65   (flet ((intern-slot-names (slot-names)
66            ;; FIXME: NIL at the head of the list is a remnant from
67            ;; old purged code, that hasn't been quite cleaned up yet.
68            ;; ...but as long as we assume it is there, we may as well
69            ;; assert it.
70            (aver (not (car slot-names)))
71            (or (gethash slot-names *slot-name-lists*)
72                (setf (gethash slot-names *slot-name-lists*) slot-names)))
73          (%intern-pv-table (snl)
74            (or (gethash snl *pv-tables*)
75                (setf (gethash snl *pv-tables*)
76                      (make-pv-table :slot-name-lists snl
77                                     :pv-size (* 2 (reduce #'+ snl
78                                                           :key (lambda (slots)
79                                                                  (length (cdr slots))))))))))
80     (sb-thread:with-mutex (*pv-lock*)
81       (%intern-pv-table (mapcar #'intern-slot-names slot-name-lists)))))
82 \f
83 (defun use-standard-slot-access-p (class slot-name type)
84   (or (not (eq **boot-state** 'complete))
85       (and (standard-class-p class)
86            (let ((slotd (find-slot-definition class slot-name)))
87              (and slotd
88                   (slot-accessor-std-p slotd type))))))
89
90 (defun slot-missing-info (class slot-name)
91   (flet ((missing (operation)
92            (lambda (object)
93              (slot-missing class object slot-name operation))))
94     (make-slot-info
95      :reader (missing 'slot-value)
96      :boundp (missing 'slot-boundp)
97      :writer (lambda (new-value object)
98                (slot-missing class object slot-name 'setf new-value)))))
99
100 (defun compute-pv (slot-name-lists wrappers)
101   (unless (listp wrappers)
102     (setq wrappers (list wrappers)))
103   (let (elements)
104     (dolist (slot-names slot-name-lists)
105       (when slot-names
106         (let* ((wrapper (pop wrappers))
107                (std-p (typep wrapper 'wrapper))
108                (class (wrapper-class* wrapper)))
109           (dolist (slot-name (cdr slot-names))
110             (let ((cell
111                    (or (find-slot-cell wrapper slot-name)
112                        (cons nil (slot-missing-info class slot-name)))))
113               (push (when (and std-p (use-standard-slot-access-p class slot-name 'all))
114                       (car cell))
115                   elements)
116               (push (or (cdr cell)
117                         (bug "No SLOT-INFO for ~S in ~S" slot-name class))
118                   elements))))))
119     (let* ((n (length elements))
120            (pv (make-array n)))
121       (loop for i from (1- n) downto 0
122          do (setf (svref pv i) (pop elements)))
123       pv)))
124
125 (defun pv-table-lookup (pv-table pv-wrappers)
126   (let* ((slot-name-lists (pv-table-slot-name-lists pv-table))
127          (cache (or (pv-table-cache pv-table)
128                     (setf (pv-table-cache pv-table)
129                           (make-cache :key-count (- (length slot-name-lists)
130                                                     (count nil slot-name-lists))
131                                       :value t
132                                       :size 2)))))
133     (multiple-value-bind (hitp value) (probe-cache cache pv-wrappers)
134       (if hitp
135           value
136           (let* ((pv (compute-pv slot-name-lists pv-wrappers))
137                  (new-cache (fill-cache cache pv-wrappers pv)))
138             ;; This is safe: if another thread races us here the loser just
139             ;; misses the next time as well.
140             (unless (eq new-cache cache)
141               (setf (pv-table-cache pv-table) new-cache))
142             pv)))))
143
144 (defun make-pv-type-declaration (var)
145   `(type simple-vector ,var))
146 \f
147 ;;; Sometimes we want to finalize if we can, but it's OK if
148 ;;; we can't.
149 (defun try-finalize-inheritance (class)
150   (unless (typep class 'forward-referenced-class)
151     (when (every (lambda (super)
152                    (or (eq super class)
153                        (class-finalized-p super)
154                        (try-finalize-inheritance super)))
155                  (class-direct-superclasses class))
156       (finalize-inheritance class)
157       t)))
158
159 (defun can-optimize-access (form required-parameters env)
160   (destructuring-bind (op var-form slot-name-form &optional new-value) form
161     (let ((type (ecase op
162                   (slot-value 'reader)
163                   (set-slot-value 'writer)
164                   (slot-boundp 'boundp)))
165           (var (extract-the var-form))
166           (slot-name (constant-form-value slot-name-form env)))
167       (when (and (symbolp var) (not (var-special-p var env)))
168         (let* ((rebound? (caddr (var-declaration '%variable-rebinding var env)))
169                (parameter-or-nil (car (memq (or rebound? var)
170                                             required-parameters))))
171           (when parameter-or-nil
172             (let* ((class-name (caddr (var-declaration '%class
173                                                        parameter-or-nil
174                                                        env)))
175                    (class (find-class class-name nil)))
176               (cond ((not (eq **boot-state** 'complete))
177                      (setq class nil))
178                     ((and class (not (class-finalized-p class)))
179                      ;; The class itself is never forward-referenced
180                      ;; here, but its superclasses may be.
181                      (unless (try-finalize-inheritance class)
182                        (when (boundp 'sb-c:*lexenv*)
183                          (sb-c:compiler-notify
184                           "~@<Cannot optimize slot access, inheritance of ~S is not ~
185                            yet finaliable due to forward-referenced superclasses:~
186                            ~%  ~S~:@>"
187                           class form))
188                        (setf class nil))))
189               (when (and class-name (not (eq class-name t)))
190                 (when (not (and class
191                                 (memq *the-class-structure-object*
192                                       (class-precedence-list class))))
193                   (aver type)
194                   (values (cons parameter-or-nil (or class class-name))
195                           slot-name
196                           new-value))))))))))
197
198 ;;; Check whether the binding of the named variable is modified in the
199 ;;; method body.
200 (defun parameter-modified-p (parameter-name env)
201   (let ((modified-variables (%macroexpand '%parameter-binding-modified env)))
202     (memq parameter-name modified-variables)))
203
204 (defun optimize-slot-value (form slots required-parameters env)
205   (multiple-value-bind (sparameter slot-name)
206       (can-optimize-access form required-parameters env)
207     (if sparameter
208         (let ((optimized-form
209                (optimize-instance-access slots :read sparameter
210                                          slot-name nil)))
211           ;; We don't return the optimized form directly, since there's
212           ;; still a chance that we'll find out later on that the
213           ;; optimization should not have been done, for example due to
214           ;; the walker encountering a SETQ on SPARAMETER later on in
215           ;; the body [ see for example clos.impure.lisp test with :name
216           ;; ((:setq :method-parameter) slot-value)) ]. Instead we defer
217           ;; the decision until the compiler macroexpands
218           ;; OPTIMIZED-SLOT-VALUE.
219           ;;
220           ;; Note that we must still call OPTIMIZE-INSTANCE-ACCESS at
221           ;; this point (instead of when expanding
222           ;; OPTIMIZED-SLOT-VALUE), since it mutates the structure of
223           ;; SLOTS. If that mutation isn't done during the walking,
224           ;; MAKE-METHOD-LAMBDA-INTERNAL won't wrap a correct PV-BINDING
225           ;; form around the body, and compilation will fail.  -- JES,
226           ;; 2006-09-18
227           `(optimized-slot-value ,form ,(car sparameter) ,optimized-form))
228         `(accessor-slot-value ,@(cdr form)))))
229
230 (defmacro optimized-slot-value (form parameter-name optimized-form
231                                 &environment env)
232   ;; Either use OPTIMIZED-FORM or fall back to the safe
233   ;; ACCESSOR-SLOT-VALUE.
234   (if (parameter-modified-p parameter-name env)
235       `(accessor-slot-value ,@(cdr form))
236       optimized-form))
237
238 (defun optimize-set-slot-value (form slots required-parameters env)
239   (multiple-value-bind (sparameter slot-name new-value)
240       (can-optimize-access form required-parameters env)
241     (if sparameter
242         (let ((optimized-form
243                (optimize-instance-access slots :write sparameter
244                                          slot-name new-value (safe-code-p env))))
245              ;; See OPTIMIZE-SLOT-VALUE
246              `(optimized-set-slot-value ,form ,(car sparameter) ,optimized-form))
247            `(accessor-set-slot-value ,@(cdr form)))))
248
249 (defmacro optimized-set-slot-value (form parameter-name optimized-form
250                                     &environment env)
251   (cond ((parameter-modified-p parameter-name env)
252          ;; ACCESSOR-SET-SLOT-VALUE doesn't do type-checking,
253          ;; so we need to use SAFE-SET-SLOT-VALUE.
254          (if (safe-code-p env)
255              `(safe-set-slot-value ,@(cdr form)))
256              `(accessor-set-slot-value ,@(cdr form)))
257         (t
258          optimized-form)))
259
260 (defun optimize-slot-boundp (form slots required-parameters env)
261   (multiple-value-bind (sparameter slot-name)
262       (can-optimize-access form required-parameters env)
263     (if sparameter
264         (let ((optimized-form
265                (optimize-instance-access slots :boundp sparameter
266                                          slot-name nil)))
267           ;; See OPTIMIZE-SLOT-VALUE
268           `(optimized-slot-boundp ,form ,(car sparameter) ,optimized-form))
269         `(accessor-slot-boundp ,@(cdr form)))))
270
271 (defmacro optimized-slot-boundp (form parameter-name optimized-form
272                                  &environment env)
273   (if (parameter-modified-p parameter-name env)
274       `(accessor-slot-boundp ,@(cdr form))
275       optimized-form))
276
277 ;;; The SLOTS argument is an alist, the CAR of each entry is the name
278 ;;; of a required parameter to the function. The alist is in order, so
279 ;;; the position of an entry in the alist corresponds to the
280 ;;; argument's position in the lambda list.
281 (defun optimize-instance-access (slots read/write sparameter slot-name
282                                  new-value &optional safep)
283   (let ((class (if (consp sparameter) (cdr sparameter) *the-class-t*))
284         (parameter (if (consp sparameter) (car sparameter) sparameter)))
285     (if (and (eq **boot-state** 'complete)
286              (classp class)
287              (memq *the-class-structure-object* (class-precedence-list class)))
288         (let ((slotd (find-slot-definition class slot-name)))
289           (ecase read/write
290             (:read
291              `(,(slot-definition-defstruct-accessor-symbol slotd) ,parameter))
292             (:write
293              `(setf (,(slot-definition-defstruct-accessor-symbol slotd)
294                      ,parameter)
295                     ,new-value))
296             (:boundp
297              t)))
298         (let* ((parameter-entry (assq parameter slots))
299                (slot-entry      (assq slot-name (cdr parameter-entry)))
300                (position (posq parameter-entry slots))
301                (pv-offset-form (list 'pv-offset ''.PV-OFFSET.)))
302           (unless parameter-entry
303             (bug "slot optimization bewilderment: O-I-A"))
304           (unless slot-entry
305             (setq slot-entry (list slot-name))
306             (push slot-entry (cdr parameter-entry)))
307           (push pv-offset-form (cdr slot-entry))
308           (ecase read/write
309             (:read
310              `(instance-read ,pv-offset-form ,parameter ,position
311                              ',slot-name ',class))
312             (:write
313              `(let ((.new-value. ,new-value))
314                 (instance-write ,pv-offset-form ,parameter ,position
315                                 ',slot-name ',class .new-value. ,safep)))
316             (:boundp
317              `(instance-boundp ,pv-offset-form ,parameter ,position
318                                ',slot-name ',class)))))))
319
320 (define-walker-template pv-offset) ; These forms get munged by mutate slots.
321 (defmacro pv-offset (arg) arg)
322 (define-walker-template instance-accessor-parameter)
323 (defmacro instance-accessor-parameter (x) x)
324
325 ;;; It is safe for these two functions to be wrong. They just try to
326 ;;; guess what the most likely case will be.
327 (defun generate-fast-class-slot-access-p (class-form slot-name-form)
328   (let ((class (and (constantp class-form) (constant-form-value class-form)))
329         (slot-name (and (constantp slot-name-form)
330                         (constant-form-value slot-name-form))))
331     (and (eq **boot-state** 'complete)
332          (standard-class-p class)
333          (not (eq class *the-class-t*)) ; shouldn't happen, though.
334          (let ((slotd (find-slot-definition class slot-name)))
335            (and slotd (eq :class (slot-definition-allocation slotd)))))))
336
337 (defun constant-value-or-nil (form)
338   (and (constantp form) (constant-form-value form)))
339
340 (defun slot-access-strategy (class slot-name type &optional conservative)
341   ;; CONSERVATIVE means we should assume custom access pattern even if
342   ;; there are no custom accessors defined if the metaclass is non-standard.
343   ;;
344   ;; This is needed because DEFCLASS generates accessor methods before possible
345   ;; SLOT-VALUE-USING-CLASS methods are defined, which causes them to take
346   ;; the slow path unless we make the conservative assumption here.
347   (if (eq **boot-state** 'complete)
348       (let (slotd)
349         (cond ((or
350                 ;; Conditions, structures, and classes for which FIND-CLASS
351                 ;; doesn't return them yet.
352                 ;; FIXME: surely we can get faster accesses for structures?
353                 (not (standard-class-p class))
354                 ;; Should not happen... (FIXME: assert instead?)
355                 (eq class *the-class-t*)
356                 (not (class-finalized-p class))
357                 ;; Strangeness...
358                 (not (setf slotd (find-slot-definition class slot-name))))
359                :accessor)
360               ((and (slot-accessor-std-p slotd type)
361                     (or (not conservative) (eq *the-class-standard-class* (class-of class))))
362                ;; The best case.
363                :standard)
364               (t
365                :custom)))
366       :standard))
367
368 ;;;; SLOT-VALUE
369
370 (defmacro instance-read (pv-offset parameter position slot-name class)
371   (ecase (slot-access-strategy (constant-value-or-nil class)
372                                (constant-value-or-nil slot-name)
373                                'reader)
374     (:standard
375      `(instance-read-standard
376        .pv. ,(slot-vector-symbol position)
377        ,pv-offset (accessor-slot-value ,parameter ,slot-name)
378        ,(if (generate-fast-class-slot-access-p class slot-name)
379             :class :instance)))
380     (:custom
381      `(instance-read-custom .pv. ,pv-offset ,parameter))
382     (:accessor
383      `(accessor-slot-value ,parameter ,slot-name))))
384
385 (defmacro instance-read-standard (pv slots pv-offset default &optional kind)
386   (unless (member kind '(nil :instance :class))
387     (error "illegal kind argument to ~S: ~S" 'instance-read-standard kind))
388   (let* ((index (gensym))
389          (value index))
390     `(locally (declare #.*optimize-speed*)
391        (let ((,index (svref ,pv ,pv-offset))
392              (,slots (truly-the simple-vector ,slots)))
393          (setq ,value (typecase ,index
394                         ;; FIXME: the line marked by KLUDGE below (and
395                         ;; the analogous spot in
396                         ;; INSTANCE-WRITE-STANDARD) is there purely to
397                         ;; suppress a type mismatch warning that
398                         ;; propagates through to user code.
399                         ;; Presumably SLOTS at this point can never
400                         ;; actually be NIL, but the compiler seems to
401                         ;; think it could, so we put this here to shut
402                         ;; it up.  (see also mail Rudi Schlatte
403                         ;; sbcl-devel 2003-09-21) -- CSR, 2003-11-30
404                         ,@(when (or (null kind) (eq kind :instance))
405                                 `((fixnum
406                                    (clos-slots-ref ,slots ,index))))
407                         ,@(when (or (null kind) (eq kind :class))
408                                 `((cons (cdr ,index))))
409                         (t
410                          +slot-unbound+)))
411          (if (eq ,value +slot-unbound+)
412              ,default
413              ,value)))))
414
415 (defmacro instance-read-custom (pv pv-offset parameter)
416   `(locally (declare #.*optimize-speed*)
417      (funcall (slot-info-reader (svref ,pv (1+ ,pv-offset))) ,parameter)))
418
419 ;;;; (SETF SLOT-VALUE)
420
421 (defmacro instance-write (pv-offset parameter position slot-name class new-value
422                           &optional check-type-p)
423   (ecase (slot-access-strategy (constant-value-or-nil class)
424                                (constant-value-or-nil slot-name)
425                                'writer)
426     (:standard
427      `(instance-write-standard
428        .pv. ,(slot-vector-symbol position)
429        ,pv-offset ,new-value
430        ;; KLUDGE: .GOOD-NEW-VALUE. is type-checked by the time this form
431        ;; is executed (if it is executed).
432        (accessor-set-slot-value ,parameter ,slot-name .good-new-value.)
433        ,(if (generate-fast-class-slot-access-p class slot-name)
434             :class :instance)
435        ,check-type-p))
436     (:custom
437      `(instance-write-custom .pv. ,pv-offset ,parameter ,new-value))
438     (:accessor
439      (if check-type-p
440          ;; FIXME: We don't want this here. If it's _possible_ the fast path
441          ;; is applicable, we want to use it as well.
442          `(safe-set-slot-value ,parameter ,slot-name ,new-value)
443          `(accessor-set-slot-value ,parameter ,slot-name ,new-value)))))
444
445 (defmacro instance-write-standard (pv slots pv-offset new-value default
446                                    &optional kind safep)
447   (unless (member kind '(nil :instance :class))
448     (error "illegal kind argument to ~S: ~S" 'instance-write-standard kind))
449   (let* ((index (gensym))
450          (new-value-form
451           (if safep
452               `(let ((.typecheckfun. (slot-info-typecheck (svref ,pv (1+ ,pv-offset)))))
453                  (declare (type (or function null) .typecheckfun.))
454                  (if .typecheckfun.
455                      (funcall .typecheckfun. ,new-value)
456                      ,new-value))
457               new-value)))
458     `(locally (declare #.*optimize-speed*)
459        (let ((.good-new-value. ,new-value-form)
460              (,index (svref ,pv ,pv-offset)))
461          (typecase ,index
462            ,@(when (or (null kind) (eq kind :instance))
463                    `((fixnum (and ,slots
464                                   (setf (clos-slots-ref ,slots ,index)
465                                         .good-new-value.)))))
466            ,@(when (or (null kind) (eq kind :class))
467                    `((cons (setf (cdr ,index) .good-new-value.))))
468            (t ,default))))))
469
470 (defmacro instance-write-custom (pv pv-offset parameter new-value)
471   `(locally (declare #.*optimize-speed*)
472      (funcall (slot-info-writer (svref ,pv (1+ ,pv-offset))) ,new-value ,parameter)))
473
474 ;;;; SLOT-BOUNDP
475
476 (defmacro instance-boundp (pv-offset parameter position slot-name class)
477   (ecase (slot-access-strategy (constant-value-or-nil class)
478                                (constant-value-or-nil slot-name)
479                                'boundp)
480     (:standard
481      `(instance-boundp-standard
482        .pv. ,(slot-vector-symbol position)
483        ,pv-offset (accessor-slot-boundp ,parameter ,slot-name)
484        ,(if (generate-fast-class-slot-access-p class slot-name)
485             :class :instance)))
486     (:custom
487      `(instance-boundp-custom .pv. ,pv-offset ,parameter))
488     (:accessor
489      `(accessor-slot-boundp ,parameter ,slot-name))))
490
491 (defmacro instance-boundp-standard (pv slots pv-offset default
492                                     &optional kind)
493   (unless (member kind '(nil :instance :class))
494     (error "illegal kind argument to ~S: ~S" 'instance-boundp-standard kind))
495   (let* ((index (gensym)))
496     `(locally (declare #.*optimize-speed*)
497        (let ((,index (svref ,pv ,pv-offset)))
498          (typecase ,index
499            ,@(when (or (null kind) (eq kind :instance))
500                    `((fixnum (not (and ,slots
501                                        (eq (clos-slots-ref ,slots ,index)
502                                            +slot-unbound+))))))
503            ,@(when (or (null kind) (eq kind :class))
504                    `((cons (not (eq (cdr ,index) +slot-unbound+)))))
505            (t ,default))))))
506
507 (defmacro instance-boundp-custom (pv pv-offset parameter)
508   `(locally (declare #.*optimize-speed*)
509      (funcall (slot-info-boundp (svref ,pv (1+ ,pv-offset))) ,parameter)))
510
511 ;;; This magic function has quite a job to do indeed.
512 ;;;
513 ;;; The careful reader will recall that <slots> contains all of the
514 ;;; optimized slot access forms produced by OPTIMIZE-INSTANCE-ACCESS.
515 ;;; Each of these is a call to either INSTANCE-READ or INSTANCE-WRITE.
516 ;;;
517 ;;; At the time these calls were produced, the first argument was
518 ;;; specified as the symbol .PV-OFFSET.; what we have to do now is
519 ;;; convert those pv-offset arguments into the actual number that is
520 ;;; the correct offset into the pv.
521 ;;;
522 ;;; But first, oh but first, we sort <slots> a bit so that for each
523 ;;; argument we have the slots in alphabetical order. This
524 ;;; canonicalizes the PV-TABLE's a bit and will hopefully lead to
525 ;;; having fewer PV's floating around. Even if the gain is only
526 ;;; modest, it costs nothing.
527 (defun slot-name-lists-from-slots (slots)
528   (let ((slots (mutate-slots slots)))
529     (let* ((slot-name-lists
530             (mapcar (lambda (parameter-entry)
531                       (cons nil (mapcar #'car (cdr parameter-entry))))
532                     slots)))
533       (mapcar (lambda (r+snl)
534                 (when (or (car r+snl) (cdr r+snl))
535                   r+snl))
536               slot-name-lists))))
537
538 (defun mutate-slots (slots)
539   (let ((sorted-slots (sort-slots slots))
540         (pv-offset -1))
541     (dolist (parameter-entry sorted-slots)
542       (dolist (slot-entry (cdr parameter-entry))
543         (incf pv-offset)
544         (dolist (form (cdr slot-entry))
545           (setf (cadr form) pv-offset))
546         ;; Count one more for the slot we use for SLOT-INFO.
547         (incf pv-offset)))
548     sorted-slots))
549
550 (defun symbol-pkg-name (sym)
551   (let ((pkg (symbol-package sym)))
552     (if pkg (package-name pkg) "")))
553
554 ;;; FIXME: Because of the existence of UNINTERN and RENAME-PACKAGE,
555 ;;; the part of this ordering which is based on SYMBOL-PKG-NAME is not
556 ;;; stable. This ordering is only used in to
557 ;;; SLOT-NAME-LISTS-FROM-SLOTS, where it serves to "canonicalize the
558 ;;; PV-TABLE's a bit and will hopefully lead to having fewer PV's
559 ;;; floating around", so it sounds as though the instability won't
560 ;;; actually lead to bugs, just small inefficiency. But still, it
561 ;;; would be better to reimplement this function as a comparison based
562 ;;; on SYMBOL-HASH:
563 ;;;   * stable comparison
564 ;;;   * smaller code (here, and in being able to discard SYMBOL-PKG-NAME)
565 ;;;   * faster code.
566 (defun symbol-lessp (a b)
567   (if (eq (symbol-package a)
568           (symbol-package b))
569       (string-lessp (symbol-name a)
570                     (symbol-name b))
571       (string-lessp (symbol-pkg-name a)
572                     (symbol-pkg-name b))))
573
574 (defun symbol-or-cons-lessp (a b)
575   (etypecase a
576     (symbol (etypecase b
577               (symbol (symbol-lessp a b))
578               (cons t)))
579     (cons   (etypecase b
580               (symbol nil)
581               (cons (if (eq (car a) (car b))
582                         (symbol-or-cons-lessp (cdr a) (cdr b))
583                         (symbol-or-cons-lessp (car a) (car b))))))))
584
585 (defun sort-slots (slots)
586   (mapcar (lambda (parameter-entry)
587             (cons (car parameter-entry)
588                   (sort (cdr parameter-entry)   ;slot entries
589                         #'symbol-or-cons-lessp
590                         :key #'car)))
591           slots))
592
593 \f
594 ;;;; This needs to work in terms of metatypes and also needs to work
595 ;;;; for automatically generated reader and writer functions.
596 ;;;; Automatically generated reader and writer functions use this
597 ;;;; stuff too.
598
599 (defmacro pv-binding ((required-parameters slot-name-lists pv-table-form)
600                       &body body)
601   (let (slot-vars pv-parameters)
602     (loop for slots in slot-name-lists
603           for required-parameter in required-parameters
604           for i from 0
605           do (when slots
606                (push required-parameter pv-parameters)
607                (push (slot-vector-symbol i) slot-vars)))
608     `(pv-binding1 (,pv-table-form
609                    ,(nreverse pv-parameters) ,(nreverse slot-vars))
610        ,@body)))
611
612 (defmacro pv-binding1 ((pv-table-form pv-parameters slot-vars)
613                        &body body)
614   `(pv-env (,pv-table-form ,pv-parameters)
615      (let (,@(mapcar (lambda (slot-var p) `(,slot-var (get-slots-or-nil ,p)))
616                      slot-vars pv-parameters))
617        (declare (ignorable ,@(mapcar #'identity slot-vars)))
618        ,@body)))
619
620 ;;; This will only be visible in PV-ENV when the default MAKE-METHOD-LAMBDA is
621 ;;; overridden.
622 (define-symbol-macro pv-env-environment overridden)
623
624 (defmacro pv-env (&environment env
625                   (pv-table-form pv-parameters)
626                   &rest forms)
627   ;; Decide which expansion to use based on the state of the PV-ENV-ENVIRONMENT
628   ;; symbol-macrolet.
629   (if (eq (macroexpand 'pv-env-environment env) 'default)
630       `(locally (declare (simple-vector .pv.))
631          ,@forms)
632       `(let* ((.pv-table. ,pv-table-form)
633               (.pv. (pv-table-lookup-pv-args .pv-table. ,@pv-parameters)))
634         (declare ,(make-pv-type-declaration '.pv.))
635         ,@forms)))
636
637 (defun split-declarations (body args req-args cnm-p parameters-setqd)
638   (let ((inner-decls nil)
639         (outer-decls nil)
640         decl)
641     (loop
642       (when (null body)
643         (return nil))
644       (setq decl (car body))
645       (unless (and (consp decl) (eq (car decl) 'declare))
646         (return nil))
647       (dolist (form (cdr decl))
648         (when (consp form)
649           (let* ((name (car form)))
650             (cond ((eq '%class name)
651                    (push `(declare ,form) inner-decls))
652                   ((or (member name '(ignore ignorable special dynamic-extent type))
653                        (info :type :kind name))
654                    (let* ((inners nil)
655                           (outers nil)
656                           (tail (cdr form))
657                           (head (if (eq 'type name)
658                                     (list name (pop tail))
659                                     (list name))))
660                      (dolist (var tail)
661                        (if (member var args :test #'eq)
662                            ;; Quietly remove IGNORE declarations on
663                            ;; args when a next-method is involved, to
664                            ;; prevent compiler warnings about ignored
665                            ;; args being read.
666                            (unless (and (eq 'ignore name) (member var req-args :test #'eq) (or cnm-p (member var parameters-setqd)))
667                              (push var outers))
668                            (push var inners)))
669                      (when outers
670                        (push `(declare (,@head ,@outers)) outer-decls))
671                      (when inners
672                        (push `(declare (,@head ,@inners)) inner-decls))))
673                   (t
674                    ;; All other declarations are not variable declarations,
675                    ;; so they become outer declarations.
676                    (push `(declare ,form) outer-decls))))))
677       (setq body (cdr body)))
678     (values outer-decls inner-decls body)))
679
680 ;;; Convert a lambda expression containing a SB-PCL::%METHOD-NAME
681 ;;; declaration (which is a naming style internal to PCL) into an
682 ;;; SB-INT:NAMED-LAMBDA expression (which is a naming style used
683 ;;; throughout SBCL, understood by the main compiler); or if there's
684 ;;; no SB-PCL::%METHOD-NAME declaration, then just return the original
685 ;;; lambda expression.
686 (defun name-method-lambda (method-lambda)
687   (let ((method-name *method-name*))
688     (if method-name
689         `(named-lambda (slow-method ,@method-name) ,@(rest method-lambda))
690         method-lambda)))
691
692 (defun make-method-initargs-form-internal (method-lambda initargs env)
693   (declare (ignore env))
694   (let (method-lambda-args
695         lmf ; becomes body of function
696         lmf-params)
697     (if (not (and (= 3 (length method-lambda))
698                   (= 2 (length (setq method-lambda-args (cadr method-lambda))))
699                   (consp (setq lmf (third method-lambda)))
700                   (eq 'simple-lexical-method-functions (car lmf))
701                   (eq (car method-lambda-args)
702                       (cadr (setq lmf-params (cadr lmf))))
703                   (eq (cadr method-lambda-args)
704                       (caddr lmf-params))))
705         `(list* :function ,(name-method-lambda method-lambda)
706                 ',initargs)
707         (let* ((lambda-list (car lmf-params))
708                (nreq 0)
709                (restp nil)
710                (args nil))
711           (dolist (arg lambda-list)
712             (when (member arg '(&optional &rest &key))
713               (setq restp t)
714               (return nil))
715             (when (eq arg '&aux)
716               (return nil))
717             (incf nreq)
718             (push arg args))
719           (setq args (nreverse args))
720           (setf (getf (getf initargs 'plist) :arg-info) (cons nreq restp))
721           (make-method-initargs-form-internal1
722            initargs (cddr lmf) args lmf-params restp)))))
723
724 (defun lambda-list-parameter-names (lambda-list)
725   ;; Given a valid lambda list, extract the parameter names.
726   (loop for x in lambda-list
727         with res = nil
728         do (unless (member x lambda-list-keywords :test #'eq)
729              (if (consp x)
730                  (let ((name (car x)))
731                    (if (consp name)
732                        ;; ... ((:BAR FOO) 1)
733                        (push (second name) res)
734                        ;; ... (FOO 1)
735                        (push name res))
736                    ;; ... (... 1 FOO-P)
737                    (let ((name-p (cddr x)))
738                      (when name-p
739                        (push (car name-p) res))))
740                  ;; ... FOO
741                  (push x res)))
742         finally (return res)))
743
744 (defun make-method-initargs-form-internal1
745     (initargs body req-args lmf-params restp)
746   (let* (;; The lambda-list of the method, minus specifiers
747          (lambda-list (car lmf-params))
748          ;; Names of the parameters that will be in the outermost lambda-list
749          ;; (and whose bound declarations thus need to be in OUTER-DECLS).
750          (outer-parameters req-args)
751          ;; The lambda-list used by BIND-ARGS
752          (bind-list lambda-list)
753          (parameters-setqd (getf (cdr lmf-params) :parameters-setqd))
754          (auxp (member '&aux bind-list))
755          (call-next-method-p (getf (cdr lmf-params) :call-next-method-p)))
756     ;; Try to use the normal function call machinery instead of BIND-ARGS
757     ;; binding the arguments, unless:
758     (unless (or ;; If all arguments are required, BIND-ARGS will be a no-op
759                 ;; in any case.
760                 (and (not restp) (not auxp))
761                 ;; CALL-NEXT-METHOD wants to use BIND-ARGS, and needs a
762                 ;; list of all non-required arguments.
763                 call-next-method-p)
764       (setf ;; We don't want a binding for .REST-ARG.
765             restp nil
766             ;; Get all the parameters for declaration parsing
767             outer-parameters (lambda-list-parameter-names lambda-list)
768             ;; Ensure that BIND-ARGS won't do anything (since
769             ;; BIND-LIST won't contain any non-required parameters,
770             ;; and REQ-ARGS will be of an equal length). We still want
771             ;; to pass BIND-LIST to FAST-LEXICAL-METHOD-FUNCTIONS so
772             ;; that BIND-FAST-LEXICAL-METHOD-FUNCTIONS can take care
773             ;; of rebinding SETQd required arguments around the method
774             ;; body.
775             bind-list req-args))
776     (multiple-value-bind (outer-decls inner-decls body-sans-decls)
777         (split-declarations
778          body outer-parameters req-args call-next-method-p parameters-setqd)
779       (let* ((rest-arg (when restp
780                          '.rest-arg.))
781              (fmf-lambda-list (if rest-arg
782                                   (append req-args (list '&rest rest-arg))
783                                   (if call-next-method-p
784                                       req-args
785                                       lambda-list))))
786         `(list*
787           :function
788           (let* ((fmf (,(if *method-name* 'named-lambda 'lambda)
789                         ,@(when *method-name*
790                                 ;; function name
791                                 (list `(fast-method ,@*method-name*)))
792                         ;; The lambda-list of the FMF
793                         (.pv. .next-method-call. ,@fmf-lambda-list)
794                         ;; body of the function
795                         (declare (ignorable .pv. .next-method-call.)
796                                  (disable-package-locks pv-env-environment))
797                         ,@outer-decls
798                         (symbol-macrolet ((pv-env-environment default))
799                           (fast-lexical-method-functions
800                               (,bind-list .next-method-call. ,req-args ,rest-arg
801                                 ,@(cdddr lmf-params))
802                             ,@inner-decls
803                             ,@body-sans-decls))))
804                  (mf (%make-method-function fmf nil)))
805             (set-funcallable-instance-function
806              mf (method-function-from-fast-function fmf ',(getf initargs 'plist)))
807             mf)
808           ',initargs)))))
809
810 ;;; Use arrays and hash tables and the fngen stuff to make this much
811 ;;; better. It doesn't really matter, though, because a function
812 ;;; returned by this will get called only when the user explicitly
813 ;;; funcalls a result of method-function. BUT, this is needed to make
814 ;;; early methods work.
815 (defun method-function-from-fast-function (fmf plist)
816   (declare (type function fmf))
817   (let* ((method-function nil)
818          (snl (getf plist :slot-name-lists))
819          (pv-table (when snl
820                      (intern-pv-table :slot-name-lists snl)))
821          (arg-info (getf plist :arg-info))
822          (nreq (car arg-info))
823          (restp (cdr arg-info)))
824     (setq method-function
825           (lambda (method-args next-methods)
826             (let* ((pv (when pv-table
827                          (get-pv method-args pv-table)))
828                    (nm (car next-methods))
829                    (nms (cdr next-methods))
830                    (nmc (when nm
831                           (make-method-call
832                            :function (if (std-instance-p nm)
833                                          (method-function nm)
834                                          nm)
835                            :call-method-args (list nms)))))
836               (apply fmf pv nmc method-args))))
837     ;; FIXME: this looks dangerous.
838     (let* ((fname (%fun-name fmf)))
839       (when (and fname (eq (car fname) 'fast-method))
840         (set-fun-name method-function (cons 'slow-method (cdr fname)))))
841     method-function))
842
843 ;;; this is similar to the above, only not quite.  Only called when
844 ;;; the MOP is heavily involved.  Not quite parallel to
845 ;;; METHOD-FUNCTION-FROM-FAST-METHOD-FUNCTION, because we can close
846 ;;; over the actual PV-CELL in this case.
847 (defun method-function-from-fast-method-call (fmc)
848   (let* ((fmf (fast-method-call-function fmc))
849          (pv (fast-method-call-pv fmc))
850          (arg-info (fast-method-call-arg-info fmc))
851          (nreq (car arg-info))
852          (restp (cdr arg-info)))
853     (lambda (method-args next-methods)
854       (let* ((nm (car next-methods))
855              (nms (cdr next-methods))
856              (nmc (when nm
857                     (make-method-call
858                      :function (if (std-instance-p nm)
859                                    (method-function nm)
860                                    nm)
861                      :call-method-args (list nms)))))
862         (apply fmf pv nmc method-args)))))
863
864 (defun get-pv (method-args pv-table)
865   (let ((pv-wrappers (pv-wrappers-from-all-args pv-table method-args)))
866     (when pv-wrappers
867       (pv-table-lookup pv-table pv-wrappers))))
868
869 (defun pv-table-lookup-pv-args (pv-table &rest pv-parameters)
870   (pv-table-lookup pv-table (pv-wrappers-from-pv-args pv-parameters)))
871
872 (defun pv-wrappers-from-pv-args (&rest args)
873   (loop for arg in args
874         collect (valid-wrapper-of arg)))
875
876 (defun pv-wrappers-from-all-args (pv-table args)
877   (loop for snl in (pv-table-slot-name-lists pv-table)
878         and arg in args
879         when snl
880         collect (valid-wrapper-of arg)))
881
882 ;;; Return the subset of WRAPPERS which is used in the cache
883 ;;; of PV-TABLE.
884 (defun pv-wrappers-from-all-wrappers (pv-table wrappers)
885   (loop for snl in (pv-table-slot-name-lists pv-table) and w in wrappers
886         when snl
887         collect w))