gencgc: Don't use defconstant for DYNAMIC-SPACE-END
[sbcl.git] / src / code / symbol.lisp
blobe7f0c574b692e3d98e89f115e8efa9db3663557d
1 ;;;; code to manipulate symbols (but not packages, which are handled
2 ;;;; elsewhere)
3 ;;;;
4 ;;;; Many of these definitions are trivial interpreter entries to
5 ;;;; functions open-coded by the compiler.
7 ;;;; This software is part of the SBCL system. See the README file for
8 ;;;; more information.
9 ;;;;
10 ;;;; This software is derived from the CMU CL system, which was
11 ;;;; written at Carnegie Mellon University and released into the
12 ;;;; public domain. The software is in the public domain and is
13 ;;;; provided with absolutely no warranty. See the COPYING and CREDITS
14 ;;;; files for more information.
16 (in-package "SB!IMPL")
18 (declaim (maybe-inline get3 %put getf remprop %putf get-properties keywordp))
20 ;;; Used by [GLOBAL-]SYMBOL-VALUE compiler-macros:
21 ;;;
22 ;;; When SYMBOL is constant, check whether it names a deprecated
23 ;;; variable, potentially signaling a {EARLY,LATE}-DEPRECATION-WARNING
24 ;;; in the process. Furthermore, if the deprecation state is :FINAL,
25 ;;; replace FORM by SYMBOL, causing the symbol-macro on SYMBOL to
26 ;;; expand into a call to DEPRECATION-ERROR.
27 ;;;
28 ;;; See SB-IMPL:SETUP-VARIABLE-IN-FINAL-DEPRECATION.
29 #-sb-xc-host
30 (eval-when (:compile-toplevel :load-toplevel :execute)
31 (defun maybe-handle-deprecated-global-variable (symbol env)
32 (when (sb!xc:constantp symbol env)
33 (let ((name (constant-form-value symbol env)))
34 (when (symbolp name)
35 (case (deprecated-thing-p 'variable name)
36 ((:early :late)
37 (check-deprecated-thing 'variable name)
38 nil)
39 ;; In this case, there is a symbol-macro for NAME that
40 ;; will signal the FINAL-DEPRECATION-WARNING when
41 ;; ir1converted and the DEPRECATION-ERROR at runtime.
42 (:final
43 name)))))))
45 (defun symbol-value (symbol)
46 "Return SYMBOL's current bound value."
47 (declare (optimize (safety 1)))
48 (symbol-value symbol))
50 #-sb-xc-host
51 (define-compiler-macro symbol-value (&whole form symbol &environment env)
52 (or (maybe-handle-deprecated-global-variable symbol env) form))
54 (defun boundp (symbol)
55 "Return non-NIL if SYMBOL is bound to a value."
56 (boundp symbol))
58 (defun set (symbol new-value)
59 "Set SYMBOL's value cell to NEW-VALUE."
60 (declare (type symbol symbol))
61 (about-to-modify-symbol-value symbol 'set new-value)
62 (%set-symbol-value symbol new-value))
64 (defun %set-symbol-value (symbol new-value)
65 (%set-symbol-value symbol new-value))
67 (defun symbol-global-value (symbol)
68 "Return the SYMBOL's current global value. Identical to SYMBOL-VALUE,
69 in single-threaded builds: in multithreaded builds bound values are
70 distinct from the global value. Can also be SETF."
71 (declare (optimize (safety 1)))
72 (symbol-global-value symbol))
74 #-sb-xc-host
75 (define-compiler-macro symbol-global-value (&whole form symbol
76 &environment env)
77 (or (maybe-handle-deprecated-global-variable symbol env) form))
79 (defun set-symbol-global-value (symbol new-value)
80 (about-to-modify-symbol-value symbol 'set new-value)
81 (%set-symbol-global-value symbol new-value))
83 (declaim (inline %makunbound))
84 (defun %makunbound (symbol)
85 (%set-symbol-value symbol (make-unbound-marker)))
87 (defun makunbound (symbol)
88 "Make SYMBOL unbound, removing any value it may currently have."
89 (with-single-package-locked-error (:symbol symbol "unbinding the symbol ~A")
90 ;; :EVENTUALLY is allowed for :always-bound here, as it has no bearing
91 (when (eq (info :variable :always-bound symbol) :always-bound)
92 (error "Can't make ~A variable unbound: ~S" 'always-bound symbol))
93 (about-to-modify-symbol-value symbol 'makunbound)
94 (when (eq (info :variable :kind symbol) :constant)
95 (clear-info :variable :kind symbol))
96 (%makunbound symbol)
97 symbol))
99 ;; Compute a symbol's hash. Also used by FIND-SYMBOL which requires that a hash
100 ;; be a pure function of the name and not a semi-opaque property of the symbol.
101 ;; The hash of all symbols named "NIL" must be the same, so not to pessimize
102 ;; FIND-SYMBOL by special-casing the finding of CL:NIL with an extra "or"
103 ;; in the hash-equality test. i.e. We can't recognize that CL:NIL was the
104 ;; object sought (having an exceptional hash) until it has been found.
105 (defun compute-symbol-hash (string length)
106 (declare (simple-string string) (index length))
107 (if (and (= length 3)
108 (locally
109 ;; SXHASH-SUBSTRING is unsafe, so this is too. but do we know that
110 ;; length is ok, or is it an accident that it can scan too far?
111 (declare (optimize (safety 0)))
112 (string-dispatch (simple-base-string (simple-array character (*)))
113 string
114 (and (char= (schar string 0) #\N)
115 (char= (schar string 1) #\I)
116 (char= (schar string 2) #\L)))))
117 (return-from compute-symbol-hash (sxhash nil)))
118 ;; And make a symbol's hash not the same as (sxhash name) in general.
119 (let ((sxhash (logand (lognot (%sxhash-simple-substring string length))
120 sb!xc:most-positive-fixnum)))
121 (if (zerop sxhash) #x55AA sxhash))) ; arbitrary substitute for 0
123 ;; Return SYMBOL's hash, a strictly positive fixnum, computing it if not stored.
124 ;; The inlined code for (SXHASH symbol) only calls ENSURE-SYMBOL-HASH if
125 ;; needed, however this is ok to call even if the hash is already nonzero.
126 (defun ensure-symbol-hash (symbol)
127 (let ((hash (symbol-hash symbol)))
128 (if (zerop hash)
129 (let ((name (symbol-name symbol)))
130 (%set-symbol-hash symbol (compute-symbol-hash name (length name))))
131 hash)))
133 ;;; Interpreter stub: Return whatever is in the SYMBOL-HASH slot of SYMBOL.
134 (defun symbol-hash (symbol)
135 (symbol-hash symbol))
137 (defun symbol-function (symbol)
138 "Return SYMBOL's current function definition. Settable with SETF."
139 (%coerce-name-to-fun symbol symbol-fdefn))
141 ;; I think there are two bugs here.
142 ;; Per CLHS "SETF may be used with symbol-function to replace a global
143 ;; function definition when the symbol's function definition
144 ;; does not represent a special operator."
145 ;; 1. This should fail:
146 ;; * (in-package CL) ; circumvent package lock
147 ;; * (setf (symbol-function 'if) #'cons) => #<FUNCTION CONS>
148 ;; 2. (SETF (SYMBOL-FUNCTION 'I-ONCE-WAS-A-MACRO) #'CONS)
149 ;; should _probably_ make I-ONCE-WAS-A-MACRO not a macro
150 (defun (setf symbol-function) (new-value symbol)
151 (declare (type symbol symbol) (type function new-value))
152 ;; (SYMBOL-FUNCTION symbol) == (FDEFINITION symbol) according to the writeup
153 ;; on SYMBOL-FUNCTION. It doesn't say that SETF behaves the same, but let's
154 ;; assume it does, and that we can't assign our macro/special guard funs.
155 (err-if-unacceptable-function new-value '(setf symbol-function))
156 (with-single-package-locked-error
157 (:symbol symbol "setting the symbol-function of ~A")
158 ;; This code is a little "surprising" in that it is not just a limited
159 ;; case of (SETF FDEFINITION), but instead a different thing.
160 ;; I really think the code paths should be reconciled.
161 ;; e.g. what's up with *USER-HASH-TABLE-TESTS* being checked
162 ;; in %SET-FDEFINITION but not here?
163 (maybe-clobber-ftype symbol new-value)
164 (let ((fdefn (find-or-create-fdefn symbol)))
165 (setf (fdefn-fun fdefn) new-value))))
167 ;;; Accessors for the dual-purpose info/plist slot
169 ;; A symbol's INFO slot is always in one of three states:
170 ;; 1. NIL ; the initial state
171 ;; 2. #(data ....) ; globaldb used the slot
172 ;; 3. (PLIST . NIL) or (PLIST . #(data ...)) ; plist was touched,
173 ;; and also possibly globaldb used the slot
175 ;; State 1 transitions to state 2 by assigning globaldb data,
176 ;; or to state 3 via ({SETF|CAS} SYMBOL-PLIST).
177 ;; (SETF INFO) by itself will never cause 1 -> 3 transition.
178 ;; State 2 transitions to state 3 via ({SETF|CAS} SYMBOL-PLIST).
179 ;; There are *no* other permissible state transitions.
181 (defun symbol-info (symbol)
182 (symbol-info symbol))
184 ;; An "interpreter stub" for an operation that is only implemented for
185 ;; the benefit of platforms without compare-and-swap-vops.
186 (defun (setf symbol-info) (new-info symbol)
187 (setf (symbol-info symbol) new-info))
189 ;; Atomically update SYMBOL's info/plist slot to contain a new info vector.
190 ;; The vector is computed by calling UPDATE-FN on the old vector,
191 ;; repeatedly as necessary, until no conflict happens with other updaters.
192 ;; The function may choose to abort the update by returning NIL.
193 (defun update-symbol-info (symbol update-fn)
194 (declare (symbol symbol)
195 (type (function (t) t) update-fn))
196 (prog ((info-holder (symbol-info symbol))
197 (current-vect))
198 outer-restart
199 ;; Do not use SYMBOL-INFO-VECTOR - this must not perform a slot read again.
200 (setq current-vect (if (listp info-holder) (cdr info-holder) info-holder))
201 inner-restart
202 (let ((new-vect (funcall update-fn (or current-vect +nil-packed-infos+))))
203 (unless (simple-vector-p new-vect)
204 (aver (null new-vect))
205 (return)) ; nothing to do
206 (if (consp info-holder) ; State 3: exchange the CDR
207 (let ((old (%compare-and-swap-cdr info-holder current-vect new-vect)))
208 (when (eq old current-vect) (return t)) ; win
209 (setq current-vect old) ; Don't touch holder- it's still a cons
210 (go inner-restart)))
211 ;; State 1 or 2: info-holder is NIL or a vector.
212 ;; Exchange the contents of the info slot. Type-inference derives
213 ;; SIMPLE-VECTOR-P on the args to CAS, so no extra checking.
214 (let ((old (%compare-and-swap-symbol-info symbol info-holder new-vect)))
215 (when (eq old info-holder) (return t)) ; win
216 ;; Check whether we're in state 2 or 3 now.
217 ;; Impossible to be in state 1: nobody ever puts NIL in the slot.
218 ;; Up above, we bailed out if the update-fn returned NIL.
219 (setq info-holder old)
220 (go outer-restart)))))
222 (eval-when (:compile-toplevel)
223 ;; If we're in state 1 or state 3, we can take (CAR (SYMBOL-INFO S))
224 ;; to get the property list. If we're in state 2, this same access
225 ;; gets the fixnum which is the VECTOR-LENGTH of the info vector.
226 ;; So all we have to do is turn any fixnum to NIL, and we have a plist.
227 ;; Ensure that this pun stays working.
228 (assert (= (- (* sb!vm:n-word-bytes sb!vm:cons-car-slot)
229 sb!vm:list-pointer-lowtag)
230 (- (* sb!vm:n-word-bytes sb!vm:vector-length-slot)
231 sb!vm:other-pointer-lowtag))))
233 (defun symbol-plist (symbol)
234 "Return SYMBOL's property list."
235 #!+symbol-info-vops
236 (symbol-plist symbol) ; VOP translates it
237 #!-symbol-info-vops
238 (let ((list (car (truly-the list (symbol-info symbol))))) ; a white lie
239 ;; Just ensure the result is not a fixnum, and we're done.
240 (if (fixnump list) nil list)))
242 (declaim (ftype (sfunction (symbol t) cons) %ensure-plist-holder)
243 (inline %ensure-plist-holder))
245 ;; When a plist update (setf or cas) is first performed on a symbol,
246 ;; a one-time allocation of an extra cons is done which creates two
247 ;; "slots" from one: a slot for the info-vector and a slot for the plist.
248 ;; This avoids complications in the implementation of the user-facing
249 ;; (CAS SYMBOL-PLIST) function, which should not have to be aware of
250 ;; competition from globaldb mutators even if no other threads attempt
251 ;; to manipulate the plist per se.
253 ;; Given a SYMBOL and its current INFO of type (OR LIST SIMPLE-VECTOR)
254 ;; ensure that SYMBOL's current info is a cons, and return that.
255 ;; If racing with multiple threads, at most one thread will install the cons.
256 (defun %ensure-plist-holder (symbol info)
257 ;; Invoked only when SYMBOL is known to be a symbol.
258 (declare (optimize (safety 0)))
259 (if (consp info) ; it's fine to call this with a cell already installed
260 info ; all done
261 (let (newcell)
262 ;; The pointer from the new cons to the old info must be persisted
263 ;; to memory before the symbol's info slot points to the cons.
264 ;; [x86oid doesn't need the barrier, others might]
265 (sb!thread:barrier (:write)
266 (setq newcell (cons nil info)))
267 (loop (let ((old (%compare-and-swap-symbol-info symbol info newcell)))
268 (cond ((eq old info) (return newcell)) ; win
269 ((consp old) (return old))) ; somebody else made a cons!
270 (setq info old)
271 (sb!thread:barrier (:write) ; Retry using same newcell
272 (rplacd newcell info)))))))
274 (declaim (inline %compare-and-swap-symbol-plist
275 %set-symbol-plist))
277 (defun %compare-and-swap-symbol-plist (symbol old new)
278 ;; This is the entry point into which (CAS SYMBOL-PLIST) is transformed.
279 ;; If SYMBOL's info cell is a cons, we can do (CAS CAR). Otherwise punt.
280 (declare (symbol symbol) (list old new))
281 (let ((cell (symbol-info symbol)))
282 (if (consp cell)
283 (%compare-and-swap-car cell old new)
284 (%%compare-and-swap-symbol-plist symbol old new))))
286 (defun %%compare-and-swap-symbol-plist (symbol old new)
287 ;; This is just the second half of a partially-inline function, to avoid
288 ;; code bloat in the exceptional case. Type assertions should have been
289 ;; done - or not, per policy - by the caller of %COMPARE-AND-SWAP-SYMBOL-PLIST
290 ;; so now use TRULY-THE to avoid further type checking.
291 (%compare-and-swap-car (%ensure-plist-holder (truly-the symbol symbol)
292 (symbol-info symbol))
293 old new))
295 (defun %set-symbol-plist (symbol new-value)
296 ;; This is the entry point into which (SETF SYMBOL-PLIST) is transformed.
297 ;; If SYMBOL's info cell is a cons, we can do (SETF CAR). Otherwise punt.
298 (declare (symbol symbol) (list new-value))
299 (let ((cell (symbol-info symbol)))
300 (if (consp cell)
301 (setf (car cell) new-value)
302 (%%set-symbol-plist symbol new-value))))
304 (defun %%set-symbol-plist (symbol new-value)
305 ;; Same considerations as for %%COMPARE-AND-SWAP-SYMBOL-PLIST,
306 ;; with a slight efficiency hack: if the symbol has no plist holder cell
307 ;; and the NEW-VALUE is NIL, try to avoid creating a holder cell.
308 ;; Yet we must write something, because omitting a memory operation
309 ;; could have a subtle effect in the presence of multi-threading.
310 (let ((info (symbol-info (truly-the symbol symbol))))
311 (when (and (not new-value) (atom info)) ; try to treat this as a no-op
312 (let ((old (%compare-and-swap-symbol-info symbol info info)))
313 (if (eq old info) ; good enough
314 (return-from %%set-symbol-plist new-value) ; = nil
315 (setq info old))))
316 (setf (car (%ensure-plist-holder symbol info)) new-value)))
318 ;;; End of Info/Plist slot manipulation
320 (defun symbol-name (symbol)
321 "Return SYMBOL's name as a string."
322 (symbol-name symbol))
324 (defun symbol-package (symbol)
325 "Return the package SYMBOL was interned in, or NIL if none."
326 (symbol-package symbol))
328 (defun %set-symbol-package (symbol package)
329 (declare (type symbol symbol))
330 (%set-symbol-package symbol package))
332 (defun make-symbol (string)
333 "Make and return a new symbol with the STRING as its print name."
334 (declare (type string string))
335 (%make-symbol 0 (if (simple-string-p string) string (subseq string 0))))
337 ;;; All symbols go into immobile space if #!+immobile-symbols is enabled,
338 ;;; but not if disabled. The win with immobile space that is that all symbols
339 ;;; can be considered static from an addressing viewpoint, but GC'able.
340 ;;; (After codegen learns how, provided that defrag becomes smart enough
341 ;;; to fixup machine code so that defrag remains meaningful)
343 ;;; However, with immobile space being limited in size, you might not want
344 ;;; symbols in there. In particular, if an application uses symbols as data
345 ;;; - perhaps symbolic algebra on a Raspberry Pi - then not only is a faster
346 ;;; purely Lisp allocator better, you probably want not to run out of space.
347 ;;; The plausible heuristic that interned symbols be immobile, and otherwise not,
348 ;;; is mostly ok, except for the unfortunate possibility of calling IMPORT
349 ;;; on a random gensym. And even if a symbol is in immobile space at compile-time,
350 ;;; it might not be at load-time, if you do nasty things like that, so really
351 ;;; we can't make any reasonable determination - it's sort of all or nothing.
353 ;;; We can perhaps hardcode addresses of keywords in any case if we think that
354 ;;; people aren't in the habit of importing gensyms into #<package KEYWORD>.
355 ;;; It's kinda useless to do that, though not technically forbidden.
356 ;;; (It can produce a not-necessarily-self-evaluating keyword)
358 #!+immobile-space
359 (defun %make-symbol (kind name)
360 (declare (ignorable kind) (type simple-string name))
361 (set-header-data name sb!vm:+vector-shareable+) ; Set "logically read-only" bit
362 (if #!-immobile-symbols
363 (or (eql kind 1) ; keyword
364 (and (eql kind 2) ; random interned symbol
365 (plusp (length name))
366 (char= (char name 0) #\*)
367 (char= (char name (1- (length name))) #\*)))
368 #!+immobile-symbols t ; always place them there
369 (truly-the (values symbol) (%primitive sb!vm::alloc-immobile-symbol name))
370 (sb!vm::%%make-symbol name)))
372 (defun get (symbol indicator &optional (default nil))
373 "Look on the property list of SYMBOL for the specified INDICATOR. If this
374 is found, return the associated value, else return DEFAULT."
375 (get3 symbol indicator default))
377 (defun get3 (symbol indicator default)
378 (let (cdr-pl)
379 (do ((pl (symbol-plist symbol) (cdr cdr-pl)))
380 ((atom pl) default)
381 (setq cdr-pl (cdr pl))
382 (cond ((atom cdr-pl)
383 (error "~S has an odd number of items in its property list."
384 symbol))
385 ((eq (car pl) indicator)
386 (return (car cdr-pl)))))))
388 (defun %put (symbol indicator value)
389 "The VALUE is added as a property of SYMBOL under the specified INDICATOR.
390 Returns VALUE."
391 (do ((pl (symbol-plist symbol) (cddr pl)))
392 ((endp pl)
393 (setf (symbol-plist symbol)
394 (list* indicator value (symbol-plist symbol)))
395 value)
396 (cond ((endp (cdr pl))
397 (error "~S has an odd number of items in its property list."
398 symbol))
399 ((eq (car pl) indicator)
400 (rplaca (cdr pl) value)
401 (return value)))))
403 (defun remprop (symbol indicator)
404 "Look on property list of SYMBOL for property with specified
405 INDICATOR. If found, splice this indicator and its value out of
406 the plist, and return the tail of the original list starting with
407 INDICATOR. If not found, return () with no side effects.
409 NOTE: The ANSI specification requires REMPROP to return true (not false)
410 or false (the symbol NIL). Portable code should not rely on any other value."
411 (do ((pl (symbol-plist symbol) (cddr pl))
412 (prev nil pl))
413 ((atom pl) nil)
414 (cond ((atom (cdr pl))
415 (error "~S has an odd number of items in its property list."
416 symbol))
417 ((eq (car pl) indicator)
418 (cond (prev (rplacd (cdr prev) (cddr pl)))
420 (setf (symbol-plist symbol) (cddr pl))))
421 (return pl)))))
423 (defun getf (place indicator &optional (default ()))
424 "Search the property list stored in PLACE for an indicator EQ to INDICATOR.
425 If one is found, return the corresponding value, else return DEFAULT."
426 (do ((plist place (cddr plist)))
427 ((null plist) default)
428 (cond ((atom (cdr plist))
429 (error 'simple-type-error
430 :format-control "malformed property list: ~S."
431 :format-arguments (list place)
432 :datum (cdr plist)
433 :expected-type 'cons))
434 ((eq (car plist) indicator)
435 (return (cadr plist))))))
437 (defun %putf (place property new-value)
438 (declare (type list place))
439 (do ((plist place (cddr plist)))
440 ((endp plist) (list* property new-value place))
441 (declare (type list plist))
442 (when (eq (car plist) property)
443 (setf (cadr plist) new-value)
444 (return place))))
446 (defun get-properties (place indicator-list)
447 "Like GETF, except that INDICATOR-LIST is a list of indicators which will
448 be looked for in the property list stored in PLACE. Three values are
449 returned, see manual for details."
450 (do ((plist place (cddr plist)))
451 ((null plist) (values nil nil nil))
452 (cond ((atom (cdr plist))
453 (error 'simple-type-error
454 :format-control "malformed property list: ~S."
455 :format-arguments (list place)
456 :datum (cdr plist)
457 :expected-type 'cons))
458 ((memq (car plist) indicator-list)
459 (return (values (car plist) (cadr plist) plist))))))
461 (defun copy-symbol (symbol &optional (copy-props nil) &aux new-symbol)
462 "Make and return a new uninterned symbol with the same print name
463 as SYMBOL. If COPY-PROPS is false, the new symbol is neither bound
464 nor fbound and has no properties, else it has a copy of SYMBOL's
465 function, value and property list."
466 (declare (type symbol symbol))
467 (setq new-symbol (make-symbol (symbol-name symbol)))
468 (when copy-props
469 (%set-symbol-value new-symbol
470 (%primitive sb!c:fast-symbol-value symbol))
471 (setf (symbol-plist new-symbol)
472 (copy-list (symbol-plist symbol)))
473 (when (fboundp symbol)
474 (setf (symbol-function new-symbol) (symbol-function symbol))))
475 new-symbol)
477 (defun keywordp (object)
478 "Return true if Object is a symbol in the \"KEYWORD\" package."
479 (and (symbolp object)
480 (eq (symbol-package object) *keyword-package*)))
482 ;;;; GENSYM and friends
484 (defvar *gentemp-counter* 0)
485 (declaim (type unsigned-byte *gentemp-counter*))
487 (flet ((%symbol-nameify (prefix counter)
488 (declare (string prefix))
489 (if (typep counter '(and fixnum unsigned-byte))
490 (let ((s ""))
491 (declare (simple-string s))
492 (labels ((recurse (depth n)
493 (multiple-value-bind (q r) (truncate n 10)
494 (if (plusp q)
495 (recurse (1+ depth) q)
496 (let ((et (if (or (base-string-p prefix)
497 #!+sb-unicode ; no #'base-char-p
498 (every #'base-char-p prefix))
499 'base-char 'character)))
500 (setq s (make-string (+ (length prefix) depth)
501 :element-type et))
502 (replace s prefix)))
503 (setf (char s (- (length s) depth))
504 (code-char (+ (char-code #\0) r)))
505 s)))
506 (recurse 1 counter)))
507 (with-simple-output-to-string (s)
508 (write-string prefix s)
509 (%output-integer-in-base counter 10 s)))))
511 (defvar *gensym-counter* 0
512 "counter for generating unique GENSYM symbols")
514 (defun gensym (&optional (thing "G"))
515 "Creates a new uninterned symbol whose name is a prefix string (defaults
516 to \"G\"), followed by a decimal number. Thing, when supplied, will
517 alter the prefix if it is a string, or be used for the decimal number
518 if it is a number, of this symbol. The default value of the number is
519 the current value of *gensym-counter* which is incremented each time
520 it is used."
521 (multiple-value-bind (prefix int)
522 (if (integerp thing)
523 (values "G" thing)
524 (values thing (let ((old *gensym-counter*))
525 (setq *gensym-counter* (1+ old))
526 old)))
527 (make-symbol (%symbol-nameify prefix int))))
529 (defun gentemp (&optional (prefix "T") (package (sane-package)))
530 "Creates a new symbol interned in package PACKAGE with the given PREFIX."
531 (loop (multiple-value-bind (sym accessibility)
532 (intern (%symbol-nameify prefix (incf *gentemp-counter*)) package)
533 (unless accessibility (return sym))))))
535 ;;; This function is to be called just before a change which would affect the
536 ;;; symbol value. We don't absolutely have to call this function before such
537 ;;; changes, since such changes to constants are given as undefined behavior,
538 ;;; it's nice to do so. To circumvent this you need code like this:
540 ;;; (defvar foo)
541 ;;; (defun set-foo (x) (setq foo x))
542 ;;; (defconstant foo 42)
543 ;;; (set-foo 13)
544 ;;; foo => 13, (constantp 'foo) => t
546 ;;; ...in which case you frankly deserve to lose.
547 (defun about-to-modify-symbol-value (symbol action &optional (new-value nil valuep) bind)
548 (declare (symbol symbol))
549 (declare (explicit-check))
550 (flet ((describe-action ()
551 (ecase action
552 (set "set SYMBOL-VALUE of ~S")
553 (progv "bind ~S")
554 (compare-and-swap "compare-and-swap SYMBOL-VALUE of ~S")
555 (defconstant "define ~S as a constant")
556 (makunbound "make ~S unbound"))))
557 (let ((kind (info :variable :kind symbol)))
558 (multiple-value-bind (what continue)
559 (cond ((eq kind :constant)
560 (cond ((eq symbol t)
561 (values "Veritas aeterna. (can't ~@?)" nil))
562 ((eq symbol nil)
563 (values "Nihil ex nihil. (can't ~@?)" nil))
564 ((keywordp symbol)
565 (values "Can't ~@?." nil))
567 (values "Constant modification: attempt to ~@?." t))))
568 ((and bind (eq kind :global))
569 (values "Can't ~@? (global variable)." nil))
570 ((and (eq action 'set)
571 (eq kind :unknown))
572 (with-single-package-locked-error
573 (:symbol symbol "setting the value of ~S"))
574 nil))
575 (when what
576 (if continue
577 (cerror "Modify the constant." what (describe-action) symbol)
578 (error what (describe-action) symbol)))
579 (when valuep
580 (multiple-value-bind (type declaredp) (info :variable :type symbol)
581 ;; If globaldb returned the default of *UNIVERSAL-TYPE*,
582 ;; don't bother with a type test.
583 (when (and declaredp (not (%%typep new-value type nil)))
584 (let ((spec (type-specifier type)))
585 (error 'simple-type-error
586 :format-control "~@<Cannot ~@? to ~S, not of type ~S.~:@>"
587 :format-arguments (list (describe-action) symbol new-value spec)
588 :datum new-value
589 :expected-type spec)))))))
590 nil))