1.0.20.28: Fewer STYLE-WARNINGs for gf calls.
[sbcl.git] / src / compiler / ir1tran.lisp
index e633b96..d09ddce 100644 (file)
 
 (declaim (special *compiler-error-bailout*))
 
+;;; *CURRENT-FORM-NUMBER* is used in FIND-SOURCE-PATHS to compute the
+;;; form number to associate with a source path. This should be bound
+;;; to an initial value of 0 before the processing of each truly
+;;; top level form.
+(declaim (type index *current-form-number*))
+(defvar *current-form-number*)
+
 ;;; *SOURCE-PATHS* is a hashtable from source code forms to the path
 ;;; taken through the source to reach the form. This provides a way to
 ;;; keep track of the location of original source forms, even when
 ;;; macroexpansions and other arbitary permutations of the code
 ;;; happen. This table is initialized by calling FIND-SOURCE-PATHS on
 ;;; the original source.
+;;;
+;;; It is fairly useless to store symbols, characters, or fixnums in
+;;; this table, as 42 is EQ to 42 no matter where in the source it
+;;; appears. GET-SOURCE-PATH and NOTE-SOURCE-PATH functions should be
+;;; always used to access this table.
 (declaim (hash-table *source-paths*))
 (defvar *source-paths*)
 
+(declaim (inline source-form-has-path-p))
+(defun source-form-has-path-p (form)
+  (not (typep form '(or symbol fixnum character))))
+
+(defun get-source-path (form)
+  (when (source-form-has-path-p form)
+    (gethash form *source-paths*)))
+
+(defun note-source-path (form &rest arguments)
+  (when (source-form-has-path-p form)
+    (setf (gethash form *source-paths*)
+          (apply #'list* 'original-source-start *current-form-number* arguments))))
+
 ;;; *CURRENT-COMPONENT* is the COMPONENT structure which we link
 ;;; blocks into as we generate them. This just serves to glue the
 ;;; emitted blocks together until local call analysis and flow graph
         (eq (defined-fun-inlinep fun) :notinline)
         (eq (info :function :inlinep name) :notinline))))
 
+;; This will get redefined in PCL boot.
+(declaim (notinline update-info-for-gf))
+(defun maybe-update-info-for-gf (name)
+  (declare (ignorable name))
+  (values))
+
 ;;; Return a GLOBAL-VAR structure usable for referencing the global
 ;;; function NAME.
 (defun find-global-fun (name latep)
      :%source-name name
      :type (if (and (not latep)
                     (or *derive-function-types*
-                        (eq where :declared)
+                        (member where '(:declared :defined-method))
                         (and (member name *fun-names-in-this-file*
                                      :test #'equal)
                              (not (fun-lexically-notinline-p name)))))
-               (info :function :type name)
+               (progn
+                 (maybe-update-info-for-gf name)
+                 (info :function :type name))
                (specifier-type 'function))
+     :defined-type (if (eq where :defined)
+                       (info :function :type name)
+                       *universal-type*)
      :where-from where)))
 
 ;;; Has the *FREE-FUNS* entry FREE-FUN become invalid?
                        (type (type-specifier (info :variable :type name))))
                    `(macro . (the ,type ,expansion))))
                 (:constant
-                 (let ((value (info :variable :constant-value name)))
-                   (make-constant :value value
-                                  :%source-name name
-                                  :type (ctype-of value)
-                                  :where-from where-from)))
+                 (let ((value (symbol-value name)))
+                   ;; Override the values of standard symbols in XC,
+                   ;; since we can't redefine them.
+                   #+sb-xc-host
+                   (when (eql (find-symbol (symbol-name name) :cl) name)
+                     (multiple-value-bind (xc-value foundp)
+                         (info :variable :xc-constant-value name)
+                       (cond (foundp
+                              (setf value xc-value))
+                             ((not (eq value name))
+                              (compiler-warn
+                               "Using cross-compilation host's definition of ~S: ~A~%"
+                               name (symbol-value name))))))
+                   (find-constant value name)))
                 (t
                  (make-global-var :kind kind
                                   :%source-name name
 ;;; processed with MAKE-LOAD-FORM. We have to be careful, because
 ;;; CONSTANT might be circular. We also check that the constant (and
 ;;; any subparts) are dumpable at all.
-(eval-when (:compile-toplevel :load-toplevel :execute)
-  ;; The EVAL-WHEN is necessary for #.(1+ LIST-TO-HASH-TABLE-THRESHOLD)
-  ;; below. -- AL 20010227
-  (def!constant list-to-hash-table-threshold 32))
-(defun maybe-emit-make-load-forms (constant)
-  (let ((things-processed nil)
-        (count 0))
-    ;; FIXME: Does this LIST-or-HASH-TABLE messiness give much benefit?
-    (declare (type (or list hash-table) things-processed)
-             (type (integer 0 #.(1+ list-to-hash-table-threshold)) count)
-             (inline member))
-    (labels ((grovel (value)
+(defun maybe-emit-make-load-forms (constant &optional (name nil namep))
+  (let ((xset (alloc-xset)))
+    (labels ((trivialp (value)
+               (typep value
+                      '(or
+                        #-sb-xc-host unboxed-array
+                        #+sb-xc-host (simple-array (unsigned-byte 8) (*))
+                        symbol
+                        number
+                        character
+                        string)))
+             (grovel (value)
                ;; Unless VALUE is an object which which obviously
                ;; can't contain other objects
-               (unless (typep value
-                              '(or #-sb-xc-host unboxed-array
-                                   #+sb-xc-host (simple-array (unsigned-byte 8) (*))
-                                   symbol
-                                   number
-                                   character
-                                   string))
-                 (etypecase things-processed
-                   (list
-                    (when (member value things-processed :test #'eq)
-                      (return-from grovel nil))
-                    (push value things-processed)
-                    (incf count)
-                    (when (> count list-to-hash-table-threshold)
-                      (let ((things things-processed))
-                        (setf things-processed
-                              (make-hash-table :test 'eq))
-                        (dolist (thing things)
-                          (setf (gethash thing things-processed) t)))))
-                   (hash-table
-                    (when (gethash value things-processed)
-                      (return-from grovel nil))
-                    (setf (gethash value things-processed) t)))
+               (unless (trivialp value)
+                 (if (xset-member-p value xset)
+                     (return-from grovel nil)
+                     (add-to-xset value xset))
                  (typecase value
                    (cons
                     (grovel (car value))
                    ((array t)
                     (dotimes (i (array-total-size value))
                       (grovel (row-major-aref value i))))
-                   (;; In the target SBCL, we can dump any instance,
-                    ;; but in the cross-compilation host,
-                    ;; %INSTANCE-FOO functions don't work on general
-                    ;; instances, only on STRUCTURE!OBJECTs.
-                    #+sb-xc-host structure!object
+                   (#+sb-xc-host structure!object
                     #-sb-xc-host instance
+                    ;; In the target SBCL, we can dump any instance, but
+                    ;; in the cross-compilation host, %INSTANCE-FOO
+                    ;; functions don't work on general instances, only on
+                    ;; STRUCTURE!OBJECTs.
+                    ;;
+                    ;; FIXME: What about funcallable instances with
+                    ;; user-defined MAKE-LOAD-FORM methods?
                     (when (emit-make-load-form value)
                       (dotimes (i (- (%instance-length value)
                                      #+sb-xc-host 0
                     (compiler-error
                      "Objects of type ~S can't be dumped into fasl files."
                      (type-of value)))))))
-      (grovel constant)))
+      ;; Dump all non-trivial named constants using the name.
+      (if (and namep (not (typep constant '(or symbol character
+                                            ;; FIXME: Cold init breaks if we
+                                            ;; try to reference FP constants
+                                            ;; thru their names.
+                                            #+sb-xc-host number
+                                            #-sb-xc-host fixnum))))
+          (emit-make-load-form constant name)
+          (grovel constant))))
   (values))
 \f
 ;;;; some flow-graph hacking utilities
       (error "~S is already a predecessor of ~S." node-block block))
     (push node-block (block-pred block))))
 
+;;; Insert NEW before OLD in the flow-graph.
+(defun insert-node-before (old new)
+  (let ((prev (node-prev old))
+        (temp (make-ctran)))
+    (ensure-block-start prev)
+    (setf (ctran-next prev) nil)
+    (link-node-to-previous-ctran new prev)
+    (use-ctran new temp)
+    (link-node-to-previous-ctran old temp))
+  (values))
+
 ;;; This function is used to set the ctran for a node, and thus
 ;;; determine what receives the value.
 (defun use-lvar (node lvar)
 ;;; The hashtables used to hold global namespace info must be
 ;;; reallocated elsewhere. Note also that *LEXENV* is not bound, so
 ;;; that local macro definitions can be introduced by enclosing code.
-(defun ir1-toplevel (form path for-value)
+(defun ir1-toplevel (form path for-value &optional (allow-instrumenting t))
   (declare (list path))
   (let* ((*current-path* path)
          (component (make-empty-component))
          (*current-component* component)
-         (*allow-instrumenting* t))
+         (*allow-instrumenting* allow-instrumenting))
     (setf (component-name component) 'initial-component)
     (setf (component-kind component) :initial)
     (let* ((forms (if for-value `(,form) `(,form nil)))
             (functional-kind res) :toplevel)
       res)))
 
-;;; *CURRENT-FORM-NUMBER* is used in FIND-SOURCE-PATHS to compute the
-;;; form number to associate with a source path. This should be bound
-;;; to an initial value of 0 before the processing of each truly
-;;; top level form.
-(declaim (type index *current-form-number*))
-(defvar *current-form-number*)
-
 ;;; This function is called on freshly read forms to record the
 ;;; initial location of each form (and subform.) Form is the form to
 ;;; find the paths in, and TLF-NUM is the top level form number of the
     (sub-find-source-paths form (list tlf-num)))
   (values))
 (defun sub-find-source-paths (form path)
-  (unless (gethash form *source-paths*)
-    (setf (gethash form *source-paths*)
-          (list* 'original-source-start *current-form-number* path))
+  (unless (get-source-path form)
+    (note-source-path form path)
     (incf *current-form-number*)
     (let ((pos 0)
           (subform form)
                    '(progn
                       (when (atom subform) (return))
                       (let ((fm (car subform)))
-                        (when (consp fm)
-                          (sub-find-source-paths fm (cons pos path)))
+                        (if (consp fm)
+                            ;; If it's a cons, recurse
+                            (sub-find-source-paths fm (cons pos path))
+                            ;; Otherwise store the containing form. It's
+                            ;; not perfect, but better than nothing.
+                            (unless (zerop pos)
+                              (note-source-path subform pos path)))
                         (incf pos))
                       (setq subform (cdr subform))
                       (when (eq subform trail) (return)))))
   ;; namespace.
   (defun ir1-convert (start next result form)
     (ir1-error-bailout (start next result form)
-      (let ((*current-path* (or (gethash form *source-paths*)
-                                (cons form *current-path*))))
+      (let* ((*current-path* (or (get-source-path form)
+                                 (cons form *current-path*)))
+             (start (instrument-coverage start nil form)))
         (cond ((atom form)
                (cond ((and (symbolp form) (not (keywordp form)))
                       (ir1-convert-var start next result form))
     (values))
 
   ;; Generate a reference to a manifest constant, creating a new leaf
-  ;; if necessary. If we are producing a fasl file, make sure that
-  ;; MAKE-LOAD-FORM gets used on any parts of the constant that it
-  ;; needs to be.
+  ;; if necessary.
   (defun reference-constant (start next result value)
     (declare (type ctran start next)
-             (type (or lvar null) result)
-             (inline find-constant))
+             (type (or lvar null) result))
     (ir1-error-bailout (start next result value)
-     (when (producing-fasl-file)
-       (maybe-emit-make-load-forms value))
-     (let* ((leaf (find-constant value))
-            (res (make-ref leaf)))
-       (push res (leaf-refs leaf))
-       (link-node-to-previous-ctran res start)
-       (use-continuation res next result)))
+      (let* ((leaf (find-constant value))
+             (res (make-ref leaf)))
+        (push res (leaf-refs leaf))
+        (link-node-to-previous-ctran res start)
+        (use-continuation res next result)))
     (values)))
 
 ;;; Add FUNCTIONAL to the COMPONENT-REANALYZE-FUNCTIONALS, unless it's
 ;;; needed. If LEAF represents a defined function which has already
 ;;; been converted, and is not :NOTINLINE, then reference the
 ;;; functional instead.
-(defun reference-leaf (start next result leaf)
+(defun reference-leaf (start next result leaf &optional (name '.anonymous.))
   (declare (type ctran start next) (type (or lvar null) result) (type leaf leaf))
   (when (functional-p leaf)
     (assure-functional-live-p leaf))
                                     '(nil :optional)))
                      (maybe-reanalyze-functional leaf))
                    leaf))
-         (ref (make-ref leaf)))
+         (ref (make-ref leaf name)))
     (push ref (leaf-refs leaf))
     (setf (leaf-ever-used leaf) t)
     (link-node-to-previous-ctran ref start)
 (defun ir1-convert-var (start next result name)
   (declare (type ctran start next) (type (or lvar null) result) (symbol name))
   (let ((var (or (lexenv-find name vars) (find-free-var name))))
-    (etypecase var
-      (leaf
-       (when (lambda-var-p var)
-         (let ((home (ctran-home-lambda-or-null start)))
-           (when home
-             (sset-adjoin var (lambda-calls-or-closes home))))
-         (when (lambda-var-ignorep var)
-           ;; (ANSI's specification for the IGNORE declaration requires
-           ;; that this be a STYLE-WARNING, not a full WARNING.)
-           #-sb-xc-host
-           (compiler-style-warn "reading an ignored variable: ~S" name)
-           ;; there's no need for us to accept ANSI's lameness when
-           ;; processing our own code, though.
-           #+sb-xc-host
-           (warn "reading an ignored variable: ~S" name)))
-       (reference-leaf start next result var))
-      (cons
-       (aver (eq (car var) 'macro))
-       ;; FIXME: [Free] type declarations. -- APD, 2002-01-26
-       (ir1-convert start next result (cdr var)))
-      (heap-alien-info
-       (ir1-convert start next result `(%heap-alien ',var)))))
+    (if (and (global-var-p var) (not result))
+        ;; KLUDGE: If the reference is dead, convert using SYMBOL-VALUE
+        ;; which is not flushable, so that unbound dead variables signal
+        ;; an error (bug 412).
+        (ir1-convert start next result `(symbol-value ',name))
+        (etypecase var
+          (leaf
+           (when (lambda-var-p var)
+             (let ((home (ctran-home-lambda-or-null start)))
+               (when home
+                 (sset-adjoin var (lambda-calls-or-closes home))))
+             (when (lambda-var-ignorep var)
+               ;; (ANSI's specification for the IGNORE declaration requires
+               ;; that this be a STYLE-WARNING, not a full WARNING.)
+               #-sb-xc-host
+               (compiler-style-warn "reading an ignored variable: ~S" name)
+               ;; there's no need for us to accept ANSI's lameness when
+               ;; processing our own code, though.
+               #+sb-xc-host
+               (warn "reading an ignored variable: ~S" name)))
+           (reference-leaf start next result var name))
+          (cons
+           (aver (eq (car var) 'macro))
+           ;; FIXME: [Free] type declarations. -- APD, 2002-01-26
+           (ir1-convert start next result (cdr var)))
+          (heap-alien-info
+           (ir1-convert start next result `(%heap-alien ',var))))))
   (values))
 
 ;;; Find a compiler-macro for a form, taking FUNCALL into account.
              (let ((*print-pretty* nil)
                    ;; We rely on the printer to abbreviate FORM.
                    (*print-length* 3)
-                   (*print-level* 1))
+                   (*print-level* 3))
                (format
                 nil
                 #-sb-xc-host "(in macroexpansion of ~S)"
             (forms body))
         (loop
           (let ((form (car forms)))
+            (setf this-start
+                  (maybe-instrument-progn-like this-start forms form))
             (when (endp (cdr forms))
               (ir1-convert this-start next result form)
               (return))
               (setq this-start this-ctran
                     forms (cdr forms)))))))
   (values))
+
+\f
+;;;; code coverage
+
+;;; Check the policy for whether we should generate code coverage
+;;; instrumentation. If not, just return the original START
+;;; ctran. Otherwise insert code coverage instrumentation after
+;;; START, and return the new ctran.
+(defun instrument-coverage (start mode form)
+  ;; We don't actually use FORM for anything, it's just convenient to
+  ;; have around when debugging the instrumentation.
+  (declare (ignore form))
+  (if (and (policy *lexenv* (> store-coverage-data 0))
+           *code-coverage-records*
+           *allow-instrumenting*)
+      (let ((path (source-path-original-source *current-path*)))
+        (when mode
+          (push mode path))
+        (if (member (ctran-block start)
+                    (gethash path *code-coverage-blocks*))
+            ;; If this source path has already been instrumented in
+            ;; this block, don't instrument it again.
+            start
+            (let ((store
+                   ;; Get an interned record cons for the path. A cons
+                   ;; with the same object identity must be used for
+                   ;; each instrument for the same block.
+                   (or (gethash path *code-coverage-records*)
+                       (setf (gethash path *code-coverage-records*)
+                             (cons path +code-coverage-unmarked+))))
+                  (next (make-ctran))
+                  (*allow-instrumenting* nil))
+              (push (ctran-block start)
+                    (gethash path *code-coverage-blocks*))
+              (let ((*allow-instrumenting* nil))
+                (ir1-convert start next nil
+                             `(locally
+                                  (declare (optimize speed
+                                                     (safety 0)
+                                                     (debug 0)
+                                                     (check-constant-modification 0)))
+                                ;; We're being naughty here, and
+                                ;; modifying constant data. That's ok,
+                                ;; we know what we're doing.
+                                (%rplacd ',store t))))
+              next)))
+      start))
+
+;;; In contexts where we don't have a source location for FORM
+;;; e.g. due to it not being a cons, but where we have a source
+;;; location for the enclosing cons, use the latter source location if
+;;; available. This works pretty well in practice, since many PROGNish
+;;; macroexpansions will just directly splice a block of forms into
+;;; some enclosing form with `(progn ,@body), thus retaining the
+;;; EQness of the conses.
+(defun maybe-instrument-progn-like (start forms form)
+  (or (when (and *allow-instrumenting*
+                 (not (get-source-path form)))
+        (let ((*current-path* (get-source-path forms)))
+          (when *current-path*
+            (instrument-coverage start nil form))))
+      start))
+
+(defun record-code-coverage (info cc)
+  (setf (gethash info *code-coverage-info*) cc))
+
+(defun clear-code-coverage ()
+  (clrhash *code-coverage-info*))
+
+(defun reset-code-coverage ()
+  (maphash (lambda (info cc)
+             (declare (ignore info))
+             (dolist (cc-entry cc)
+               (setf (cdr cc-entry) +code-coverage-unmarked+)))
+           *code-coverage-info*))
+
+(defun code-coverage-record-marked (record)
+  (aver (consp record))
+  (ecase (cdr record)
+    ((#.+code-coverage-unmarked+) nil)
+    ((t) t)))
+
 \f
 ;;;; converting combinations
 
   (let ((node (make-combination fun-lvar)))
     (setf (lvar-dest fun-lvar) node)
     (collect ((arg-lvars))
-      (let ((this-start start))
+      (let ((this-start start)
+            (forms args))
         (dolist (arg args)
+          (setf this-start
+                (maybe-instrument-progn-like this-start forms arg))
+          (setf forms (cdr forms))
           (let ((this-ctran (make-ctran))
                 (this-lvar (make-lvar node)))
             (ir1-convert this-start this-ctran this-lvar arg)
                     (ir1-convert start next result transformed)))
               (ir1-convert-maybe-predicate start next result form var))))))
 
+;;; KLUDGE: If we insert a synthetic IF for a function with the PREDICATE
+;;; attribute, don't generate any branch coverage instrumentation for it.
+(defvar *instrument-if-for-code-coverage* t)
+
 ;;; If the function has the PREDICATE attribute, and the RESULT's DEST
 ;;; isn't an IF, then we convert (IF <form> T NIL), ensuring that a
 ;;; predicate always appears in a conditional context.
     (if (and info
              (ir1-attributep (fun-info-attributes info) predicate)
              (not (if-p (and result (lvar-dest result)))))
-        (ir1-convert start next result `(if ,form t nil))
+        (let ((*instrument-if-for-code-coverage* nil))
+          (ir1-convert start next result `(if ,form t nil)))
         (ir1-convert-combination-checking-type start next result form var))))
 
 ;;; Actually really convert a global function call that we are allowed
            (type leaf var))
   (let* ((node (ir1-convert-combination start next result form var))
          (fun-lvar (basic-combination-fun node))
-         (type (leaf-type var)))
-    (when (validate-call-type node type t)
+         (type (leaf-type var))
+         (defined-type (leaf-defined-type var)))
+    (when (validate-call-type node type defined-type t)
       (setf (lvar-%derived-type fun-lvar)
             (make-single-value-type type))
       (setf (lvar-reoptimize fun-lvar) nil)))
         (setf (lambda-var-ignorep var) t)))))
   (values))
 
-(defun process-dx-decl (names vars fvars)
+(defun process-dx-decl (names vars fvars kind)
   (flet ((maybe-notify (control &rest args)
            (when (policy *lexenv* (> speed inhibit-warnings))
              (apply #'compiler-notify control args))))
-    (if (policy *lexenv* (= stack-allocate-dynamic-extent 3))
-        (dolist (name names)
-          (cond
-            ((symbolp name)
-             (let* ((bound-var (find-in-bindings vars name))
-                    (var (or bound-var
-                             (lexenv-find name vars)
-                             (find-free-var name))))
-               (etypecase var
-                 (leaf
-                  (if bound-var
-                      (setf (leaf-dynamic-extent var) t)
-                      (maybe-notify
-                       "ignoring DYNAMIC-EXTENT declaration for free ~S"
-                       name)))
-                 (cons
-                  (compiler-error "DYNAMIC-EXTENT on symbol-macro: ~S" name))
-                 (heap-alien-info
-                  (compiler-error "DYNAMIC-EXTENT on heap-alien-info: ~S"
-                                  name)))))
-            ((and (consp name)
-                  (eq (car name) 'function)
-                  (null (cddr name))
-                  (valid-function-name-p (cadr name)))
-             (let* ((fname (cadr name))
-                    (bound-fun (find fname fvars
-                                     :key #'leaf-source-name
-                                     :test #'equal)))
-               (etypecase bound-fun
-                 (leaf
-                  #!+stack-allocatable-closures
-                  (setf (leaf-dynamic-extent bound-fun) t)
-                  #!-stack-allocatable-closures
-                  (maybe-notify
-                   "ignoring DYNAMIC-EXTENT declaration on a function ~S ~
+    (let ((dx (cond ((eq 'truly-dynamic-extent kind)
+                     :truly)
+                    ((and (eq 'dynamic-extent kind)
+                          *stack-allocate-dynamic-extent*)
+                     t))))
+      (if dx
+          (dolist (name names)
+            (cond
+              ((symbolp name)
+               (let* ((bound-var (find-in-bindings vars name))
+                      (var (or bound-var
+                               (lexenv-find name vars)
+                               (find-free-var name))))
+                 (etypecase var
+                   (leaf
+                    (if bound-var
+                        (setf (leaf-dynamic-extent var) dx)
+                        (maybe-notify
+                         "ignoring DYNAMIC-EXTENT declaration for free ~S"
+                         name)))
+                   (cons
+                    (compiler-error "DYNAMIC-EXTENT on symbol-macro: ~S" name))
+                   (heap-alien-info
+                    (compiler-error "DYNAMIC-EXTENT on heap-alien-info: ~S"
+                                    name)))))
+              ((and (consp name)
+                    (eq (car name) 'function)
+                    (null (cddr name))
+                    (valid-function-name-p (cadr name)))
+               (let* ((fname (cadr name))
+                      (bound-fun (find fname fvars
+                                       :key #'leaf-source-name
+                                       :test #'equal)))
+                 (etypecase bound-fun
+                   (leaf
+                    #!+stack-allocatable-closures
+                    (setf (leaf-dynamic-extent bound-fun) dx)
+                    #!-stack-allocatable-closures
+                    (maybe-notify
+                     "ignoring DYNAMIC-EXTENT declaration on a function ~S ~
                     (not supported on this platform)." fname))
-                 (cons
-                  (compiler-error "DYNAMIC-EXTENT on macro: ~S" fname))
-                 (null
-                  (maybe-notify
-                   "ignoring DYNAMIC-EXTENT declaration for free ~S"
-                   fname)))))
-            (t (compiler-error "DYNAMIC-EXTENT on a weird thing: ~S" name))))
-      (maybe-notify "ignoring DYNAMIC-EXTENT declarations for ~S" names))))
+                   (cons
+                    (compiler-error "DYNAMIC-EXTENT on macro: ~S" fname))
+                   (null
+                    (maybe-notify
+                     "ignoring DYNAMIC-EXTENT declaration for free ~S"
+                     fname)))))
+              (t (compiler-error "DYNAMIC-EXTENT on a weird thing: ~S" name))))
+          (maybe-notify "ignoring DYNAMIC-EXTENT declarations for ~S" names)))))
 
 ;;; FIXME: This is non-ANSI, so the default should be T, or it should
 ;;; go away, I think.
                        (car types)
                        `(values ,@types)))))
           res))
-       (dynamic-extent
-        (process-dx-decl (cdr spec) vars fvars)
+       ((dynamic-extent truly-dynamic-extent)
+        (process-dx-decl (cdr spec) vars fvars (first spec))
         res)
        ((disable-package-locks enable-package-locks)
         (make-lexenv