primarily intending to integrate Colin Walter's O(N) map code and
authorWilliam Harold Newman <william.newman@airmail.net>
Fri, 22 Sep 2000 14:56:53 +0000 (14:56 +0000)
committerWilliam Harold Newman <william.newman@airmail.net>
Fri, 22 Sep 2000 14:56:53 +0000 (14:56 +0000)
fixing BUTLAST (but doing some other stuff too, since achieving the
primary objective involved a lot of inspecting other code):

another revision of MAP stuff, part I:
  * I switched over to code inspired by Colin Walters' O(N) MAP
    code (from the cmucl-imp mailing list 2000 Sep 02) for the
    general non-DEFTRANSFORM case.
  * HIGH-SECURITY-SUPPORT error checking logic goes away, pending
    part II
  * I made some  miscellaneous cleanups of old arity-1 code too.
  * The old MAP-FOR-EFFECT, MAP-TO-LIST, and MAP-TO-SIMPLE macros,
    and the old functions MAP-WITHOUT-ERRORCHECKING, and
    GET-MINIMUM-LENGTH-SEQUENCES go away.
  * The old #+!HIGH-SECURITY length-checking logic goes
    away, to be replaced by stuff in part II.
  * New O(N) functions %MAP-FOR-EFFECT, %MAP-TO-LIST, and
    %MAP-TO-VECTOR are added, and MAP is redefined in terms of them.
  * Add a note pointing out that since MAP-INTO has not been
    rewritten to take advantage of all the new mapping technology,
    it's still slow.
  * Delete no-longer-used ELT-SLICE macro.

another revision of MAP stuff, part II: Peter Van Eynde might go into
a killing frenzy, or at least his ansi-test suite will gnaw SBCL to
death, unless we raise type errors on length mismatches like
  (MAP '(SIMPLE-VECTOR 128) #'+ #(1 2) #(1 1)).
How to do this without clobbering efficiency? More DEFTRANSFORMs, I
think..
  * MAP becomes a wrapper around %MAP. %MAP doesn't do this
    kind of length checking, MAP does. The old DEFUN MAP,
    DEFKNOWN MAP, and DEFTRANSFORM MAP stuff all turns into
    corresponding definitions for %MAP. The wrapper is
    implemented both as a DEFUN MAP and a DEFTRANSFORM MAP.
  * Now make DEFTRANSFORM MAP smarter:
    ** If necessary, check at runtime that ARRAY-DIMENSION
       matches what we pull out of SPECIFIER-TYPE.
    ** No test is done when SPEED > SAFETY.
    ** No test is needed when we can tell at compile time that
       the result type doesn't specify the length of the result.
  * Also add the same kind of ARRAY-DIMENSION/SPECIFIER-TYPE runtime
    check to DEFUN MAP.
  * While I'm at it, since DEFTRANSFORM MAP needs to think hard about
    the type of the result anyway, it might as well declare what
    it's figured out (TRULY-THE) to benefit any code downstream.

Start playing with MAP regression tests. Add tests/assertoid.lisp to
support future regression tests.

Once I started using the QUIT :UNIX-CODE keyword argument in my test
cases, I could see that it isn't very mnemonic. So I changed it to the
more-descriptive name :UNIX-STATUS, leaving the old name supported but
deprecated.

Oops! The old DEFTRANSFORM MAP (now DEFTRANSFORM %MAP) should really
only be done when (>= SPEED SPACE), but it wasn't declared that way.

While looking for an example of a DEFTRANSFORM with &REST arguments
to use as a model for the code in the new DEFTRANSFORM from MAP to
%MAP, I noticed that the problem of taking a list of names and
generating a corresponding list of gensyms is solved in many different
places in the code, in several ways. Also, the related problem of just
creating a list of N gensyms is solved in several places in in the
code. This seems unnecessarily error-prone and wasteful, so I went
looking for such cases and turned them into calls to MAKE-GENSYM-LIST.

another revision of MAP stuff, part III:
  * Search for 'map' in the output from clocc ansi-tests/tests.lisp,
    to check that the new MAP code isn't too obviously broken.
  * Add some regression tests in test/map.impure.lisp.

Oops! The various %MAP-..-ARITY-1 helper functions expect a function
argument, but DEFTRANSFORM MAP can call them passing them a function
name instead.
  * Change the helper functions so that they can handle
    function names as arguments.
  * Define %COERCE-CALLABLE-TO-FUNCTION to help with this. Note that
    this seems to be what %COERCE-NAME-TO-FUNCTION meant long ago,
    judging from DEFTRANSFORM %COERCE-NAME-TO-FUNCTION; so
    appropriate that DEFTRANSFORM for %COERCE-CALLABLE-TO-FUNCTION.
  * Use %COERCE-CALLABLE-TO-FUNCTION elsewhere that expressions
    involving %COERCE-NAME-TO-FUNCTION were used previously.

deleted the old commented-out version of DEFMACRO HANDLER-CASE
(since it was marked "Delete this when the system is stable.":-)

deleted the old commented-out version of GEN-FORMAT-DEF-FORM,
since it was supposed to be safe to do so after sbcl-0.6.4

I removed the apology for not using PRINT-OBJECT everywhere in the
printer from the bugs list in the man page, since it seems to be
rather tricky to construct a test case which exposes the system's
non-PRINT-OBJECT-ness without the test case itself violating the ANSI
spec.

I updated, cleaned up, or removed outright some other outdated or
confusing entries in the BUGS file and from the bugs list on the man
page.

Now that BUTLAST no longer blows up on the new problem cases a la
(BUTLAST NIL -1), I wonder whether I could stop it from blowing
up on the old problem cases a la (BUTLAST NIL)? It looks like
a compiler problem, since the interpreted definition of BUTLAST works,
even though the compiled one doesn't. In fact, it's a declaration
problem, since LENGTH is set to -1 when LIST=NIL, but is declared
as an INDEX. (Of course it's likely also a compiler problem, since
the compiler is supposed to signal type errors for this kind of
declaration error.) I fixed the misdeclaration, and noted the
possible compiler bug in BUGS.

After writing the new revised weird type declarations for the
not-necessarily positive LENGTH, and writing explanatory comments,
  ;; (Despite the name, LENGTH can be -1 when when LIST is an ATOM.)
for each of the cut-and-pasted (LET ((LENGTH ..)) ..) forms in BUTLAST
and NBUTLAST, I said "screw it" -- no, that's not it, I quoted Martin
Fowler and Kent Beck: "If you see the same code structure in more than
one place, you can be sure that your program will be better if you
find a way to unify them," and "It's surprising how often you look at
thickly commented code and notice that the comments are there because
the code is bad." So I just rewrote BUTLAST and NBUTLAST. Hopefully
the new versions will be better-behaved than the old ones.

Now that the INDEX type is used in DEFUN MAKE-GENSYM-LIST, which
belongs in early-extensions.lisp, INDEX should be defined before
early-extensions.lisp, i.e. earlier than its current definition in
early-c.lisp. Move it to early-extensions.lisp. Then to make that
work, since DEF!TYPE is used to define INDEX, defbangtype.lisp needs
to precede early-extensions.lisp in stems-and-flags.lisp-expr; so move
it. Also, INDEX is defined in terms of SB!XC:ARRAY-DIMENSION-LIMIT, so
early-array.lisp needs to move before the new location of
defbangtype.lisp. And then early-vm.lisp needs to move before that, so
I might as well move the rest of the early-vm-ish stuff back too. And
then DEFTYPE is used before deftype.lisp, so I need to change DEFMACRO
DEF!TYPE to DEF!MACRO DEF!TYPE, so I need to move defbangmacro.lisp
before deftype.lisp. (This is like a trip down memory lane to the
endless tweaks and recompiles it took me to find and unravel the
twisted order dependencies which make CMU CL unbootstrappable. Ah,
those were the days..:-)

The DEFTYPEs for INDEX and POSN in early-assem.lisp duplicate
the functionality of the SB-KERNEL:INDEX type.
  * Change uses of the SB-ASSEM::POSN type to uses of the INDEX type.
  * Delete the SB-ASSEM::POSN type and the SB-ASSEM::MAX-POSN constant.
  * Move SB-KERNEL:INDEX into SB-INT, since it's not really
    just a kernel-level thing, but makes sense for implementing
    user-level stuff in SB-INT and SB-EXT and SB-C (and SB-ASSEM).
  * Grep for all '[a-z]:+index[^-a-z]' and rename them (or just
    remove prefixes) to match new SB-INT-ness of INDEX.
  * Make the SB-ASSEM package use the SB-INT package; delete
    the SB-ASSEM::INDEX type and SB-ASSEM::MAX-INDEX constant.
    And since as a rule anything which can see SB-INT deserves
    to see SB-EXT too, make SB-ASSEM use SB-EXT as well.

16 files changed:
src/code/byte-interp.lisp
src/code/cold-init.lisp
src/code/debug.lisp
src/code/defboot.lisp
src/code/early-extensions.lisp
src/code/early-setf.lisp
src/code/early-target-error.lisp
src/code/fdefinition.lisp
src/code/host-alieneval.lisp
src/code/list.lisp
src/code/multi-proc.lisp
src/code/seq.lisp
src/code/setf-funs.lisp
src/code/sharpm.lisp
src/code/target-alieneval.lisp
src/code/target-hash-table.lisp

index c6173c5..9dc34ea 100644 (file)
                            (inline-function-info-type info)))
                     (arg-types (second spec))
                     (result-type (third spec))
-                    (args (mapcar #'(lambda (x)
-                                      (declare (ignore x))
-                                      (gensym))
-                                  arg-types))
+                    (args (make-gensym-list (length arg-types)))
                     (func
                      `(the ,result-type
                            (,(inline-function-info-interpreter-function info)
                                      arg-types args))
                   ,(if (and (consp result-type)
                             (eq (car result-type) 'values))
-                       (let ((results
-                              (mapcar #'(lambda (x)
-                                          (declare (ignore x))
-                                          (gensym))
-                                      (cdr result-type))))
+                       (let ((results (make-gensym-list
+                                       (length (cdr result-type)))))
                          `(multiple-value-bind ,results ,func
                             ,@(mapcar #'(lambda (res)
                                           `(push-eval-stack ,res))
index 8e33ddb..79872d9 100644 (file)
     (flush-standard-output-streams)
     (sb!unix:unix-exit wot)))
 
-(defun quit (&key recklessly-p (unix-code 0))
+(defun quit (&key recklessly-p
+                 (unix-code 0 unix-code-p)
+                 (unix-status unix-code))
   #!+sb-doc
   "Terminate the current Lisp. Things are cleaned up (with UNWIND-PROTECT
   and so forth) unless RECKLESSLY-P is non-NIL. On UNIX-like systems,
-  UNIX-CODE is used as the status code."
+  UNIX-STATUS is used as the status code."
   (declare (type (signed-byte 32) unix-code))
+  ;; TO DO: UNIX-CODE was deprecated in sbcl-0.6.8, after having been
+  ;; around for less than a year. It should be safe to remove it after
+  ;; a year.
+  (when unix-code-p
+    (warn "The UNIX-CODE argument is deprecated. Use the UNIX-STATUS argument
+instead (which is another name for the same thing)."))
   (if recklessly-p
-      (sb!unix:unix-exit unix-code)
+      (sb!unix:unix-exit unix-status)
       (throw '%end-of-the-world unix-code)))
 \f
 ;;;; initialization functions
index b4115b5..5105bb4 100644 (file)
@@ -1204,7 +1204,7 @@ Function and macro commands:
 ;;; We also cache the last top-level form that we printed a source for so that
 ;;; we don't have to do repeated reads and calls to FORM-NUMBER-TRANSLATIONS.
 (defvar *cached-top-level-form-offset* nil)
-(declaim (type (or sb!kernel:index null) *cached-top-level-form-offset*))
+(declaim (type (or index null) *cached-top-level-form-offset*))
 (defvar *cached-top-level-form*)
 (defvar *cached-form-number-translations*)
 
index 4e9854c..e280779 100644 (file)
@@ -59,9 +59,7 @@
           `(multiple-value-bind (,g) ,value-form
              ,g)))
        ((list-of-symbols-p vars)
-        (let ((temps (mapcar #'(lambda (x)
-                                 (declare (ignore x))
-                                 (gensym)) vars)))
+        (let ((temps (make-gensym-list (length vars))))
           `(multiple-value-bind ,temps ,value-form
              ,@(mapcar #'(lambda (var temp)
                            `(setq ,var ,temp))
index e5de12a..9642a5f 100644 (file)
 (file-comment
   "$Header$")
 
+;;; a type used for indexing into arrays, and for related quantities
+;;; like lengths of lists
+;;;
+;;; It's intentionally limited to one less than the
+;;; ARRAY-DIMENSION-LIMIT for efficiency reasons, because in SBCL
+;;; ARRAY-DIMENSION-LIMIT is MOST-POSITIVE-FIXNUM, and staying below
+;;; that lets the system know it can increment a value of this type
+;;; without having to worry about using a bignum to represent the
+;;; result.
+;;;
+;;; (It should be safe to use ARRAY-DIMENSION-LIMIT as an exclusive
+;;; bound because ANSI specifies it as an exclusive bound.)
+(def!type index () `(integer 0 (,sb!xc:array-dimension-limit)))
+
 ;;; the default value used for initializing character data. The ANSI
 ;;; spec says this is arbitrary. CMU CL used #\NULL, which we avoid
 ;;; because it's not in the ANSI table of portable characters.
         (error "not legal as a function name: ~S" function-name))))
 
 ;;; Is X a (possibly-improper) list of at least N elements?
+(declaim (ftype (function (t index)) list-of-length-at-least-p))
 (defun list-of-length-at-least-p (x n)
-  (declare (type (and unsigned-byte fixnum) n))
   (or (zerop n) ; since anything can be considered an improper list of length 0
       (and (consp x)
           (list-of-length-at-least-p (cdr x) (1- n)))))
+
+;;; Return a list of N gensyms. (This is a common suboperation in
+;;; macros and other code-manipulating code.)
+(declaim (ftype (function (index) list) make-gensym-list))
+(defun make-gensym-list (n)
+  (loop repeat n collect (gensym)))
 \f
 #|
 ;;; REMOVEME when done testing byte cross-compiler
index bd1c473..b3d1441 100644 (file)
@@ -518,10 +518,7 @@ GET-SETF-EXPANSION directly."
     (error "SETF of APPLY is only defined for function args like #'SYMBOL."))
   (let ((function (second functionoid))
        (new-var (gensym))
-       (vars (mapcar #'(lambda (x)
-                         (declare (ignore x))
-                         (gensym))
-                     args)))
+       (vars (make-gensym-list (length args))))
     (values vars args (list new-var)
            `(apply #'(setf ,function) ,new-var ,@vars)
            `(apply #',function ,@vars))))
index 8ea1f03..3b19746 100644 (file)
                                         body))))
                           annotated-cases))))))))
 
-;;; FIXME: Delete this when the system is stable.
-#|
-This macro doesn't work in our system due to lossage in closing over tags.
-The previous version sets up unique run-time tags.
-
-(defmacro handler-case (form &rest cases)
-  #!+sb-doc
-  "(HANDLER-CASE form
-   { (type ([var]) body) }* )
-   Executes form in a context with handlers established for the condition
-   types. A peculiar property allows type to be :no-error. If such a clause
-   occurs, and form returns normally, all its values are passed to this clause
-   as if by MULTIPLE-VALUE-CALL. The :no-error clause accepts more than one
-   var specification."
-  (let ((no-error-clause (assoc ':no-error cases)))
-    (if no-error-clause
-       (let ((normal-return (make-symbol "normal-return"))
-             (error-return  (make-symbol "error-return")))
-         `(block ,error-return
-            (multiple-value-call #'(lambda ,@(cdr no-error-clause))
-              (block ,normal-return
-                (return-from ,error-return
-                  (handler-case (return-from ,normal-return ,form)
-                    ,@(remove no-error-clause cases)))))))
-       (let ((tag (gensym))
-             (var (gensym))
-             (annotated-cases (mapcar #'(lambda (case) (cons (gensym) case))
-                                      cases)))
-         `(block ,tag
-            (let ((,var nil))
-              ,var                             ;ignorable
-              (tagbody
-                (handler-bind
-                 ,(mapcar #'(lambda (annotated-case)
-                              (list (cadr annotated-case)
-                                    `#'(lambda (temp)
-                                         ,(if (caddr annotated-case)
-                                              `(setq ,var temp)
-                                              '(declare (ignore temp)))
-                                         (go ,(car annotated-case)))))
-                          annotated-cases)
-                              (return-from ,tag ,form))
-                ,@(mapcan
-                   #'(lambda (annotated-case)
-                       (list (car annotated-case)
-                             (let ((body (cdddr annotated-case)))
-                               `(return-from
-                                 ,tag
-                                 ,(cond ((caddr annotated-case)
-                                         `(let ((,(caaddr annotated-case)
-                                                 ,var))
-                                            ,@body))
-                                        ((not (cdr body))
-                                         (car body))
-                                        (t
-                                         `(progn ,@body)))))))
-                          annotated-cases))))))))
-|#
-
 (defmacro ignore-errors (&rest forms)
   #!+sb-doc
   "Executes forms after establishing a handler for all error conditions that
index 1c720c5..3403fea 100644 (file)
 ;;;      back to the symbol which was used to indirect into the function,
 ;;;      so the undefined function handler can base its complaint on that.
 ;;;
-;;; Another problem with doing the simple thing: people will want to indirect
-;;; through something in order to get to SETF functions, in order to be able to
-;;; redefine them. What will they indirect through? This could be done with a
-;;; hack, making an anonymous symbol and linking it to the main symbol's
-;;; SB!KERNEL:SETF-FUNCTION property. The anonymous symbol could even point
-;;; back to the symbol it's the SETF function for, so that if the SETF function
-;;; was undefined at the time a call was made, the debugger could say which
-;;; function caused the problem. It'd probably be cleaner, though, to use a new
-;;; type of primitive object (SYMBOLOID?) instead. It could probably be like
-;;; symbol except that its name could be any object and its value points back
-;;; to the symbol which owns it. Then the setf functions for FOO could be on
-;;; the list (GET FOO 'SB!KERNEL:SYMBOLOIDS)
+;;; Another problem with doing the simple thing: people will want to
+;;; indirect through something in order to get to SETF functions, in
+;;; order to be able to redefine them. What will they indirect
+;;; through? This could be done with a hack, making an anonymous
+;;; symbol and linking it to the main symbol's SB!KERNEL:SETF-FUNCTION
+;;; property. The anonymous symbol could even point back to the symbol
+;;; it's the SETF function for, so that if the SETF function was
+;;; undefined at the time a call was made, the debugger could say
+;;; which function caused the problem. It'd probably be cleaner,
+;;; though, to use a new type of primitive object (SYMBOLOID?)
+;;; instead. It could probably be like symbol except that its name
+;;; could be any object and its value points back to the symbol which
+;;; owns it. Then the setf functions for FOO could be on the list (GET
+;;; FOO 'SB!KERNEL:SYMBOLOIDS)
 ;;;
-;;; FIXME: Oh, my. Now that I've started thinking about it, I appreciate more
-;;; fully how weird and twisted FDEFNs might be. Look at the calling sequence
-;;; for full calls. It goes and reads the address of a function object from its
-;;; own table of immediate values, then jumps into that. Consider how weird
-;;; that is. Not only is it not doing indirection through a symbol (which I'd
-;;; already realized) but it's not doing indirection through
+;;; FIXME: Oh, my. Now that I've started thinking about it, I
+;;; appreciate more fully how weird and twisted FDEFNs might be. Look
+;;; at the calling sequence for full calls. It goes and reads the
+;;; address of a function object from its own table of immediate
+;;; values, then jumps into that. Consider how weird that is. Not only
+;;; is it not doing indirection through a symbol (which I'd already
+;;; realized) but it's not doing indirection through
 
 ;;; The compiler emits calls to this when someone tries to funcall a symbol.
 (defun %coerce-name-to-function (name)
     (or (and fdefn (fdefn-function fdefn))
        (error 'undefined-function :name name))))
 
+(defun %coerce-callable-to-function (callable)
+  (if (functionp callable)
+      callable
+      (%coerce-name-to-function callable)))
+
 ;;; This is just another name for %COERCE-NAME-TO-FUNCTION.
 #!-sb-fluid (declaim (inline raw-definition))
 (defun raw-definition (name)
index 13c4223..72a65e0 100644 (file)
 
 (def-alien-type-translator array (ele-type &rest dims &environment env)
   (when dims
-    (unless (typep (first dims) '(or sb!kernel:index null))
+    (unless (typep (first dims) '(or index null))
       (error "The first dimension is not a non-negative fixnum or NIL: ~S"
             (first dims)))
-    (let ((loser (find-if-not #'(lambda (x) (typep x 'sb!kernel:index))
+    (let ((loser (find-if-not #'(lambda (x) (typep x 'index))
                              (rest dims))))
       (when loser
        (error "A dimension is not a non-negative fixnum: ~S" loser))))
index f72c474..13244d3 100644 (file)
 \f
 ;;; list copying functions
 
-;;; The list is copied correctly even if the list is not terminated by ()
-;;; The new list is built by cdr'ing splice which is always at the tail
-;;; of the new list
-
 (defun copy-list (list)
   #!+sb-doc
-  "Returns a new list EQUAL but not EQ to list"
+  "Returns a new list which is EQUAL to LIST."
+  ;; The list is copied correctly even if the list is not terminated
+  ;; by NIL. The new list is built by CDR'ing SPLICE which is always
+  ;; at the tail of the new list.
   (if (atom list)
       list
       (let ((result (list (car list))))
 
 (defun copy-alist (alist)
   #!+sb-doc
-  "Returns a new association list equal to alist, constructed in space"
+  "Returns a new association list which is EQUAL to ALIST."
   (if (atom alist)
       alist
       (let ((result
        (result y (cons (car top) result)))
       ((endp top) result)))
 
-;;; NCONC finds the first non-null list, so it can make splice point to a cons.
-;;; After finding the first cons element, it holds it in a result variable
-;;; while running down successive elements tacking them together. While
-;;; tacking lists together, if we encounter a null list, we set the previous
-;;; list's last cdr to nil just in case it wasn't already nil, and it could
-;;; have been dotted while the null list was the last argument to NCONC. The
-;;; manipulation of splice (that is starting it out on a first cons, setting
-;;; LAST of splice, and setting splice to ele) inherently handles (nconc x x),
-;;; and it avoids running down the last argument to NCONC which allows the last
-;;; argument to be circular.
+;;; NCONC finds the first non-null list, so it can make splice point
+;;; to a cons. After finding the first cons element, it holds it in a
+;;; result variable while running down successive elements tacking
+;;; them together. While tacking lists together, if we encounter a
+;;; null list, we set the previous list's last cdr to nil just in case
+;;; it wasn't already nil, and it could have been dotted while the
+;;; null list was the last argument to NCONC. The manipulation of
+;;; splice (that is starting it out on a first cons, setting LAST of
+;;; splice, and setting splice to ele) inherently handles (nconc x x),
+;;; and it avoids running down the last argument to NCONC which allows
+;;; the last argument to be circular.
 (defun nconc (&rest lists)
   #!+sb-doc
   "Concatenates the lists given as arguments (by changing them)"
       ((atom 2nd) 3rd)
     (rplacd 2nd 3rd)))
 \f
-(defun butlast (list &optional (n 1))
-  #!+sb-doc
-  "Return a new list the same as LIST without the last N conses.
-   List must not be circular."
-  (declare (list list) (type index n))
-  (let ((length (do ((list list (cdr list))
-                    (i 0 (1+ i)))
-                   ((atom list) (1- i)))))
-    (declare (type index length))
-    (unless (< length n)
-      (do* ((top (cdr list) (cdr top))
-           (result (list (car list)))
-           (splice result)
-           (count length (1- count)))
-          ((= count n) result)
-       (declare (type index count))
-       (setq splice (cdr (rplacd splice (list (car top)))))))))
-
-(defun nbutlast (list &optional (n 1))
-  #!+sb-doc
-  "Modifies List to remove the last N conses. List must not be circular."
-  (declare (list list) (type index n))
-  (let ((length (do ((list list (cdr list))
-                    (i 0 (1+ i)))
-                   ((atom list) (1- i)))))
-    (declare (type index length))
-    (unless (< length n)
-      (do ((1st (cdr list) (cdr 1st))
-          (2nd list 1st)
-          (count length (1- count)))
-         ((= count n)
-          (rplacd 2nd ())
-          list)
-       (declare (type index count))))))
+(flet (;; Return the number of conses at the head of the
+       ;; possibly-improper list LIST. (Or if LIST is circular, you
+       ;; lose.)
+       (count-conses (list)
+        (do ((in-list list (cdr in-list))
+             (result 0 (1+ result)))
+            ((atom in-list)
+             result)
+          (declare (type index result)))))
+  (declare (ftype (function (t) index) count-conses))
+  (defun butlast (list &optional (n 1))
+    (let* ((n-conses-in-list (count-conses list))
+          (n-remaining-to-copy (- n-conses-in-list n)))
+      (declare (type fixnum n-remaining-to-copy))
+      (when (plusp n-remaining-to-copy)
+       (do* ((result (list (first list)))
+             (rest (rest list) (rest rest))
+             (splice result))
+           ((zerop (decf n-remaining-to-copy))
+            result)
+         (setf splice
+               (setf (cdr splice)
+                     (list (first rest))))))))
+  (defun nbutlast (list &optional (n 1))
+    (let ((n-conses-in-list (count-conses list)))
+      (unless (< n-conses-in-list n)
+       (setf (cdr (nthcdr (- n-conses-in-list n 1) list))
+             nil)
+       list))))
 
 (defun ldiff (list object)
   "Returns a new list, whose elements are those of List that appear before
        (return (cdr result))
        (setq splice (cdr (rplacd splice (list (car list))))))))
 \f
-;;; Functions to alter list structure
+;;;; functions to alter list structure
 
 (defun rplaca (x y)
   #!+sb-doc
             (declare (optimize-interface (speed 3) (safety 0)))
             value))))
 \f
-;;;; macros for (&key (key #'identity) (test #'eql testp) (test-not nil notp)).
+;;;; macros for (&KEY (KEY #'IDENTITY) (TEST #'EQL TESTP) (TEST-NOT NIL NOTP))
 
 ;;; Use these with the following keyword args:
 (defmacro with-set-keys (funcall)
index deced14..d750c3e 100644 (file)
   ;; State: :active or :inactive.
   (state :inactive :type (member :active :inactive))
   ;; The control stack; an index into *control-stacks*.
-  (control-stack-id nil :type (or sb!kernel:index null))
+  (control-stack-id nil :type (or sb!int:index null))
   ;; Binding stack.
   (binding-stack nil :type (or (simple-array t (*)) null))
   ;; Twice the number of bindings.
            (len (length eval-stack)))
        (do ((i eval-stack-top (1+ i)))
            ((= i len))
-         (declare (type sb!kernel:index i))
+         (declare (type sb!int:index i))
          (setf (svref eval-stack i) nil))))))
 
 ;;; Generate the initial bindings for a newly created stack-group.
   (let ((destroyed-processes nil))
     (do ((cnt 0 (1+ cnt)))
        ((> cnt 10))
-      (declare (type sb!kernel:index cnt))
+      (declare (type sb!int:index cnt))
       (dolist (process *all-processes*)
        (when (and (not (eq process *current-process*))
                   (process-active-p process)
   "Wait until FD is usable for DIRECTION and return True. DIRECTION should be
   either :INPUT or :OUTPUT. TIMEOUT, if supplied, is the number of seconds to
   wait before giving up and returning NIL."
-  (declare (type sb!kernel:index fd)
+  (declare (type sb!int:index fd)
           (type (or real null) timeout)
           (optimize (speed 3)))
   (if (or (eq *current-process* *initial-process*)
index 52c28e8..7baeb3c 100644 (file)
 
 (eval-when (:compile-toplevel)
 
-;;; Seq-Dispatch does an efficient type-dispatch on the given Sequence.
-
-;;; FIXME: It might be worth making three cases here, LIST, SIMPLE-VECTOR,
-;;; and VECTOR, instead of the current LIST and VECTOR. It tend to make code
-;;; run faster but be bigger; some benchmarking is needed to decide.
+;;; SEQ-DISPATCH does an efficient type-dispatch on the given SEQUENCE.
+;;;
+;;; FIXME: It might be worth making three cases here, LIST,
+;;; SIMPLE-VECTOR, and VECTOR, instead of the current LIST and VECTOR.
+;;; It tend to make code run faster but be bigger; some benchmarking
+;;; is needed to decide.
 (sb!xc:defmacro seq-dispatch (sequence list-form array-form)
   `(if (listp ,sequence)
        ,list-form
        ,array-form))
 
-;;; FIXME: Implementations of MAPFOO which use this are O(N*N) when users
-;;; could reasonably expect them to be O(N). This should be fixed.
-(sb!xc:defmacro elt-slice (sequences n)
-  #!+sb-doc
-  "Returns a list of the Nth element of each of the sequences. Used by MAP
-   and friends."
-  `(mapcar #'(lambda (seq) (elt seq ,n)) ,sequences))
-
 (sb!xc:defmacro make-sequence-like (sequence length)
   #!+sb-doc
   "Returns a sequence of the same type as SEQUENCE and the given LENGTH."
 
 ) ; EVAL-WHEN
 
+;;; It's possible with some sequence operations to declare the length
+;;; of a result vector, and to be safe, we really ought to verify that
+;;; the actual result has the declared length.
+(defun vector-of-checked-length-given-length (vector declared-length)
+  (declare (type vector vector))
+  (declare (type index declared-length))
+  (let ((actual-length (length vector)))
+    (unless (= actual-length declared-length)
+      (error 'simple-type-error
+            :datum vector
+            :expected-type `(vector ,declared-length)
+            :format-control
+            "Vector length (~D) doesn't match declared length (~D)."
+            :format-arguments (list actual-length declared-length))))
+  vector)
+(defun sequence-of-checked-length-given-type (sequence result-type)
+  (let ((ctype (specifier-type result-type)))
+    (if (not (array-type-p ctype))
+       sequence
+       (let ((declared-length (first (array-type-dimensions ctype))))
+         (if (eq declared-length '*)
+             sequence
+             (vector-of-checked-length-given-length sequence
+                                                    declared-length))))))
+
 ;;; Given an arbitrary type specifier, return a sane sequence type
 ;;; specifier that we can directly match.
 (defun result-type-or-lose (type &optional nil-ok)
 
 (defun signal-index-too-large-error (sequence index)
   (let* ((length (length sequence))
-        (max-index (and (plusp length)(1- length))))
+        (max-index (and (plusp length) (1- length))))
     (error 'index-too-large-error
           :datum index
           :expected-type (if max-index
 (defun concat-to-simple* (type &rest sequences)
   (concatenate-to-mumble type sequences))
 \f
-;;;; MAP
+;;;; MAP and MAP-INTO
 
-;;; helper functions to handle the common consing subcases of MAP
+;;; helper functions to handle arity-1 subcases of MAP
 (declaim (ftype (function (function sequence) list) %map-list-arity-1))
 (declaim (ftype (function (function sequence) simple-vector)
                %map-simple-vector-arity-1))
                  (simple-vector (dovector (,i sequence) ,@body))
                  (vector (dovector (,i sequence) ,@body))))))
   (defun %map-to-list-arity-1 (fun sequence)
-    (declare (type function fun))
-    (let ((really-fun (if (functionp fun) fun (%coerce-name-to-function fun)))
-         (reversed-result nil))
+    (let ((reversed-result nil)
+         (really-fun (%coerce-callable-to-function fun)))
       (dosequence (element sequence)
        (push (funcall really-fun element)
              reversed-result))
       (nreverse reversed-result)))
   (defun %map-to-simple-vector-arity-1 (fun sequence)
-    (declare (type function fun))
-    (let ((really-fun (if (functionp fun) fun (%coerce-name-to-function fun)))
-         (result (make-array (length sequence)))
-         (index 0))
+    (let ((result (make-array (length sequence)))
+         (index 0)
+         (really-fun (%coerce-callable-to-function fun)))
       (declare (type index index))
       (dosequence (element sequence)
         (setf (aref result index)
              (funcall really-fun element))
        (incf index))
-      result)))
-
-(eval-when (:compile-toplevel :execute)
-
-(sb!xc:defmacro map-to-list (function sequences)
-  `(do ((seqs more-sequences (cdr seqs))
-       (min-length (length first-sequence)))
-       ((null seqs)
-       (let ((result (list nil)))
-         (do ((index 0 (1+ index))
-              (splice result))
-             ((= index min-length) (cdr result))
-           (declare (fixnum index))
-           (setq splice
-                 (cdr (rplacd splice
-                              (list (apply ,function (elt-slice ,sequences
-                                                                index)))))))))
-     (declare (fixnum min-length))
-     (let ((length (length (car seqs))))
-       (declare (fixnum length))
-       (if (< length min-length)
-          (setq min-length length)))))
-
-(sb!xc:defmacro map-to-simple (output-type-spec function sequences)
-  `(do ((seqs more-sequences (cdr seqs))
-       (min-length (length first-sequence)))
-       ((null seqs)
-       (do ((index 0 (1+ index))
-            (result (make-sequence-of-type ,output-type-spec min-length)))
-           ((= index min-length) result)
-         (declare (fixnum index))
-         (setf (aref result index)
-               (apply ,function (elt-slice ,sequences index)))))
-     (declare (fixnum min-length))
-     (let ((length (length (car seqs))))
-       (declare (fixnum length))
-       (if (< length min-length)
-          (setq min-length length)))))
-
-(sb!xc:defmacro map-for-effect (function sequences)
-  `(do ((seqs more-sequences (cdr seqs))
-       (min-length (length first-sequence)))
-       ((null seqs)
-       (do ((index 0 (1+ index)))
-           ((= index min-length) nil)
-         (apply ,function (elt-slice ,sequences index))))
-     (declare (fixnum min-length))
-     (let ((length (length (car seqs))))
-       (declare (fixnum length))
-       (if (< length min-length)
-          (setq min-length length)))))
-
-) ; EVAL-WHEN
-
-#!+high-security-support
-(defun get-minimum-length-sequences (sequences)
-  #!+sb-doc "Gets the minimum length of the sequences. This is
-needed to check whether the supplied type is appropriate."
-    (let ((min nil))
-      (dolist (i sequences)
-       (when (or (listp i) (vectorp i))
-         (let ((l (length i)))
-           (when (or (null min)
-                     (> min l)))
-           (setf min l))))
-      min))
-
-(defun map (output-type-spec function first-sequence &rest more-sequences)
-  #!+sb-doc
-  "FUNCTION must take as many arguments as there are sequences provided. The
-   result is a sequence such that element i is the result of applying FUNCTION
-   to element i of each of the argument sequences."
-  (let ((really-function (if (functionp function)
-                            function
-                            (%coerce-name-to-function function))))
-    ;; Pick off the easy non-consing arity-1 special case and handle
-    ;; it without consing, since the user probably didn't expect us to
-    ;; cons here. (Notably, the super duper users who wrote PCL in
-    ;; terms of quantifiers without declaring the types of their
-    ;; sequence arguments didn't expect to end up consing when SBCL
-    ;; transforms the quantifiers into calls to MAP NIL.)
-    (when (and (null more-sequences)
-              (null output-type-spec))
-      (macrolet ((frob () '(return-from map
-                            (map nil really-function first-sequence))))
-       (etypecase first-sequence
-         (simple-vector (frob))
-         (list (frob))
-         (vector (frob)))))
-    ;; Otherwise, if the user didn't give us enough information to
-    ;; simplify at compile time, we cons and cons and cons..
-    (let ((sequences (cons first-sequence more-sequences)))
-      (case (type-specifier-atom output-type-spec)
-       ((nil) (map-for-effect really-function sequences))
-       (list (map-to-list really-function sequences))
-       ((simple-vector simple-string vector string array simple-array
-                       bit-vector simple-bit-vector base-string simple-base-string)
-        #!+high-security
-        (let ((min-length-sequences (get-minimum-length-sequences
-                                     sequences))
-              (dimensions (array-type-dimensions (specifier-type
-                                                  output-type-spec))))
-          (when (or (/= (length dimensions) 1)
-                    (and (not (eq (car dimensions) '*))
-                         (/= (car dimensions) min-length-sequences)))
-            (error 'simple-type-error
-                   :datum output-type-spec
-                   :expected-type
-                   (ecase (type-specifier-atom output-type-spec)
-                     ((simple-vector bit-vector simple-bit-vector string simple-string base-string)
-                      `(,(type-specifier-atom output-type-spec) ,min-length-sequences))
-                     ((array vector simple-array)   `(,(type-specifier-atom output-type-spec) * ,min-length-sequences)))
-                   :format-control "Minimum length of sequences is ~S, this is not compatible with the type ~S."
-                   :format-arguments
-                   (list min-length-sequences output-type-spec))))
-        (let ((result (map-to-simple output-type-spec
-                                     really-function
-                                     sequences)))
-          #!+high-security
-          (check-type-var result output-type-spec)
-          result))
-       (t
-        (apply #'map (result-type-or-lose output-type-spec t)
-               really-function sequences))))))
-
-#!+high-security-support
-(defun map-without-errorchecking
-    (output-type-spec function first-sequence &rest more-sequences)
-  #!+sb-doc
-  "FUNCTION must take as many arguments as there are sequences provided. The
-   result is a sequence such that element i is the result of applying FUNCTION
-   to element I of each of the argument sequences. This version has no
-   error-checking, to pass cold-load."
-  (let ((sequences (cons first-sequence more-sequences)))
-    (case (type-specifier-atom output-type-spec)
-      ((nil) (map-for-effect function sequences))
-      (list (map-to-list function sequences))
-      ((simple-vector simple-string vector string array simple-array
-       bit-vector simple-bit-vector base-string simple-base-string)
-       (map-to-simple output-type-spec function sequences))
-      (t
-       (apply #'map (result-type-or-lose output-type-spec t)
-             function sequences)))))
-
+      result))
+  (defun %map-for-effect-arity-1 (fun sequence)
+    (let ((really-fun (%coerce-callable-to-function fun)))
+      (dosequence (element sequence)
+       (funcall really-fun element)))
+    nil))
+
+;;; helper functions to handle arity-N subcases of MAP
+;;;
+;;; KLUDGE: This is hairier, and larger, than need be, because we
+;;; don't have DYNAMIC-EXTENT. With DYNAMIC-EXTENT, we could define
+;;; %MAP-FOR-EFFECT, and then implement the
+;;; other %MAP-TO-FOO functions reasonably efficiently by passing closures to
+;;; %MAP-FOR-EFFECT. (DYNAMIC-EXTENT would help a little by avoiding
+;;; consing each closure, and would help a lot by allowing us to define
+;;; a closure (LAMBDA (&REST REST) <do something with (APPLY FUN REST)>)
+;;; with the REST list allocated with DYNAMIC-EXTENT. -- WHN 20000920
+(macrolet (;; Execute BODY in a context where the machinery for
+          ;; UPDATED-MAP-APPLY-ARGS has been set up.
+          (with-map-state (sequences &body body)
+             `(let* ((%sequences ,sequences)
+                    (%iters (mapcar (lambda (sequence)
+                                      (etypecase sequence
+                                        (list sequence)
+                                        (vector 0)))
+                                    %sequences))
+                    (%apply-args (make-list (length %sequences))))
+               (declare (type list %sequences %iters %apply-args))
+               ,@body))
+          ;; Return a list of args to pass to APPLY for the next
+          ;; function call in the mapping, or NIL if no more function
+          ;; calls should be made (because we've reached the end of a
+          ;; sequence arg).
+          (updated-map-apply-args ()
+            '(do ((in-sequences  %sequences  (cdr in-sequences))
+                  (in-iters      %iters      (cdr in-iters))
+                  (in-apply-args %apply-args (cdr in-apply-args)))
+                 ((null in-sequences)
+                  %apply-args)
+               (declare (type list in-sequences in-iters in-apply-args))
+               (let ((i (car in-iters)))
+                 (declare (type (or list index) i))
+                 (if (listp i)
+                     (if (null i)      ; if end of this sequence
+                         (return nil)
+                         (setf (car in-apply-args) (car i)
+                               (car in-iters) (cdr i)))
+                     (let ((v (the vector (car in-sequences))))
+                       (if (>= i (length v)) ; if end of this sequence
+                           (return nil)
+                           (setf (car in-apply-args) (aref v i)
+                                 (car in-iters) (1+ i)))))))))
+  (defun %map-to-list (func sequences)
+    (declare (type function func))
+    (declare (type list sequences))
+    (with-map-state sequences
+      (loop with updated-map-apply-args 
+           while (setf updated-map-apply-args (updated-map-apply-args))
+           collect (apply func updated-map-apply-args))))
+  (defun %map-to-vector (output-type-spec func sequences)
+    (declare (type function func))
+    (declare (type list sequences))
+    (let ((min-len (with-map-state sequences
+                    (do ((counter 0 (1+ counter)))
+                        ;; Note: Doing everything in
+                        ;; UPDATED-MAP-APPLY-ARGS here is somewhat
+                        ;; wasteful; we even do some extra consing.
+                        ;; And stepping over every element of
+                        ;; VECTORs, instead of just grabbing their
+                        ;; LENGTH, is also wasteful. But it's easy
+                        ;; and safe. (If you do rewrite it, please
+                        ;; try to make sure that
+                        ;;   (MAP NIL #'F SOME-CIRCULAR-LIST #(1))
+                        ;; does the right thing.)
+                        ((not (updated-map-apply-args))
+                         counter)
+                      (declare (type index counter))))))
+      (declare (type index min-len))
+      (with-map-state sequences
+       (let ((result (make-sequence-of-type output-type-spec min-len))
+             (index 0))
+         (declare (type index index))
+         (loop with updated-map-apply-args
+               while (setf updated-map-apply-args (updated-map-apply-args))
+               do
+               (setf (aref result index)
+                     (apply func updated-map-apply-args))
+               (incf index))
+         result))))
+  (defun %map-for-effect (func sequences)
+    (declare (type function func))
+    (declare (type list sequences))
+    (with-map-state sequences
+      (loop with updated-map-apply-args
+           while (setf updated-map-apply-args (updated-map-apply-args))
+           do
+           (apply func updated-map-apply-args))
+      nil)))
+
+  "FUNCTION must take as many arguments as there are sequences provided.  
+  The result is a sequence of type OUTPUT-TYPE-SPEC such that element I 
+  is the result of applying FUNCTION to element I of each of the argument
+  sequences."
+
+;;; %MAP is just MAP without the final just-to-be-sure check that
+;;; length of the output sequence matches any length specified
+;;; in RESULT-TYPE.
+(defun %map (result-type function first-sequence &rest more-sequences)
+  (let ((really-function (%coerce-callable-to-function function)))
+    ;; Handle one-argument MAP NIL specially, using ETYPECASE to turn
+    ;; it into something which can be DEFTRANSFORMed away. (It's
+    ;; fairly important to handle this case efficiently, since
+    ;; quantifiers like SOME are transformed into this case, and since
+    ;; there's no consing overhead to dwarf our inefficiency.)
+    (if (and (null more-sequences)
+            (null result-type))
+       (%map-for-effect-arity-1 really-function first-sequence)
+       ;; Otherwise, use the industrial-strength full-generality
+       ;; approach, consing O(N-ARGS) temporary storage (which can have
+       ;; DYNAMIC-EXTENT), then using O(N-ARGS * RESULT-LENGTH) time.
+       (let ((sequences (cons first-sequence more-sequences)))
+         (case (type-specifier-atom result-type)
+           ((nil) (%map-for-effect really-function sequences))
+           (list (%map-to-list really-function sequences))
+           ((simple-vector simple-string vector string array simple-array
+             bit-vector simple-bit-vector base-string simple-base-string)
+            (%map-to-vector result-type really-function sequences))
+           (t
+            (apply #'map
+                   (result-type-or-lose result-type t)
+                   really-function
+                   sequences)))))))
+
+(defun map (result-type function first-sequence &rest more-sequences)
+  (sequence-of-checked-length-given-type (apply #'%map
+                                               result-type
+                                               function
+                                               first-sequence
+                                               more-sequences)
+                                        ;; (The RESULT-TYPE isn't
+                                        ;; strictly the type of the
+                                        ;; result, because when
+                                        ;; RESULT-TYPE=NIL, the result
+                                        ;; actually has NULL type. But
+                                        ;; that special case doesn't
+                                        ;; matter here, since we only
+                                        ;; look closely at vector
+                                        ;; types; so we can just pass
+                                        ;; RESULT-TYPE straight through
+                                        ;; as a type specifier.)
+                                        result-type))
+
+;;; KLUDGE: MAP has been rewritten substantially since the fork from
+;;; CMU CL in order to give reasonable performance, but this
+;;; implementation of MAP-INTO still has the same problems as the old
+;;; MAP code. Ideally, MAP-INTO should be rewritten to be efficient in
+;;; the same way that the corresponding cases of MAP have been
+;;; rewritten. Instead of doing it now, though, it's easier to wait
+;;; until we have DYNAMIC-EXTENT, at which time it should become
+;;; extremely easy to define a reasonably efficient MAP-INTO in terms
+;;; of (MAP NIL ..). -- WHN 20000920
 (defun map-into (result-sequence function &rest sequences)
   (let* ((fp-result
          (and (arrayp result-sequence)
@@ -796,11 +829,12 @@ needed to check whether the supplied type is appropriate."
     (when fp-result
       (setf (fill-pointer result-sequence) len))
 
-    (dotimes (index len)
-      (setf (elt result-sequence index)
-           (apply function
-                  (mapcar #'(lambda (seq) (elt seq index))
-                          sequences)))))
+    (let ((really-fun (%coerce-callable-to-function function)))
+      (dotimes (index len)
+       (setf (elt result-sequence index)
+             (apply really-fun
+                    (mapcar #'(lambda (seq) (elt seq index))
+                            sequences))))))
   result-sequence)
 \f
 ;;;; quantifiers
@@ -849,10 +883,7 @@ needed to check whether the supplied type is appropriate."
                ;; enough that we can use an inline function instead
                ;; of a compiler macro (as above). -- WHN 20000410
                (define-compiler-macro ,name (pred first-seq &rest more-seqs)
-                 (let ((elements (mapcar (lambda (x)
-                                           (declare (ignore x))
-                                           (gensym "ARG"))
-                                         (cons first-seq more-seqs)))
+                 (let ((elements (make-gensym-list (1+ (length more-seqs))))
                        (blockname (gensym "BLOCK")))
                    (once-only ((pred pred))
                      `(block ,blockname
index 74d3f4e..9c9a3ff 100644 (file)
@@ -22,7 +22,7 @@
         (res (type-specifier
               (single-value-type
                (values-specifier-type (third type)))))
-        (arglist (loop repeat (1+ (length args)) collect (gensym))))
+        (arglist (make-gensym-list (1+ (length args)))))
     (cond
      ((null (intersection args lambda-list-keywords))
       `(defun (setf ,name) ,arglist
index 1a9112a..6ca5a51 100644 (file)
         tree)
        (t tree)))
 
-;;; Sharp-equal works as follows. When a label is assigned (ie when #= is
-;;; called) we GENSYM a symbol is which is used as an unforgeable tag.
-;;; *SHARP-SHARP-ALIST* maps the integer tag to this gensym.
+;;; Sharp-equal works as follows. When a label is assigned (i.e. when
+;;; #= is called) we GENSYM a symbol is which is used as an
+;;; unforgeable tag. *SHARP-SHARP-ALIST* maps the integer tag to this
+;;; gensym.
 ;;;
-;;; When SHARP-SHARP encounters a reference to a label, it returns the symbol
-;;; assoc'd with the label. Resolution of the reference is deferred until the
-;;; read done by #= finishes. Any already resolved tags (in
-;;; *SHARP-EQUAL-ALIST*) are simply returned.
+;;; When SHARP-SHARP encounters a reference to a label, it returns the
+;;; symbol assoc'd with the label. Resolution of the reference is
+;;; deferred until the read done by #= finishes. Any already resolved
+;;; tags (in *SHARP-EQUAL-ALIST*) are simply returned.
 ;;;
 ;;; After reading of the #= form is completed, we add an entry to
-;;; *SHARP-EQUAL-ALIST* that maps the gensym tag to the resolved object. Then
-;;; for each entry in the *SHARP-SHARP-ALIST, the current object is searched
-;;; and any uses of the gensysm token are replaced with the actual value.
+;;; *SHARP-EQUAL-ALIST* that maps the gensym tag to the resolved
+;;; object. Then for each entry in the *SHARP-SHARP-ALIST, the current
+;;; object is searched and any uses of the gensysm token are replaced
+;;; with the actual value.
 (defvar *sharp-sharp-alist* ())
 
 (defun sharp-equal (stream ignore label)
index 8665ecf..20425c0 100644 (file)
 ;;; system area pointer to it.
 #!-sb-fluid (declaim (inline %make-alien))
 (defun %make-alien (bits)
-  (declare (type sb!kernel:index bits) (optimize-interface (safety 2)))
+  (declare (type index bits) (optimize-interface (safety 2)))
   (alien-funcall (extern-alien "malloc" (function system-area-pointer unsigned))
-                (ash (the sb!kernel:index (+ bits 7)) -3)))
+                (ash (the index (+ bits 7)) -3)))
 
 #!-sb-fluid (declaim (inline free-alien))
 (defun free-alien (alien)
         (unless stub
           (setf stub
                 (let ((fun (gensym))
-                      (parms (loop repeat (length args) collect (gensym))))
+                      (parms (make-gensym-list (length args))))
                   (compile nil
                            `(lambda (,fun ,@parms)
                               (declare (type (alien ,type) ,fun))
                          :extern ,alien-name)
              ,@(alien-vars))
             ,(if (alien-values-type-p result-type)
-                 (let ((temps (loop
-                                repeat (length (alien-values-type-values
-                                                result-type))
-                                collect (gensym))))
+                 (let ((temps (make-gensym-list
+                               (length
+                                (alien-values-type-values result-type)))))
                    `(multiple-value-bind ,temps
                         (alien-funcall ,lisp-name ,@(alien-args))
                       (values ,@temps ,@(results))))
index a27d192..5175eab 100644 (file)
   #!+sb-doc
   "For each entry in HASH-TABLE, call the designated function on the key
    and value of the entry. Return NIL."
-  (let ((fun (coerce function-designator 'function))
+  (let ((fun (%coerce-callable-to-function function-designator))
        (size (length (hash-table-next-vector hash-table))))
     (declare (type function fun))
     (do ((i 1 (1+ i)))