1.0.43.57: better handling of derived function types
[sbcl.git] / src / compiler / ir1tran-lambda.lisp
index 5bb387b..100c895 100644 (file)
     (compiler-error "The variable ~S occurs more than once in the lambda list."
                     name))
   (let ((kind (info :variable :kind name)))
-    (when (or (keywordp name) (eq kind :constant))
-      (compiler-error "The name of the lambda variable ~S is already in use to name a constant."
-                      name))
+    (cond ((or (keywordp name) (eq kind :constant))
+           (compiler-error "The name of the lambda variable ~S is already in use to name a constant."
+                           name))
+          ((eq :global kind)
+           (compiler-error "The name of the lambda variable ~S is already in use to name a global variable."
+                           name)))
     (cond ((eq kind :special)
            (let ((specvar (find-free-var name)))
              (make-lambda-var :%source-name name
     (setf debug-name (name-lambdalike form)))
   (multiple-value-bind (vars keyp allow-other-keys aux-vars aux-vals)
       (make-lambda-vars (cadr form))
-    (multiple-value-bind (forms decls) (parse-body (cddr form))
+    (multiple-value-bind (forms decls doc) (parse-body (cddr form))
       (binding* (((*lexenv* result-type post-binding-lexenv)
                   (process-decls decls (append aux-vars vars) nil
                                  :binding-form-p t))
                                                       :system-lambda system-lambda)))))
         (setf (functional-inline-expansion res) form)
         (setf (functional-arg-documentation res) (cadr form))
+        (setf (functional-documentation res) doc)
         (when (boundp '*lambda-conversions*)
           ;; KLUDGE: Not counting TL-XEPs is a lie, of course, but
           ;; keeps things less confusing to users of TIME, where this
      (let ((name (cadr thing))
            (lambda-expression `(lambda ,@(cddr thing))))
        (if (and name (legal-fun-name-p name))
-           (let ((defined-fun-res (get-defined-fun name))
+           (let ((defined-fun-res (get-defined-fun name (second lambda-expression)))
                  (res (ir1-convert-lambda lambda-expression
                                           :maybe-add-debug-catch t
                                           :source-name name)))
              (assert-global-function-definition-type name res)
-             (setf (defined-fun-functional defined-fun-res) res)
+             (push res (defined-fun-functionals defined-fun-res))
              (unless (eq (defined-fun-inlinep defined-fun-res) :notinline)
                (substitute-leaf-if
                 (lambda (ref)
       (setf (functional-inline-expanded clambda) t)
       clambda)))
 
+;;; Given a lambda-list, return a FUN-TYPE object representing the signature:
+;;; return type is *, and each individual arguments type is T -- but we get
+;;; the argument counts and keywords.
+(defun ftype-from-lambda-list (lambda-list)
+  (multiple-value-bind (req opt restp rest-name keyp key-list allowp morep)
+      (parse-lambda-list lambda-list)
+    (declare (ignore rest-name))
+    (flet ((t (list)
+             (mapcar (constantly t) list)))
+      (let ((reqs (t req))
+            (opts (when opt (cons '&optional (t opt))))
+            ;; When it comes to building a type, &REST means pretty much the
+            ;; same thing as &MORE.
+            (rest (when (or morep restp) (list '&rest t)))
+            (keys (when keyp
+                    (cons '&key (mapcar (lambda (spec)
+                                          (let ((key/var (if (consp spec)
+                                                             (car spec)
+                                                             spec)))
+                                            (list (if (consp key/var)
+                                                      (car key/var)
+                                                      (keywordicate key/var))
+                                                  t)))
+                                        key-list))))
+            (allow (when allowp (list '&allow-other-keys))))
+        (specifier-type `(function (,@reqs ,@opts ,@rest ,@keys ,@allow) *))))))
+
 ;;; Get a DEFINED-FUN object for a function we are about to define. If
 ;;; the function has been forward referenced, then substitute for the
 ;;; previous references.
-(defun get-defined-fun (name)
+(defun get-defined-fun (name &optional (lambda-list nil lp))
   (proclaim-as-fun-name name)
-  (let ((found (find-free-fun name "shouldn't happen! (defined-fun)")))
-    (note-name-defined name :function)
-    (cond ((not (defined-fun-p found))
-           (aver (not (info :function :inlinep name)))
-           (let* ((where-from (leaf-where-from found))
-                  (res (make-defined-fun
-                        :%source-name name
-                        :where-from (if (eq where-from :declared)
-                                        :declared :defined)
-                        :type (leaf-type found))))
-             (substitute-leaf res found)
-             (setf (gethash name *free-funs*) res)))
-          ;; If *FREE-FUNS* has a previously converted definition
-          ;; for this name, then blow it away and try again.
-          ((defined-fun-functional found)
-           (remhash name *free-funs*)
-           (get-defined-fun name))
-          (t found))))
+  (when (boundp '*free-funs*)
+    (let ((found (find-free-fun name "shouldn't happen! (defined-fun)")))
+      (note-name-defined name :function)
+      (cond ((not (defined-fun-p found))
+             (aver (not (info :function :inlinep name)))
+             (let* ((where-from (leaf-where-from found))
+                    (res (make-defined-fun
+                          :%source-name name
+                          :where-from (if (eq where-from :declared)
+                                          :declared
+                                          :defined-here)
+                          :type (if (eq :declared where-from)
+                                    (leaf-type found)
+                                    (if lp
+                                        (ftype-from-lambda-list lambda-list)
+                                        (specifier-type 'function))))))
+               (substitute-leaf res found)
+               (setf (gethash name *free-funs*) res)))
+            ;; If *FREE-FUNS* has a previously converted definition
+            ;; for this name, then blow it away and try again.
+            ((defined-fun-functionals found)
+             (remhash name *free-funs*)
+             (get-defined-fun name lambda-list))
+            (t found)))))
 
 ;;; Check a new global function definition for consistency with
 ;;; previous declaration or definition, and assert argument/result
     (setf (functional-inlinep fun) inlinep)
     (assert-new-definition var fun)
     (setf (defined-fun-inline-expansion var) expansion)
+    ;; Associate VAR with the FUN -- and in case of an optional dispatch
+    ;; with the various entry-points. This allows XREF to know where the
+    ;; inline CLAMBDA comes from.
+    (flet ((note-inlining (f)
+             (typecase f
+               (functional
+                (setf (functional-inline-expanded f) var))
+               (cons
+                ;; Delayed entry-point.
+                (if (car f)
+                    (setf (functional-inline-expanded (cdr f)) var)
+                    (let ((old-thunk (cdr f)))
+                      (setf (cdr f) (lambda ()
+                                      (let ((g (funcall old-thunk)))
+                                        (setf (functional-inline-expanded g) var)
+                                        g)))))))))
+      (note-inlining fun)
+      (when (optional-dispatch-p fun)
+        (note-inlining (optional-dispatch-main-entry fun))
+        (note-inlining (optional-dispatch-more-entry fun))
+        (mapc #'note-inlining (optional-dispatch-entry-points fun))))
     ;; substitute for any old references
     (unless (or (not *block-compile*)
                 (and info
 (defun %compiler-defun (name lambda-with-lexenv compile-toplevel)
   (let ((defined-fun nil)) ; will be set below if we're in the compiler
     (when compile-toplevel
-      ;; better be in the compiler
-      (aver (boundp '*lexenv*))
-      (remhash name *free-funs*)
-      (setf defined-fun (get-defined-fun name))
-      (aver (fasl-output-p *compile-object*))
-      (if (member name *fun-names-in-this-file* :test #'equal)
-          (warn 'duplicate-definition :name name)
-          (push name *fun-names-in-this-file*)))
+      (setf defined-fun (if lambda-with-lexenv
+                            (get-defined-fun name (fifth lambda-with-lexenv))
+                            (get-defined-fun name)))
+      (when (boundp '*lexenv*)
+        (remhash name *free-funs*)
+        (aver (fasl-output-p *compile-object*))
+        (if (member name *fun-names-in-this-file* :test #'equal)
+            (warn 'duplicate-definition :name name)
+            (push name *fun-names-in-this-file*))))
 
     (become-defined-fun-name name)
 
     ;; old CMU CL comment:
     ;;   If there is a type from a previous definition, blast it,
     ;;   since it is obsolete.
-    (when (and defined-fun
-               (eq (leaf-where-from defined-fun) :defined))
+    (when (and defined-fun (neq :declared (leaf-where-from defined-fun)))
       (setf (leaf-type defined-fun)
             ;; FIXME: If this is a block compilation thing, shouldn't
             ;; we be setting the type to the full derived type for the