Warn when wrapping constants with THE of multiple value types
[sbcl.git] / src / compiler / ir1-translators.lisp
index d26e53a..7bd2bfb 100644 (file)
@@ -26,7 +26,7 @@ forms, returns NIL."
   #!+sb-doc
   "IF predicate then [else]
 
-If PREDICATE evaluates to false, evaluate THEN and return its values,
+If PREDICATE evaluates to true, evaluate THEN and return its values,
 otherwise evaluate ELSE and return its values. ELSE defaults to NIL."
   (let* ((pred-ctran (make-ctran))
          (pred-lvar (make-lvar))
@@ -42,7 +42,10 @@ otherwise evaluate ELSE and return its values. ELSE defaults to NIL."
     ;; IR1-CONVERT-MAYBE-PREDICATE requires DEST to be CIF, so the
     ;; order of the following two forms is important
     (setf (lvar-dest pred-lvar) node)
-    (ir1-convert start pred-ctran pred-lvar test)
+    (multiple-value-bind (context count) (possible-rest-arg-context test)
+      (if context
+          (ir1-convert start pred-ctran pred-lvar `(%rest-true ,test ,context ,count))
+          (ir1-convert start pred-ctran pred-lvar test)))
     (link-node-to-previous-ctran node pred-ctran)
 
     (let ((start-block (ctran-block pred-ctran)))
@@ -75,9 +78,14 @@ otherwise evaluate ELSE and return its values. ELSE defaults to NIL."
       nil
       (labels ((sub (form)
                  (or (get-source-path form)
-                     (and (consp form)
-                          (some #'sub form)))))
-        (or (sub form)))))
+                     (when (consp form)
+                       (unless (eq 'quote (car form))
+                         (somesub form)))))
+               (somesub (forms)
+                 (when (consp forms)
+                   (or (sub (car forms))
+                       (somesub (cdr forms))))))
+        (sub form))))
 \f
 ;;;; BLOCK and TAGBODY
 
@@ -193,11 +201,11 @@ extent of the block."
   #!+sb-doc
   "TAGBODY {tag | statement}*
 
-Define tags for use with GO. The STATEMENTS are evaluated in order ,skipping
+Define tags for use with GO. The STATEMENTS are evaluated in order, skipping
 TAGS, and NIL is returned. If a statement contains a GO to a defined TAG
 within the lexical scope of the form, then control is transferred to the next
-statement following that tag. A TAG must an integer or a symbol. A STATEMENT
-must be a list. Other objects are illegal within the body."
+statement following that tag. A TAG must be an integer or a symbol. A
+STATEMENT must be a list. Other objects are illegal within the body."
   (start-block start)
   (ctran-starts-block next)
   (let* ((dummy (make-ctran))
@@ -347,7 +355,7 @@ Evaluate the FORMS in the specified SITUATIONS (any of :COMPILE-TOPLEVEL,
   "MACROLET ({(name lambda-list form*)}*) body-form*
 
 Evaluate the BODY-FORMS in an environment with the specified local macros
-defined. Name is the local macro name, LAMBDA-LIST is a DEFMACRO style
+defined. NAME is the local macro name, LAMBDA-LIST is a DEFMACRO style
 destructuring lambda list, and the FORMS evaluate to the expansion."
   (funcall-in-macrolet-lexenv
    definitions
@@ -373,7 +381,7 @@ destructuring lambda list, and the FORMS evaluate to the expansion."
           (program-assert-symbol-home-package-unlocked
            context name "binding ~A as a local symbol-macro"))
         (let ((kind (info :variable :kind name)))
-          (when (member kind '(:special :constant))
+          (when (member kind '(:special :constant :global))
             (fail "Attempt to bind a ~(~A~) variable with SYMBOL-MACROLET: ~S"
                   kind name)))
         ;; A magical cons that MACROEXPAND-1 understands.
@@ -449,7 +457,7 @@ body, references to a NAME will effectively be replaced with the EXPANSION."
                nargs
                min)))
 
-    (when (eq (template-result-types template) :conditional)
+    (when (template-conditional-p template)
       (bug "%PRIMITIVE was used with a conditional template."))
 
     (when (template-more-results-type template)
@@ -471,22 +479,47 @@ body, references to a NAME will effectively be replaced with the EXPANSION."
 Return VALUE without evaluating it."
   (reference-constant start next result thing))
 \f
+(defun name-context ()
+  ;; Name of the outermost non-NIL BLOCK, or the source namestring
+  ;; of the source file.
+  (let ((context
+          (or (car (find-if (lambda (b)
+                              (let ((name (pop b)))
+                                (and name
+                                     ;; KLUDGE: High debug adds this block on
+                                     ;; some platforms.
+                                     #!-unwind-to-frame-and-call-vop
+                                     (neq 'return-value-tag name)
+                                     ;; KLUDGE: CATCH produces blocks whose
+                                     ;; cleanup is :CATCH.
+                                     (neq :catch (cleanup-kind (entry-cleanup (pop b)))))))
+                            (lexenv-blocks *lexenv*) :from-end t))
+              *source-namestring*
+              (let ((p (or *compile-file-truename* *load-truename*)))
+                (when p (namestring p))))))
+    (when context
+      (list :in context))))
+
 ;;;; FUNCTION and NAMED-LAMBDA
 (defun name-lambdalike (thing)
-  (ecase (car thing)
+  (case (car thing)
     ((named-lambda)
      (or (second thing)
-         `(lambda ,(third thing))))
-    ((lambda instance-lambda)
-     `(lambda ,(second thing)))
+         `(lambda ,(third thing) ,(name-context))))
+    ((lambda)
+     `(lambda ,(second thing) ,@(name-context)))
     ((lambda-with-lexenv)
-     `(lambda ,(fifth thing)))))
+     ;; FIXME: Get the original DEFUN name here.
+     `(lambda ,(fifth thing)))
+    (otherwise
+     (compiler-error "Not a valid lambda expression:~%  ~S"
+                     thing))))
 
 (defun fun-name-leaf (thing)
   (if (consp thing)
       (cond
         ((member (car thing)
-                 '(lambda named-lambda instance-lambda lambda-with-lexenv))
+                 '(lambda named-lambda lambda-with-lexenv))
          (values (ir1-convert-lambdalike
                   thing
                   :debug-name (name-lambdalike thing))
@@ -509,9 +542,9 @@ Return VALUE without evaluating it."
       (dolist (lambda lambdas)
         (setf (functional-allocator lambda) allocator)))))
 
-(defmacro with-fun-name-leaf ((leaf thing start &key global) &body body)
+(defmacro with-fun-name-leaf ((leaf thing start &key global-function) &body body)
   `(multiple-value-bind (,leaf allocate-p)
-       (if ,global
+       (if ,global-function
            (find-global-fun ,thing t)
            (fun-name-leaf ,thing))
      (if allocate-p
@@ -535,14 +568,45 @@ be a lambda expression."
 ;;; expansions, and doesn't nag about undefined functions.
 ;;; Used for optimizing things like (FUNCALL 'FOO).
 (def-ir1-translator global-function ((thing) start next result)
-  (with-fun-name-leaf (leaf thing start :global t)
+  (with-fun-name-leaf (leaf thing start :global-function t)
     (reference-leaf start next result leaf)))
 
 (defun constant-global-fun-name (thing)
   (let ((constantp (sb!xc:constantp thing)))
-    (and constantp
-         (let ((name (constant-form-value thing)))
-           (and (legal-fun-name-p name) name)))))
+    (when constantp
+      (let ((name (constant-form-value thing)))
+        (when (legal-fun-name-p name)
+          name)))))
+
+(defun lvar-constant-global-fun-name (lvar)
+  (when (constant-lvar-p lvar)
+    (let ((name (lvar-value lvar)))
+      (when (legal-fun-name-p name)
+        name))))
+
+(defun ensure-source-fun-form (source &optional give-up)
+  (let ((op (when (consp source) (car source))))
+    (cond ((eq op '%coerce-callable-to-fun)
+           (ensure-source-fun-form (second source)))
+          ((member op '(function global-function lambda named-lambda))
+           (values source nil))
+          (t
+           (let ((cname (constant-global-fun-name source)))
+             (if cname
+                 (values `(global-function ,cname) nil)
+                 (values `(%coerce-callable-to-fun ,source) give-up)))))))
+
+(defun ensure-lvar-fun-form (lvar lvar-name &optional give-up)
+  (aver (and lvar-name (symbolp lvar-name)))
+  (if (csubtypep (lvar-type lvar) (specifier-type 'function))
+      lvar-name
+      (let ((cname (lvar-constant-global-fun-name lvar)))
+        (cond (cname
+               `(global-function ,cname))
+              (give-up
+               (give-up-ir1-transform "not known to be a function"))
+              (t
+               `(%coerce-callable-to-fun ,lvar-name))))))
 \f
 ;;;; FUNCALL
 
@@ -552,45 +616,43 @@ be a lambda expression."
 (deftransform funcall ((function &rest args) * *)
   (let ((arg-names (make-gensym-list (length args))))
     `(lambda (function ,@arg-names)
-       (%funcall ,(if (csubtypep (lvar-type function)
-                                 (specifier-type 'function))
-                      'function
-                      '(%coerce-callable-to-fun function))
-                 ,@arg-names))))
+       (declare (ignorable function))
+       `(%funcall ,(ensure-lvar-fun-form function 'function) ,@arg-names))))
 
 (def-ir1-translator %funcall ((function &rest args) start next result)
-  (cond ((and (consp function) (eq (car function) 'function))
-         (with-fun-name-leaf (leaf (second function) start)
-           (ir1-convert start next result `(,leaf ,@args))))
-        ((and (consp function) (eq (car function) 'global-function))
-         (with-fun-name-leaf (leaf (second function) start :global t)
-           (ir1-convert start next result `(,leaf ,@args))))
-        (t
-         (let ((ctran (make-ctran))
-               (fun-lvar (make-lvar)))
-           (ir1-convert start ctran fun-lvar `(the function ,function))
-           (ir1-convert-combination-args fun-lvar ctran next result args)))))
+  ;; MACROEXPAND so that (LAMBDA ...) forms arriving here don't get an
+  ;; extra cast inserted for them.
+  (let* ((function (%macroexpand function *lexenv*))
+         (op (when (consp function) (car function))))
+    (cond ((eq op 'function)
+           (compiler-destructuring-bind (thing) (cdr function)
+               function
+             (with-fun-name-leaf (leaf thing start)
+               (ir1-convert start next result `(,leaf ,@args)))))
+          ((eq op 'global-function)
+           (compiler-destructuring-bind (thing) (cdr function)
+               global-function
+             (with-fun-name-leaf (leaf thing start :global-function t)
+               (ir1-convert start next result `(,leaf ,@args)))))
+          (t
+           (let ((ctran (make-ctran))
+                 (fun-lvar (make-lvar)))
+             (ir1-convert start ctran fun-lvar `(the function ,function))
+             (ir1-convert-combination-args fun-lvar ctran next result args))))))
 
 ;;; This source transform exists to reduce the amount of work for the
 ;;; compiler. If the called function is a FUNCTION form, then convert
 ;;; directly to %FUNCALL, instead of waiting around for type
 ;;; inference.
 (define-source-transform funcall (function &rest args)
-  (if (and (consp function) (member (car function) '(function lambda)))
-      `(%funcall ,function ,@args)
-      (let ((name (constant-global-fun-name function)))
-        (if name
-            `(%funcall (global-function ,name) ,@args)
-            (values nil t)))))
-
-(deftransform %coerce-callable-to-fun ((thing) (function) *)
+  `(%funcall ,(ensure-source-fun-form function) ,@args))
+
+(deftransform %coerce-callable-to-fun ((thing) * * :node node)
   "optimize away possible call to FDEFINITION at runtime"
-  'thing)
+  (ensure-lvar-fun-form thing 'thing t))
 
 (define-source-transform %coerce-callable-to-fun (thing)
-  (if (and (consp thing) (member (car thing) '(function lambda)))
-      thing
-      (values nil t)))
+  (ensure-source-fun-form thing t))
 \f
 ;;;; LET and LET*
 ;;;;
@@ -615,7 +677,8 @@ be a lambda expression."
              (varify-lambda-arg name
                                 (if (eq context 'let*)
                                     nil
-                                    (names)))))
+                                    (names))
+                                context)))
       (dolist (spec bindings)
         (cond ((atom spec)
                (let ((var (get-var spec)))
@@ -733,8 +796,9 @@ also processed as top level forms."
           (program-assert-symbol-home-package-unlocked
            :compile name "binding ~A as a local function"))
         (names name)
-        (multiple-value-bind (forms decls) (parse-body (cddr def))
+        (multiple-value-bind (forms decls doc) (parse-body (cddr def))
           (defs `(lambda ,(second def)
+                   ,@(when doc (list doc))
                    ,@decls
                    (block ,(fun-name-block-name name)
                      . ,forms))))))
@@ -776,10 +840,11 @@ lexically apparent function definition in the enclosing environment."
     (multiple-value-bind (names defs)
         (extract-flet-vars definitions 'flet)
       (let ((fvars (mapcar (lambda (n d)
-                             (ir1-convert-lambda d
-                                                 :source-name n
-                                                 :maybe-add-debug-catch t
-                                                 :debug-name (debug-name 'flet n)))
+                             (ir1-convert-lambda
+                              d :source-name n
+                                :maybe-add-debug-catch t
+                                :debug-name
+                                (debug-name 'flet n t)))
                            names defs)))
         (processing-decls (decls nil fvars next result)
           (let ((*lexenv* (make-lexenv :funs (pairlis names fvars))))
@@ -814,7 +879,7 @@ other."
                           (ir1-convert-lambda def
                                               :source-name name
                                               :maybe-add-debug-catch t
-                                              :debug-name (debug-name 'labels name)))
+                                              :debug-name (debug-name 'labels name t)))
                         names defs))))
 
         ;; Modify all the references to the dummy function leaves so
@@ -847,6 +912,8 @@ other."
                     (values-subtypep (make-single-value-type (leaf-type value))
                                      type))
                (and (sb!xc:constantp value)
+                    (or (not (values-type-p type))
+                        (values-type-may-be-single-value-p type))
                     (ctypep (constant-form-value value)
                             (single-value-type type))))
            (ir1-convert start next result value))
@@ -878,6 +945,12 @@ is unable to derive from other declared types."
 ;;; whatever you tell it. It will never generate a type check, but
 ;;; will cause a warning if the compiler can prove the assertion is
 ;;; wrong.
+;;;
+;;; For the benefit of code-walkers we also add a macro-expansion. (Using INFO
+;;; directly to get around safeguards for adding a macro-expansion for special
+;;; operator.) Because :FUNCTION :KIND remains :SPECIAL-FORM, the compiler
+;;; never uses the macro -- but manually calling its MACRO-FUNCTION or
+;;; MACROEXPANDing TRULY-THE forms does.
 (def-ir1-translator truly-the ((value-type form) start next result)
   #!+sb-doc
   "Specifies that the values returned by FORM conform to the
@@ -888,6 +961,12 @@ Consequences are undefined if any result is not of the declared type
 -- typical symptoms including memory corruptions. Use with great
 care."
   (the-in-policy value-type form '((type-check . 0)) start next result))
+
+#-sb-xc-host
+(setf (info :function :macro-function 'truly-the)
+      (lambda (whole env)
+        (declare (ignore env))
+        `(the ,@(cdr whole))))
 \f
 ;;;; SETQ
 
@@ -916,7 +995,7 @@ care."
                  (compiler-style-warn
                   "~S is being set even though it was declared to be ignored."
                   name)))
-             (if (and (global-var-p leaf) (eq :global (global-var-kind leaf)))
+             (if (and (global-var-p leaf) (eq :unknown (global-var-kind leaf)))
                  ;; For undefined variables go through SET, so that we can catch
                  ;; constant modifications.
                  (ir1-convert start next result `(set ',name ,value-form))
@@ -944,7 +1023,8 @@ care."
         (dest-lvar (make-lvar))
         (type (or (lexenv-find var type-restrictions)
                   (leaf-type var))))
-    (ir1-convert start dest-ctran dest-lvar `(the ,type ,value))
+    (ir1-convert start dest-ctran dest-lvar `(the ,(type-specifier type)
+                                                  ,value))
     (let ((res (make-set :var var :value dest-lvar)))
       (setf (lvar-dest dest-lvar) res)
       (setf (leaf-ever-used var) t)
@@ -1061,6 +1141,7 @@ due to normal completion or a non-local exit such as THROW)."
         ;; ,CLEANUP-FUN should probably be declared DYNAMIC-EXTENT,
         ;; and something can be done to make %ESCAPE-FUN have
         ;; dynamic extent too.
+        (declare (dynamic-extent #',cleanup-fun))
         (block ,drop-thru-tag
           (multiple-value-bind (,next ,start ,count)
               (block ,exit-tag
@@ -1094,13 +1175,7 @@ values from the first VALUES-FORM making up the first argument, etc."
                    ;; important for simplifying compilation of
                    ;; MV-COMBINATIONS.
                    (make-combination fun-lvar))))
-    (ir1-convert start ctran fun-lvar
-                 (if (and (consp fun) (eq (car fun) 'function))
-                     fun
-                     (let ((name (constant-global-fun-name fun)))
-                       (if name
-                           `(global-function ,name)
-                           `(%coerce-callable-to-fun ,fun)))))
+    (ir1-convert start ctran fun-lvar (ensure-source-fun-form fun))
     (setf (lvar-dest fun-lvar) node)
     (collect ((arg-lvars))
       (let ((this-start ctran))