setf: read-modify-write macros should deal with multi-value places gracefully.
[sbcl.git] / src / code / early-setf.lisp
1 ;;;; SETF and friends (except for stuff defined with COLLECT, which
2 ;;;; comes later)
3 ;;;;
4 ;;;; Note: The expansions for SETF and friends sometimes create
5 ;;;; needless LET-bindings of argument values. The compiler will
6 ;;;; remove most of these spurious bindings, so SETF doesn't worry too
7 ;;;; much about creating them.
8
9 ;;;; This software is part of the SBCL system. See the README file for
10 ;;;; more information.
11 ;;;;
12 ;;;; This software is derived from the CMU CL system, which was
13 ;;;; written at Carnegie Mellon University and released into the
14 ;;;; public domain. The software is in the public domain and is
15 ;;;; provided with absolutely no warranty. See the COPYING and CREDITS
16 ;;;; files for more information.
17
18 (in-package "SB!IMPL")
19
20 ;;; The inverse for a generalized-variable reference function is stored in
21 ;;; one of two ways:
22 ;;;
23 ;;; A SETF inverse property corresponds to the short form of DEFSETF. It is
24 ;;; the name of a function takes the same args as the reference form, plus a
25 ;;; new-value arg at the end.
26 ;;;
27 ;;; A SETF method expander is created by the long form of DEFSETF or
28 ;;; by DEFINE-SETF-EXPANDER. It is a function that is called on the reference
29 ;;; form and that produces five values: a list of temporary variables, a list
30 ;;; of value forms, a list of the single store-value form, a storing function,
31 ;;; and an accessing function.
32 (declaim (ftype (function (t &optional (or null sb!c::lexenv))) sb!xc:get-setf-expansion))
33 (defun sb!xc:get-setf-expansion (form &optional environment)
34   #!+sb-doc
35   "Return five values needed by the SETF machinery: a list of temporary
36    variables, a list of values with which to fill them, a list of temporaries
37    for the new values, the setting function, and the accessing function."
38   (let (temp)
39     (cond ((symbolp form)
40            (multiple-value-bind (expansion expanded)
41                (%macroexpand-1 form environment)
42              (if expanded
43                  (sb!xc:get-setf-expansion expansion environment)
44                  (let ((new-var (sb!xc:gensym "NEW")))
45                    (values nil nil (list new-var)
46                            `(setq ,form ,new-var) form)))))
47           ;; Local functions inhibit global SETF methods.
48           ((and environment
49                 (let ((name (car form)))
50                   (dolist (x (sb!c::lexenv-funs environment))
51                     (when (and (eq (car x) name)
52                                (not (sb!c::defined-fun-p (cdr x))))
53                       (return t)))))
54            (expand-or-get-setf-inverse form environment))
55           ((setq temp (info :setf :inverse (car form)))
56            (get-setf-method-inverse form `(,temp) nil environment))
57           ((setq temp (info :setf :expander (car form)))
58            ;; KLUDGE: It may seem as though this should go through
59            ;; *MACROEXPAND-HOOK*, but the ANSI spec seems fairly explicit
60            ;; that *MACROEXPAND-HOOK* is a hook for MACROEXPAND-1, not
61            ;; for macroexpansion in general. -- WHN 19991128
62            (funcall temp
63                     form
64                     ;; As near as I can tell from the ANSI spec,
65                     ;; macroexpanders have a right to expect an actual
66                     ;; lexical environment, not just a NIL which is to
67                     ;; be interpreted as a null lexical environment.
68                     ;; -- WHN 19991128
69                     (coerce-to-lexenv environment)))
70           (t
71            (expand-or-get-setf-inverse form environment)))))
72
73 ;;; GET-SETF-METHOD existed in pre-ANSI Common Lisp, and various code inherited
74 ;;; from CMU CL uses it repeatedly, so rather than rewrite a lot of code to not
75 ;;; use it, we just define it in terms of ANSI's GET-SETF-EXPANSION (or
76 ;;; actually, the cross-compiler version of that, i.e.
77 ;;; SB!XC:GET-SETF-EXPANSION).
78 (declaim (ftype (function (t &optional (or null sb!c::lexenv))) get-setf-method))
79 (defun get-setf-method (form &optional environment)
80   #!+sb-doc
81   "This is a specialized-for-one-value version of GET-SETF-EXPANSION (and
82 a relic from pre-ANSI Common Lisp). Portable ANSI code should use
83 GET-SETF-EXPANSION directly."
84   (multiple-value-bind (temps value-forms store-vars store-form access-form)
85       (sb!xc:get-setf-expansion form environment)
86     (when (cdr store-vars)
87       (error "GET-SETF-METHOD used for a form with multiple store ~
88               variables:~%  ~S"
89              form))
90     (values temps value-forms store-vars store-form access-form)))
91
92 ;;; If a macro, expand one level and try again. If not, go for the
93 ;;; SETF function.
94 (declaim (ftype (function (t (or null sb!c::lexenv)))
95                 expand-or-get-setf-inverse))
96 (defun expand-or-get-setf-inverse (form environment)
97   (multiple-value-bind (expansion expanded)
98       (%macroexpand-1 form environment)
99     (if expanded
100         (sb!xc:get-setf-expansion expansion environment)
101         (get-setf-method-inverse form
102                                  `(funcall #'(setf ,(car form)))
103                                  t
104                                  environment))))
105
106 (defun get-setf-method-inverse (form inverse setf-fun environment)
107   (let ((new-var (sb!xc:gensym "NEW"))
108         (vars nil)
109         (vals nil)
110         (args nil))
111     (dolist (x (reverse (cdr form)))
112       (cond ((sb!xc:constantp x environment)
113              (push x args))
114             (t
115              (let ((temp (gensym "TMP")))
116                (push temp args)
117                (push temp vars)
118                (push x vals)))))
119     (values vars
120             vals
121             (list new-var)
122             (if setf-fun
123                 `(,@inverse ,new-var ,@args)
124                 `(,@inverse ,@args ,new-var))
125             `(,(car form) ,@args))))
126 \f
127 ;;;; SETF itself
128
129 ;;; Except for atoms, we always call GET-SETF-EXPANSION, since it has
130 ;;; some non-trivial semantics. But when there is a setf inverse, and
131 ;;; G-S-E uses it, then we return a call to the inverse, rather than
132 ;;; returning a hairy LET form. This is probably important mainly as a
133 ;;; convenience in allowing the use of SETF inverses without the full
134 ;;; interpreter.
135 (defmacro-mundanely setf (&rest args &environment env)
136   #!+sb-doc
137   "Takes pairs of arguments like SETQ. The first is a place and the second
138   is the value that is supposed to go into that place. Returns the last
139   value. The place argument may be any of the access forms for which SETF
140   knows a corresponding setting form."
141   (let ((nargs (length args)))
142     (cond
143      ((= nargs 2)
144       (let ((place (first args))
145             (value-form (second args)))
146         (if (atom place)
147           `(setq ,place ,value-form)
148           (multiple-value-bind (dummies vals newval setter getter)
149               (sb!xc:get-setf-expansion place env)
150             (declare (ignore getter))
151             (let ((inverse (info :setf :inverse (car place))))
152               (if (and inverse (eq inverse (car setter)))
153                 `(,inverse ,@(cdr place) ,value-form)
154                 `(let* (,@(mapcar #'list dummies vals))
155                    (multiple-value-bind ,newval ,value-form
156                      ,setter))))))))
157      ((oddp nargs)
158       (error "odd number of args to SETF"))
159      (t
160       (do ((a args (cddr a))
161            (reversed-setfs nil))
162           ((null a)
163            `(progn ,@(nreverse reversed-setfs)))
164         (push (list 'setf (car a) (cadr a)) reversed-setfs))))))
165 \f
166 ;;;; various SETF-related macros
167
168 (defmacro-mundanely shiftf (&whole form &rest args &environment env)
169   #!+sb-doc
170   "One or more SETF-style place expressions, followed by a single
171    value expression. Evaluates all of the expressions in turn, then
172    assigns the value of each expression to the place on its left,
173    returning the value of the leftmost."
174   (when (< (length args) 2)
175     (error "~S called with too few arguments: ~S" 'shiftf form))
176   (let (let*-bindings mv-bindings setters getters)
177     (dolist (arg (butlast args))
178       (multiple-value-bind (temps subforms store-vars setter getter)
179           (sb!xc:get-setf-expansion arg env)
180         (mapc (lambda (tmp form)
181                 (push `(,tmp ,form) let*-bindings))
182               temps
183               subforms)
184         (push store-vars mv-bindings)
185         (push setter setters)
186         (push getter getters)))
187     ;; Handle the last arg specially here. The getter is just the last
188     ;; arg itself.
189     (push (car (last args)) getters)
190
191     ;; Reverse the collected lists so last bit looks nicer.
192     (setf let*-bindings (nreverse let*-bindings)
193           mv-bindings (nreverse mv-bindings)
194           setters (nreverse setters)
195           getters (nreverse getters))
196
197     (labels ((thunk (mv-bindings getters)
198                (if mv-bindings
199                    `((multiple-value-bind
200                            ,(car mv-bindings)
201                          ,(car getters)
202                        ,@(thunk (cdr mv-bindings) (cdr getters))))
203                    `(,@setters))))
204       `(let ,let*-bindings
205         (multiple-value-bind ,(car mv-bindings)
206             ,(car getters)
207           ,@(thunk mv-bindings (cdr getters))
208           (values ,@(car mv-bindings)))))))
209
210 (defmacro-mundanely push (obj place &environment env)
211   #!+sb-doc
212   "Takes an object and a location holding a list. Conses the object onto
213   the list, returning the modified list. OBJ is evaluated before PLACE."
214   (multiple-value-bind (dummies vals newval setter getter)
215       (sb!xc:get-setf-expansion place env)
216     (let ((g (gensym)))
217       `(let* ((,g ,obj)
218               ,@(mapcar #'list dummies vals)
219               (,(car newval) (cons ,g ,getter))
220               ,@(cdr newval))
221          ,setter))))
222
223 (defmacro-mundanely pushnew (obj place &rest keys
224                              &key key test test-not &environment env)
225   #!+sb-doc
226   "Takes an object and a location holding a list. If the object is
227   already in the list, does nothing; otherwise, conses the object onto
228   the list. Returns the modified list. If there is a :TEST keyword, this
229   is used for the comparison."
230   (declare (ignore key test test-not))
231   (multiple-value-bind (dummies vals newval setter getter)
232       (sb!xc:get-setf-expansion place env)
233     (let ((g (gensym)))
234       `(let* ((,g ,obj)
235               ,@(mapcar #'list dummies vals)
236               (,(car newval) (adjoin ,g ,getter ,@keys))
237               ,@(cdr newval))
238          ,setter))))
239
240 (defmacro-mundanely pop (place &environment env)
241   #!+sb-doc
242   "The argument is a location holding a list. Pops one item off the front
243   of the list and returns it."
244   (multiple-value-bind (dummies vals newval setter getter)
245       (sb!xc:get-setf-expansion place env)
246     (let ((list-head (gensym)))
247       `(let* (,@(mapcar #'list dummies vals)
248               (,list-head ,getter)
249               (,(car newval) (cdr ,list-head))
250               ,@(cdr newval))
251          ,setter
252          (car ,list-head)))))
253
254 (defmacro-mundanely remf (place indicator &environment env)
255   #!+sb-doc
256   "Place may be any place expression acceptable to SETF, and is expected
257   to hold a property list or (). This list is destructively altered to
258   remove the property specified by the indicator. Returns T if such a
259   property was present, NIL if not."
260   (multiple-value-bind (dummies vals newval setter getter)
261       (sb!xc:get-setf-expansion place env)
262     (let ((ind-temp (gensym))
263           (local1 (gensym))
264           (local2 (gensym)))
265       `(let* (,@(mapcar #'list dummies vals)
266               ;; See ANSI 5.1.3 for why we do out-of-order evaluation
267               (,ind-temp ,indicator)
268               (,(car newval) ,getter)
269               ,@(cdr newval))
270          (do ((,local1 ,(car newval) (cddr ,local1))
271               (,local2 nil ,local1))
272              ((atom ,local1) nil)
273              (cond ((atom (cdr ,local1))
274                     (error "Odd-length property list in REMF."))
275                    ((eq (car ,local1) ,ind-temp)
276                     (cond (,local2
277                            (rplacd (cdr ,local2) (cddr ,local1))
278                            (return t))
279                           (t (setq ,(car newval) (cddr ,(car newval)))
280                              ,setter
281                              (return t))))))))))
282
283 ;;; we can't use DEFINE-MODIFY-MACRO because of ANSI 5.1.3
284 (defmacro-mundanely incf (place &optional (delta 1) &environment env)
285   #!+sb-doc
286   "The first argument is some location holding a number. This number is
287   incremented by the second argument, DELTA, which defaults to 1."
288   (multiple-value-bind (dummies vals newval setter getter)
289       (sb!xc:get-setf-expansion place env)
290     (let ((d (gensym)))
291       `(let* (,@(mapcar #'list dummies vals)
292               (,d ,delta)
293               (,(car newval) (+ ,getter ,d))
294               ,@(cdr newval))
295          ,setter))))
296
297 (defmacro-mundanely decf (place &optional (delta 1) &environment env)
298   #!+sb-doc
299   "The first argument is some location holding a number. This number is
300   decremented by the second argument, DELTA, which defaults to 1."
301   (multiple-value-bind (dummies vals newval setter getter)
302       (sb!xc:get-setf-expansion place env)
303     (let ((d (gensym)))
304       `(let* (,@(mapcar #'list dummies vals)
305               (,d ,delta)
306               (,(car newval) (- ,getter ,d))
307               ,@(cdr newval))
308          ,setter))))
309 \f
310 ;;;; DEFINE-MODIFY-MACRO stuff
311
312 (def!macro sb!xc:define-modify-macro (name lambda-list function &optional doc-string)
313   #!+sb-doc
314   "Creates a new read-modify-write macro like PUSH or INCF."
315   (let ((other-args nil)
316         (rest-arg nil)
317         (env (make-symbol "ENV"))          ; To beautify resulting arglist.
318         (reference (make-symbol "PLACE"))) ; Note that these will be nonexistent
319                                            ;  in the final expansion anyway.
320     ;; Parse out the variable names and &REST arg from the lambda list.
321     (do ((ll lambda-list (cdr ll))
322          (arg nil))
323         ((null ll))
324       (setq arg (car ll))
325       (cond ((eq arg '&optional))
326             ((eq arg '&rest)
327              (if (symbolp (cadr ll))
328                (setq rest-arg (cadr ll))
329                (error "Non-symbol &REST argument in definition of ~S." name))
330              (if (null (cddr ll))
331                (return nil)
332                (error "Illegal stuff after &REST argument.")))
333             ((memq arg '(&key &allow-other-keys &aux))
334              (error "~S not allowed in DEFINE-MODIFY-MACRO lambda list." arg))
335             ((symbolp arg)
336              (push arg other-args))
337             ((and (listp arg) (symbolp (car arg)))
338              (push (car arg) other-args))
339             (t (error "Illegal stuff in lambda list."))))
340     (setq other-args (nreverse other-args))
341     `(#-sb-xc-host sb!xc:defmacro
342       #+sb-xc-host defmacro-mundanely
343          ,name (,reference ,@lambda-list &environment ,env)
344        ,doc-string
345        (multiple-value-bind (dummies vals newval setter getter)
346            (sb!xc:get-setf-expansion ,reference ,env)
347          (let ()
348              `(let* (,@(mapcar #'list dummies vals)
349                      (,(car newval)
350                       ,,(if rest-arg
351                           `(list* ',function getter ,@other-args ,rest-arg)
352                           `(list ',function getter ,@other-args)))
353                      ,@(cdr newval))
354                 ,setter))))))
355 \f
356 ;;;; DEFSETF
357
358 (eval-when (#-sb-xc :compile-toplevel :load-toplevel :execute)
359   ;;; Assign SETF macro information for NAME, making all appropriate checks.
360   (defun assign-setf-macro (name expander inverse doc)
361     (with-single-package-locked-error
362         (:symbol name "defining a setf-expander for ~A"))
363     (cond ((gethash name sb!c:*setf-assumed-fboundp*)
364            (warn
365             "defining setf macro for ~S when ~S was previously ~
366              treated as a function"
367             name
368             `(setf ,name)))
369           ((not (fboundp `(setf ,name)))
370            ;; All is well, we don't need any warnings.
371            (values))
372           ((not (eq (symbol-package name) (symbol-package 'aref)))
373            (style-warn "defining setf macro for ~S when ~S is fbound"
374                        name `(setf ,name))))
375     (remhash name sb!c:*setf-assumed-fboundp*)
376     ;; FIXME: It's probably possible to join these checks into one form which
377     ;; is appropriate both on the cross-compilation host and on the target.
378     (when (or inverse (info :setf :inverse name))
379       (setf (info :setf :inverse name) inverse))
380     (when (or expander (info :setf :expander name))
381       (setf (info :setf :expander name) expander))
382     (when doc
383       (setf (fdocumentation name 'setf) doc))
384     name))
385
386 (def!macro sb!xc:defsetf (access-fn &rest rest)
387   #!+sb-doc
388   "Associates a SETF update function or macro with the specified access
389   function or macro. The format is complex. See the manual for details."
390   (cond ((and (not (listp (car rest))) (symbolp (car rest)))
391          `(eval-when (:load-toplevel :compile-toplevel :execute)
392             (assign-setf-macro ',access-fn
393                                nil
394                                ',(car rest)
395                                 ,(when (and (car rest) (stringp (cadr rest)))
396                                    `',(cadr rest)))))
397         ((and (cdr rest) (listp (cadr rest)))
398          (destructuring-bind
399              (lambda-list (&rest store-variables) &body body)
400              rest
401            (with-unique-names (whole access-form environment)
402              (multiple-value-bind (body local-decs doc)
403                  (parse-defmacro `(,lambda-list ,@store-variables)
404                                  whole body access-fn 'defsetf
405                                  :environment environment
406                                  :anonymousp t)
407                `(eval-when (:compile-toplevel :load-toplevel :execute)
408                   (assign-setf-macro
409                    ',access-fn
410                    (lambda (,access-form ,environment)
411                      ,@local-decs
412                      (%defsetf ,access-form ,(length store-variables)
413                                (lambda (,whole)
414                                  ,body)))
415                    nil
416                    ',doc))))))
417         (t
418          (error "ill-formed DEFSETF for ~S" access-fn))))
419
420 (defun %defsetf (orig-access-form num-store-vars expander)
421   (declare (type function expander))
422   (let (subforms
423         subform-vars
424         subform-exprs
425         store-vars)
426     (dolist (subform (cdr orig-access-form))
427       (if (constantp subform)
428         (push subform subforms)
429         (let ((var (gensym)))
430           (push var subforms)
431           (push var subform-vars)
432           (push subform subform-exprs))))
433     (dotimes (i num-store-vars)
434       (push (gensym) store-vars))
435     (let ((r-subforms (nreverse subforms))
436           (r-subform-vars (nreverse subform-vars))
437           (r-subform-exprs (nreverse subform-exprs))
438           (r-store-vars (nreverse store-vars)))
439       (values r-subform-vars
440               r-subform-exprs
441               r-store-vars
442               (funcall expander (cons r-subforms r-store-vars))
443               `(,(car orig-access-form) ,@r-subforms)))))
444 \f
445 ;;;; DEFMACRO DEFINE-SETF-EXPANDER and various DEFINE-SETF-EXPANDERs
446
447 ;;; DEFINE-SETF-EXPANDER is a lot like DEFMACRO.
448 (def!macro sb!xc:define-setf-expander (access-fn lambda-list &body body)
449   #!+sb-doc
450   "Syntax like DEFMACRO, but creates a setf expander function. The body
451   of the definition must be a form that returns five appropriate values."
452   (unless (symbolp access-fn)
453     (error "~S access-function name ~S is not a symbol."
454            'sb!xc:define-setf-expander access-fn))
455   (with-unique-names (whole environment)
456     (multiple-value-bind (body local-decs doc)
457         (parse-defmacro lambda-list whole body access-fn
458                         'sb!xc:define-setf-expander
459                         :environment environment)
460       `(eval-when (:compile-toplevel :load-toplevel :execute)
461          (assign-setf-macro ',access-fn
462                             (lambda (,whole ,environment)
463                               ,@local-decs
464                               ,body)
465                             nil
466                             ',doc)))))
467
468 (sb!xc:define-setf-expander getf (place prop
469                                   &optional default
470                                   &environment env)
471   (declare (type sb!c::lexenv env))
472   (multiple-value-bind (temps values stores set get)
473       (get-setf-method place env)
474     (let ((newval (gensym))
475           (ptemp (gensym))
476           (def-temp (if default (gensym))))
477       (values `(,@temps ,ptemp ,@(if default `(,def-temp)))
478               `(,@values ,prop ,@(if default `(,default)))
479               `(,newval)
480               `(let ((,(car stores) (%putf ,get ,ptemp ,newval)))
481                  ,set
482                  ,newval)
483               `(getf ,get ,ptemp ,@(if default `(,def-temp)))))))
484
485 (sb!xc:define-setf-expander get (symbol prop &optional default)
486   (let ((symbol-temp (gensym))
487         (prop-temp (gensym))
488         (def-temp (gensym))
489         (newval (gensym)))
490     (values `(,symbol-temp ,prop-temp ,@(if default `(,def-temp)))
491             `(,symbol ,prop ,@(if default `(,default)))
492             (list newval)
493             `(%put ,symbol-temp ,prop-temp ,newval)
494             `(get ,symbol-temp ,prop-temp ,@(if default `(,def-temp))))))
495
496 (sb!xc:define-setf-expander gethash (key hashtable &optional default)
497   (let ((key-temp (gensym))
498         (hashtable-temp (gensym))
499         (default-temp (gensym))
500         (new-value-temp (gensym)))
501     (values
502      `(,key-temp ,hashtable-temp ,@(if default `(,default-temp)))
503      `(,key ,hashtable ,@(if default `(,default)))
504      `(,new-value-temp)
505      `(%puthash ,key-temp ,hashtable-temp ,new-value-temp)
506      `(gethash ,key-temp ,hashtable-temp ,@(if default `(,default-temp))))))
507
508 (sb!xc:define-setf-expander logbitp (index int &environment env)
509   (declare (type sb!c::lexenv env))
510   (multiple-value-bind (temps vals stores store-form access-form)
511       (get-setf-method int env)
512     (let ((ind (gensym))
513           (store (gensym))
514           (stemp (first stores)))
515       (values `(,ind ,@temps)
516               `(,index
517                 ,@vals)
518               (list store)
519               `(let ((,stemp
520                       (dpb (if ,store 1 0) (byte 1 ,ind) ,access-form)))
521                  ,store-form
522                  ,store)
523               `(logbitp ,ind ,access-form)))))
524
525 ;;; CMU CL had a comment here that:
526 ;;;   Evil hack invented by the gnomes of Vassar Street (though not as evil as
527 ;;;   it used to be.)  The function arg must be constant, and is converted to
528 ;;;   an APPLY of the SETF function, which ought to exist.
529 ;;;
530 ;;; It may not be clear (wasn't to me..) that this is a standard thing, but See
531 ;;; "5.1.2.5 APPLY Forms as Places" in the ANSI spec. I haven't actually
532 ;;; verified that this code has any correspondence to that code, but at least
533 ;;; ANSI has some place for SETF APPLY. -- WHN 19990604
534 (sb!xc:define-setf-expander apply (functionoid &rest args)
535   (unless (and (listp functionoid)
536                (= (length functionoid) 2)
537                (eq (first functionoid) 'function)
538                (symbolp (second functionoid)))
539     (error "SETF of APPLY is only defined for function args like #'SYMBOL."))
540   (let ((function (second functionoid))
541         (new-var (gensym))
542         (vars (make-gensym-list (length args))))
543     (values vars args (list new-var)
544             `(apply #'(setf ,function) ,new-var ,@vars)
545             `(apply #',function ,@vars))))
546
547 ;;; Special-case a BYTE bytespec so that the compiler can recognize it.
548 (sb!xc:define-setf-expander ldb (bytespec place &environment env)
549   #!+sb-doc
550   "The first argument is a byte specifier. The second is any place form
551   acceptable to SETF. Replace the specified byte of the number in this
552   place with bits from the low-order end of the new value."
553   (declare (type sb!c::lexenv env))
554   (multiple-value-bind (dummies vals newval setter getter)
555       (get-setf-method place env)
556     (if (and (consp bytespec) (eq (car bytespec) 'byte))
557         (let ((n-size (gensym))
558               (n-pos (gensym))
559               (n-new (gensym)))
560           (values (list* n-size n-pos dummies)
561                   (list* (second bytespec) (third bytespec) vals)
562                   (list n-new)
563                   `(let ((,(car newval) (dpb ,n-new (byte ,n-size ,n-pos)
564                                              ,getter)))
565                      ,setter
566                      ,n-new)
567                   `(ldb (byte ,n-size ,n-pos) ,getter)))
568         (let ((btemp (gensym))
569               (gnuval (gensym)))
570           (values (cons btemp dummies)
571                   (cons bytespec vals)
572                   (list gnuval)
573                   `(let ((,(car newval) (dpb ,gnuval ,btemp ,getter)))
574                      ,setter
575                      ,gnuval)
576                   `(ldb ,btemp ,getter))))))
577
578 (sb!xc:define-setf-expander mask-field (bytespec place &environment env)
579   #!+sb-doc
580   "The first argument is a byte specifier. The second is any place form
581   acceptable to SETF. Replaces the specified byte of the number in this place
582   with bits from the corresponding position in the new value."
583   (declare (type sb!c::lexenv env))
584   (multiple-value-bind (dummies vals newval setter getter)
585       (get-setf-method place env)
586     (let ((btemp (gensym))
587           (gnuval (gensym)))
588       (values (cons btemp dummies)
589               (cons bytespec vals)
590               (list gnuval)
591               `(let ((,(car newval) (deposit-field ,gnuval ,btemp ,getter)))
592                  ,setter
593                  ,gnuval)
594               `(mask-field ,btemp ,getter)))))
595
596 (defun setf-expand-the (the type place env)
597   (declare (type sb!c::lexenv env))
598   (multiple-value-bind (temps subforms store-vars setter getter)
599       (sb!xc:get-setf-expansion place env)
600     (values temps subforms store-vars
601             `(multiple-value-bind ,store-vars
602                  (,the ,type (values ,@store-vars))
603                ,setter)
604             `(,the ,type ,getter))))
605
606 (sb!xc:define-setf-expander the (type place &environment env)
607   (setf-expand-the 'the type place env))
608
609 (sb!xc:define-setf-expander truly-the (type place &environment env)
610   (setf-expand-the 'truly-the type place env))