(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*))))
- (cond ((step-form-p form)
- (ir1-convert-step start next result form))
- ((atom form)
+ (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))
((leaf-p 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
- (pushnew 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.
(:macro
(ir1-convert start next result
(careful-expand-macro (info :function :macro-function fun)
- form)))
+ form))
+ (unless (policy *lexenv* (zerop store-xref-data))
+ (record-macroexpansion fun (ctran-block start) *current-path*)))
((nil :function)
(ir1-convert-srctran start next result
(find-free-fun fun "shouldn't happen! (no-cmacro)")
(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
+;;; Does this form look like something that we should add single-stepping
+;;; instrumentation for?
+(defun step-form-p (form)
+ (flet ((step-symbol-p (symbol)
+ (not (member (symbol-package symbol)
+ (load-time-value
+ ;; KLUDGE: packages we're not interested in
+ ;; stepping.
+ (mapcar #'find-package '(sb!c sb!int sb!impl
+ sb!kernel sb!pcl)))))))
+ (and *allow-instrumenting*
+ (policy *lexenv* (= insert-step-conditions 3))
+ (listp form)
+ (symbolp (car form))
+ (step-symbol-p (car form)))))
+
;;; Convert a function call where the function FUN is a LEAF. FORM is
;;; the source for the call. We return the COMBINATION node so that
;;; the caller can poke at it if it wants to.
(let ((ctran (make-ctran))
(fun-lvar (make-lvar)))
(ir1-convert start ctran fun-lvar `(the (or function symbol) ,fun))
- (ir1-convert-combination-args fun-lvar ctran next result (cdr form))))
+ (let ((combination
+ (ir1-convert-combination-args fun-lvar ctran next result
+ (cdr form))))
+ (when (step-form-p form)
+ ;; Store a string representation of the form in the
+ ;; combination node. This will let the IR2 translator know
+ ;; that we want stepper instrumentation for this node. The
+ ;; string will be stored in the debug-info by DUMP-1-LOCATION.
+ (setf (combination-step-info combination)
+ (let ((*print-pretty* t)
+ (*print-circle* t)
+ (*print-readably* nil))
+ (prin1-to-string form))))
+ combination)))
;;; Convert the arguments to a call and make the COMBINATION
;;; node. FUN-LVAR yields the function to call. ARGS is the list of
(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