;;; something not EQ to anything we might legitimately READ
(defparameter *eof-object* (make-symbol "EOF-OBJECT"))
+(eval-when (:compile-toplevel :load-toplevel :execute)
+ (defconstant max-hash sb!xc:most-positive-fixnum))
+
+(def!type hash ()
+ `(integer 0 ,max-hash))
+
;;; a type used for indexing into arrays, and for related quantities
;;; like lengths of lists
;;;
((or (atom result)
(not (eq (car result) 'values)))
`(values ,result &optional))
- ((intersection (cdr result) lambda-list-keywords)
+ ((intersection (cdr result) sb!xc:lambda-list-keywords)
result)
(t `(values ,@(cdr result) &optional)))))
`(function ,args ,result)))
;;; not really an old-fashioned function, but what the calling
;;; convention should've been: like NTH, but with the same argument
-;;; order as in all the other dereferencing functions, with the
-;;; collection first and the index second
+;;; order as in all the other indexed dereferencing functions, with
+;;; the collection first and the index second
(declaim (inline nth-but-with-sane-arg-order))
(declaim (ftype (function (list index) t) nth-but-with-sane-arg-order))
(defun nth-but-with-sane-arg-order (list index)
\f
;;;; miscellaneous iteration extensions
-;;; "the ultimate iteration macro"
+;;; like Scheme's named LET
;;;
-;;; note for Schemers: This seems to be identical to Scheme's "named LET".
+;;; (CMU CL called this ITERATE, and commented it as "the ultimate
+;;; iteration macro...". I (WHN) found the old name insufficiently
+;;; specific to remind me what the macro means, so I renamed it.)
(defmacro named-let (name binds &body body)
- #!+sb-doc
(dolist (x binds)
(unless (proper-list-of-length-p x 2)
(error "malformed NAMED-LET variable spec: ~S" x)))
(tagbody
,@forms)))))))
-;;; Iterate over the entries in a HASH-TABLE.
-(defmacro dohash ((key-var value-var table &optional result) &body body)
+;;; Iterate over the entries in a HASH-TABLE, first obtaining the lock
+;;; if the table is a synchronized table.
+(defmacro dohash (((key-var value-var) table &key result locked) &body body)
(multiple-value-bind (forms decls) (parse-body body :doc-string-allowed nil)
- (let ((gen (gensym))
- (n-more (gensym)))
- `(with-hash-table-iterator (,gen ,table)
- (loop
- (multiple-value-bind (,n-more ,key-var ,value-var) (,gen)
- ,@decls
- (unless ,n-more (return ,result))
- ,@forms))))))
+ (with-unique-names (gen n-more n-table)
+ (let ((iter-form `(with-hash-table-iterator (,gen ,n-table)
+ (loop
+ (multiple-value-bind (,n-more ,key-var ,value-var) (,gen)
+ ,@decls
+ (unless ,n-more (return ,result))
+ ,@forms)))))
+ `(let ((,n-table ,table))
+ ,(if locked
+ `(with-locked-hash-table (,n-table)
+ ,iter-form)
+ iter-form))))))
\f
;;;; hash cache utility
(default-values (if (and (consp default) (eq (car default) 'values))
(cdr default)
(list default)))
- (args-and-values (gensym))
+ (args-and-values (sb!xc:gensym "ARGS-AND-VALUES"))
(args-and-values-size (+ nargs values))
- (n-index (gensym))
- (n-cache (gensym)))
+ (n-index (sb!xc:gensym "INDEX"))
+ (n-cache (sb!xc:gensym "CACHE")))
(unless (= (length default-values) values)
(error "The number of default values ~S differs from :VALUES ~W."
(values-refs)
(values-names))
(dotimes (i values)
- (let ((name (gensym)))
+ (let ((name (sb!xc:gensym "VALUE")))
(values-names name)
(values-refs `(svref ,args-and-values (+ ,nargs ,i)))
(sets `(setf (svref ,args-and-values (+ ,nargs ,i)) ,name))))
(let ((default-values (if (and (consp default) (eq (car default) 'values))
(cdr default)
(list default)))
- (arg-names (mapcar #'car args)))
- (collect ((values-names))
- (dotimes (i values)
- (values-names (gensym)))
- (multiple-value-bind (body decls doc) (parse-body body-decls-doc)
- `(progn
- (define-hash-cache ,name ,args ,@options)
- (defun ,name ,arg-names
- ,@decls
- ,doc
- (cond #!+sb-show
- ((not (boundp '*hash-caches-initialized-p*))
- ;; This shouldn't happen, but it did happen to me
- ;; when revising the type system, and it's a lot
- ;; easier to figure out what what's going on with
- ;; that kind of problem if the system can be kept
- ;; alive until cold boot is complete. The recovery
- ;; mechanism should definitely be conditional on
- ;; some debugging feature (e.g. SB-SHOW) because
- ;; it's big, duplicating all the BODY code. -- WHN
- (/show0 ,name " too early in cold init, uncached")
- (/show0 ,(first arg-names) "=..")
- (/hexstr ,(first arg-names))
- ,@body)
- (t
- (multiple-value-bind ,(values-names)
- (,(symbolicate name "-CACHE-LOOKUP") ,@arg-names)
- (if (and ,@(mapcar (lambda (val def)
- `(eq ,val ,def))
- (values-names) default-values))
- (multiple-value-bind ,(values-names)
- (progn ,@body)
- (,(symbolicate name "-CACHE-ENTER") ,@arg-names
- ,@(values-names))
- (values ,@(values-names)))
- (values ,@(values-names))))))))))))
+ (arg-names (mapcar #'car args))
+ (values-names (make-gensym-list values)))
+ (multiple-value-bind (body decls doc) (parse-body body-decls-doc)
+ `(progn
+ (define-hash-cache ,name ,args ,@options)
+ (defun ,name ,arg-names
+ ,@decls
+ ,doc
+ (cond #!+sb-show
+ ((not (boundp '*hash-caches-initialized-p*))
+ ;; This shouldn't happen, but it did happen to me
+ ;; when revising the type system, and it's a lot
+ ;; easier to figure out what what's going on with
+ ;; that kind of problem if the system can be kept
+ ;; alive until cold boot is complete. The recovery
+ ;; mechanism should definitely be conditional on some
+ ;; debugging feature (e.g. SB-SHOW) because it's big,
+ ;; duplicating all the BODY code. -- WHN
+ (/show0 ,name " too early in cold init, uncached")
+ (/show0 ,(first arg-names) "=..")
+ (/hexstr ,(first arg-names))
+ ,@body)
+ (t
+ (multiple-value-bind ,values-names
+ (,(symbolicate name "-CACHE-LOOKUP") ,@arg-names)
+ (if (and ,@(mapcar (lambda (val def)
+ `(eq ,val ,def))
+ values-names default-values))
+ (multiple-value-bind ,values-names
+ (progn ,@body)
+ (,(symbolicate name "-CACHE-ENTER") ,@arg-names
+ ,@values-names)
+ (values ,@values-names))
+ (values ,@values-names))))))))))
(defmacro define-cached-synonym
(name &optional (original (symbolicate "%" name)))
(char= #\* (aref name 0))
(char= #\* (aref name (1- (length name))))))))
-;;; Some symbols are defined by ANSI to be self-evaluating. Return
-;;; non-NIL for such symbols (and make the non-NIL value a traditional
-;;; message, for use in contexts where the user asks us to change such
-;;; a symbol).
-(defun symbol-self-evaluating-p (symbol)
- (declare (type symbol symbol))
- (cond ((eq symbol t)
- "Veritas aeterna. (can't change T)")
- ((eq symbol nil)
- "Nihil ex nihil. (can't change NIL)")
- ((keywordp symbol)
- "Keyword values can't be changed.")
- (t
- nil)))
-
-;;; This function is to be called just before a change which would
-;;; affect the symbol value. (We don't absolutely have to call this
-;;; function before such changes, since such changes are given as
-;;; undefined behavior. In particular, we don't if the runtime cost
-;;; would be annoying. But otherwise it's nice to do so.)
-(defun about-to-modify-symbol-value (symbol)
- (declare (type symbol symbol))
- (let ((reason (symbol-self-evaluating-p symbol)))
- (when reason
- (error reason)))
- ;; (Note: Just because a value is CONSTANTP is not a good enough
- ;; reason to complain here, because we want DEFCONSTANT to be able
- ;; to use this function, and it's legal to DEFCONSTANT a constant as
- ;; long as the new value is EQL to the old value.)
+;;; This function is to be called just before a change which would affect the
+;;; symbol value. We don't absolutely have to call this function before such
+;;; changes, since such changes to constants are given as undefined behavior,
+;;; it's nice to do so. To circumvent this you need code like this:
+;;;
+;;; (defvar foo)
+;;; (defun set-foo (x) (setq foo x))
+;;; (defconstant foo 42)
+;;; (set-foo 13)
+;;; foo => 13, (constantp 'foo) => t
+;;;
+;;; ...in which case you frankly deserve to lose.
+(defun about-to-modify-symbol-value (symbol action &optional (new-value nil valuep))
+ (declare (symbol symbol))
+ (multiple-value-bind (what continue)
+ (when (eq :constant (info :variable :kind symbol))
+ (cond ((eq symbol t)
+ (values "Veritas aeterna. (can't ~@?)" nil))
+ ((eq symbol nil)
+ (values "Nihil ex nihil. (can't ~@?)" nil))
+ ((keywordp symbol)
+ (values "Can't ~@?." nil))
+ (t
+ (values "Constant modification: attempt to ~@?." t))))
+ (when what
+ (if continue
+ (cerror "Modify the constant." what action symbol)
+ (error what action symbol)))
+ (when valuep
+ ;; :VARIABLE :TYPE is in the db only if it is declared, so no need to
+ ;; check.
+ (let ((type (info :variable :type symbol)))
+ (unless (sb!kernel::%%typep new-value type)
+ (let ((spec (type-specifier type)))
+ (error 'simple-type-error
+ :format-control "Cannot ~@? to ~S (not of type ~S.)"
+ :format-arguments (list action symbol new-value spec)
+ :datum new-value
+ :expected-type spec))))))
(values))
-
;;; If COLD-FSET occurs not at top level, just treat it as an ordinary
;;; assignment instead of doing cold static linking. That way things like
;;; (FLET ((FROB (X) ..))
(unless (proper-list-of-length-p spec 2)
(error "malformed ONCE-ONLY binding spec: ~S" spec))
(let* ((name (first spec))
- (exp-temp (gensym (symbol-name name))))
+ (exp-temp (gensym "ONCE-ONLY")))
`(let ((,exp-temp ,(second spec))
- (,name (gensym "ONCE-ONLY-")))
+ (,name (gensym ,(symbol-name name))))
`(let ((,,name ,,exp-temp))
,,(frob (rest specs) body))))))))
\f
;;; guts of complex systems anyway, I replaced it too.)
(defmacro aver (expr)
`(unless ,expr
- (%failed-aver ,(format nil "~A" expr))))
+ (%failed-aver ',expr)))
-(defun %failed-aver (expr-as-string)
+(defun %failed-aver (expr)
;; hackish way to tell we're in a cold sbcl and output the
- ;; message before signallign error, as it may be this is too
+ ;; message before signalling error, as it may be this is too
;; early in the cold init.
(when (find-package "SB!C")
(fresh-line)
(write-line "failed AVER:")
- (write-line expr-as-string)
+ (write expr)
(terpri))
- (bug "~@<failed AVER: ~2I~_~S~:>" expr-as-string))
+ (bug "~@<failed AVER: ~2I~_~A~:>" expr))
(defun bug (format-control &rest format-arguments)
(error 'bug
(def-constantly-fun constantly-nil nil)
(def-constantly-fun constantly-0 0))
-;;; If X is an atom, see whether it is present in *FEATURES*. Also
+;;; If X is a symbol, see whether it is present in *FEATURES*. Also
;;; handle arbitrary combinations of atoms using NOT, AND, OR.
(defun featurep (x)
- (if (consp x)
- (case (car x)
- ((:not not)
- (if (cddr x)
- (error "too many subexpressions in feature expression: ~S" x)
- (not (featurep (cadr x)))))
- ((:and and) (every #'featurep (cdr x)))
- ((:or or) (some #'featurep (cdr x)))
- (t
- (error "unknown operator in feature expression: ~S." x)))
- (not (null (memq x *features*)))))
-
-;;; Given a list of keyword substitutions `(,OLD ,NEW), and a
-;;; &KEY-argument-list-style list of alternating keywords and
-;;; arbitrary values, return a new &KEY-argument-list-style list with
-;;; all substitutions applied to it.
-;;;
-;;; Note: If efficiency mattered, we could do less consing. (But if
-;;; efficiency mattered, why would we be using &KEY arguments at
-;;; all, much less renaming &KEY arguments?)
-;;;
-;;; KLUDGE: It would probably be good to get rid of this. -- WHN 19991201
-(defun rename-key-args (rename-list key-args)
- (declare (type list rename-list key-args))
- ;; Walk through RENAME-LIST modifying RESULT as per each element in
- ;; RENAME-LIST.
- (do ((result (copy-list key-args))) ; may be modified below
- ((null rename-list) result)
- (destructuring-bind (old new) (pop rename-list)
- ;; ANSI says &KEY arg names aren't necessarily KEYWORDs.
- (declare (type symbol old new))
- ;; Walk through RESULT renaming any OLD key argument to NEW.
- (do ((in-result result (cddr in-result)))
- ((null in-result))
- (declare (type list in-result))
- (when (eq (car in-result) old)
- (setf (car in-result) new))))))
-
-;;; ANSI Common Lisp's READ-SEQUENCE function, unlike most of the
-;;; other ANSI input functions, is defined to communicate end of file
-;;; status with its return value, not by signalling. That is not the
-;;; behavior that we usually want. This function is a wrapper which
-;;; restores the behavior that we usually want, causing READ-SEQUENCE
-;;; to communicate end-of-file status by signalling.
-(defun read-sequence-or-die (sequence stream &key start end)
- ;; implementation using READ-SEQUENCE
- #-no-ansi-read-sequence
- (let ((read-end (read-sequence sequence
- stream
- :start start
- :end end)))
- (unless (= read-end end)
- (error 'end-of-file :stream stream))
- (values))
- ;; workaround for broken READ-SEQUENCE
- #+no-ansi-read-sequence
- (progn
- (aver (<= start end))
- (let ((etype (stream-element-type stream)))
- (cond ((equal etype '(unsigned-byte 8))
- (do ((i start (1+ i)))
- ((>= i end)
- (values))
- (setf (aref sequence i)
- (read-byte stream))))
- (t (error "unsupported element type ~S" etype))))))
+ (etypecase x
+ (cons
+ (case (car x)
+ ((:not not)
+ (cond
+ ((cddr x)
+ (error "too many subexpressions in feature expression: ~S" x))
+ ((null (cdr x))
+ (error "too few subexpressions in feature expression: ~S" x))
+ (t (not (featurep (cadr x))))))
+ ((:and and) (every #'featurep (cdr x)))
+ ((:or or) (some #'featurep (cdr x)))
+ (t
+ (error "unknown operator in feature expression: ~S." x))))
+ (symbol (not (null (memq x *features*))))))
\f
;;;; utilities for two-VALUES predicates
(let ((first? t)
maybe-print-space
(reversed-prints nil)
- (stream (gensym "STREAM")))
+ (stream (sb!xc:gensym "STREAM")))
(flet ((sref (slot-name)
`(,(symbolicate conc-name slot-name) structure)))
(dolist (slot-desc slot-descs)
(*print-length* (or (true *print-length*) 12)))
(funcall function))))
+;;; Returns a list of members of LIST. Useful for dealing with circular lists.
+;;; For a dotted list returns a secondary value of T -- in which case the
+;;; primary return value does not include the dotted tail.
+(defun list-members (list)
+ (when list
+ (do ((tail (cdr list) (cdr tail))
+ (members (list (car list)) (cons (car tail) members)))
+ ((or (not (consp tail)) (eq tail list))
+ (values members (not (listp tail)))))))
+
;;; Default evaluator mode (interpeter / compiler)
(declaim (type (member :compile #!+sb-eval :interpret) *evaluator-mode*))
;;; Helper for making the DX closure allocation in macros expanding
;;; to CALL-WITH-FOO less ugly.
-;;;
-;;; This expands to something like
-;;;
-;;; (flet ((foo (...) <body-of-foo>))
-;;; (declare (optimize stack-allocate-dynamic-extent))
-;;; (flet ((foo (...)
-;;; (foo ...))
-;;; (declare (dynamic-extent #'foo))
-;;; <body-of-dx-flet>)))
-;;;
-;;; The outer FLETs are inlined into the inner ones, and the inner ones
-;;; are DX-allocated. The double-fletting is done to keep the bodies of
-;;; the functions in an environment with correct policy: we don't want
-;;; to force DX allocation in their bodies, which would be bad eg.
-;;; in safe code.
(defmacro dx-flet (functions &body forms)
- (let ((names (mapcar #'car functions)))
- `(flet ,functions
- #-sb-xc-host
- (declare (optimize sb!c::stack-allocate-dynamic-extent))
- (flet ,(mapcar
- (lambda (f)
- (let ((args (cadr f))
- (name (car f)))
- (when (intersection args lambda-list-keywords)
- ;; No fundamental reason not to support them, but we
- ;; don't currently need them here.
- (error "Non-required arguments not implemented for DX-FLET."))
- `(,name ,args
- (,name ,@args))))
- functions)
- (declare (dynamic-extent ,@(mapcar (lambda (x) `(function ,x)) names)))
- ,@forms))))
-
-;;; Another similar one -- but actually touches the policy of the body,
-;;; so take care with this one...
+ `(flet ,functions
+ (declare (#+sb-xc-host dynamic-extent #-sb-xc-host truly-dynamic-extent
+ ,@(mapcar (lambda (func) `(function ,(car func))) functions)))
+ ,@forms))
+
+;;; Another similar one.
(defmacro dx-let (bindings &body forms)
- `(locally
- #-sb-xc-host
- (declare (optimize sb!c::stack-allocate-dynamic-extent))
- (let ,bindings
- (declare (dynamic-extent ,@(mapcar (lambda (bind)
- (if (consp bind)
- (car bind)
- bind))
- bindings)))
- ,@forms)))
+ `(let ,bindings
+ (declare (#+sb-xc-host dynamic-extent #-sb-xc-host truly-dynamic-extent
+ ,@(mapcar (lambda (bind) (if (consp bind) (car bind) bind))
+ bindings)))
+ ,@forms))
+
+(in-package "SB!KERNEL")
+
+(defun fp-zero-p (x)
+ (typecase x
+ (single-float (zerop x))
+ (double-float (zerop x))
+ #!+long-float
+ (long-float (zerop x))
+ (t nil)))
+
+(defun neg-fp-zero (x)
+ (etypecase x
+ (single-float
+ (if (eql x 0.0f0)
+ (make-unportable-float :single-float-negative-zero)
+ 0.0f0))
+ (double-float
+ (if (eql x 0.0d0)
+ (make-unportable-float :double-float-negative-zero)
+ 0.0d0))
+ #!+long-float
+ (long-float
+ (if (eql x 0.0l0)
+ (make-unportable-float :long-float-negative-zero)
+ 0.0l0))))