0.8.0.3:
[sbcl.git] / src / compiler / ir1opt.lisp
index d585c20..2e8c17f 100644 (file)
 ;;; constant leaf.
 (defun constant-continuation-p (thing)
   (and (continuation-p thing)
-       (let ((use (continuation-use thing)))
-        (and (ref-p use)
-             (constant-p (ref-leaf use))))))
+       (let ((use (principal-continuation-use thing)))
+         (and (ref-p use) (constant-p (ref-leaf use))))))
 
 ;;; Return the constant value for a continuation whose only use is a
 ;;; constant node.
 (declaim (ftype (function (continuation) t) continuation-value))
 (defun continuation-value (cont)
-  (aver (constant-continuation-p cont))
-  (constant-value (ref-leaf (continuation-use cont))))
+  (let ((use (principal-continuation-use cont)))
+    (constant-value (ref-leaf use))))
 \f
 ;;;; interface for obtaining results of type inference
 
-;;; Return a (possibly values) type that describes what we have proven
-;;; about the type of Cont without taking any type assertions into
-;;; consideration. This is just the union of the NODE-DERIVED-TYPE of
-;;; all the uses. Most often people use CONTINUATION-DERIVED-TYPE or
-;;; CONTINUATION-TYPE instead of using this function directly.
-(defun continuation-proven-type (cont)
-  (declare (type continuation cont))
-  (ecase (continuation-kind cont)
-    ((:block-start :deleted-block-start)
-     (let ((uses (block-start-uses (continuation-block cont))))
-       (if uses
-          (do ((res (node-derived-type (first uses))
-                    (values-type-union (node-derived-type (first current))
-                                       res))
-               (current (rest uses) (rest current)))
-              ((null current) res))
-          *empty-type*)))
-    (:inside-block
-     (node-derived-type (continuation-use cont)))))
-
 ;;; Our best guess for the type of this continuation's value. Note
 ;;; that this may be VALUES or FUNCTION type, which cannot be passed
 ;;; as an argument to the normal type operations. See
@@ -63,7 +42,7 @@
 ;;;
 ;;; What we do is call CONTINUATION-PROVEN-TYPE and check whether the
 ;;; result is a subtype of the assertion. If so, return the proven
-;;; type and set TYPE-CHECK to nil. Otherwise, return the intersection
+;;; type and set TYPE-CHECK to NIL. Otherwise, return the intersection
 ;;; of the asserted and proven types, and set TYPE-CHECK T. If
 ;;; TYPE-CHECK already has a non-null value, then preserve it. Only in
 ;;; the somewhat unusual circumstance of a newly discovered assertion
 (defun continuation-derived-type (cont)
   (declare (type continuation cont))
   (or (continuation-%derived-type cont)
-      (%continuation-derived-type cont)))
+      (setf (continuation-%derived-type cont)
+            (%continuation-derived-type cont))))
 (defun %continuation-derived-type (cont)
   (declare (type continuation cont))
-  (let ((proven (continuation-proven-type cont))
-       (asserted (continuation-asserted-type cont)))
-    (cond ((values-subtypep proven asserted)
-          (setf (continuation-%type-check cont) nil)
-          (setf (continuation-%derived-type cont) proven))
-          ((and (values-subtypep proven (specifier-type 'function))
-                (values-subtypep asserted (specifier-type 'function)))
-          ;; It's physically impossible for a runtime type check to
-          ;; distinguish between the various subtypes of FUNCTION, so
-          ;; it'd be pointless to do more type checks here.
-           (setf (continuation-%type-check cont) nil)
-           (setf (continuation-%derived-type cont)
-                ;; FIXME: This should depend on optimization
-                ;; policy. This is for SPEED > SAFETY:
-                 #+nil (values-type-intersection asserted proven)
-                 ;; and this is for SAFETY >= SPEED:
-                 #-nil proven))
-         (t
-          (unless (or (continuation-%type-check cont)
-                      (not (continuation-dest cont))
-                      (eq asserted *universal-type*))
-            (setf (continuation-%type-check cont) t))
-
-          (setf (continuation-%derived-type cont)
-                (values-type-intersection asserted proven))))))
-
-;;; Call CONTINUATION-DERIVED-TYPE to make sure the slot is up to
-;;; date, then return it.
-#!-sb-fluid (declaim (inline continuation-type-check))
-(defun continuation-type-check (cont)
-  (declare (type continuation cont))
-  (continuation-derived-type cont)
-  (continuation-%type-check cont))
+  (ecase (continuation-kind cont)
+    ((:block-start :deleted-block-start)
+     (let ((uses (block-start-uses (continuation-block cont))))
+       (if uses
+          (do ((res (node-derived-type (first uses))
+                    (values-type-union (node-derived-type (first current))
+                                       res))
+               (current (rest uses) (rest current)))
+              ((null current) res))
+          *empty-type*)))
+    (:inside-block
+     (node-derived-type (continuation-use cont)))))
 
 ;;; Return the derived type for CONT's first value. This is guaranteed
 ;;; not to be a VALUES or FUNCTION type.
-(declaim (ftype (function (continuation) ctype) continuation-type))
+(declaim (ftype (sfunction (continuation) ctype) continuation-type))
 (defun continuation-type (cont)
   (single-value-type (continuation-derived-type cont)))
 
                      and type of-type ctype in arg-types
                      do (when arg
                           (setf (continuation-%externally-checkable-type arg)
-                                type)))
+                                (coerce-to-values type))))
                   (continuation-%externally-checkable-type cont)))))))
+(declaim (inline flush-continuation-externally-checkable-type))
+(defun flush-continuation-externally-checkable-type (cont)
+  (declare (type continuation cont))
+  (setf (continuation-%externally-checkable-type cont) nil))
 \f
 ;;;; interface routines used by optimizers
 
 ;;; careful not to fly into space when the DEST's PREV is missing.
 (defun reoptimize-continuation (cont)
   (declare (type continuation cont))
+  (setf (continuation-%derived-type cont) nil)
   (unless (member (continuation-kind cont) '(:deleted :unused))
-    (setf (continuation-%derived-type cont) nil)
     (let ((dest (continuation-dest cont)))
       (when dest
        (setf (continuation-reoptimize cont) t)
       (setf (block-type-check (node-block node)) t)))
   (values))
 
+(defun reoptimize-continuation-uses (cont)
+  (declare (type continuation cont))
+  (dolist (use (find-uses cont))
+    (setf (node-reoptimize use) t)
+    (setf (block-reoptimize (node-block use)) t)
+    (setf (component-reoptimize (node-component use)) t)))
+
 ;;; Annotate NODE to indicate that its result has been proven to be
 ;;; TYPEP to RTYPE. After IR1 conversion has happened, this is the
 ;;; only correct way to supply information discovered about a node's
   (declare (type node node) (type ctype rtype))
   (let ((node-type (node-derived-type node)))
     (unless (eq node-type rtype)
-      (let ((int (values-type-intersection node-type rtype)))
+      (let ((int (values-type-intersection node-type rtype))
+            (cont (node-cont node)))
        (when (type/= node-type int)
          (when (and *check-consistency*
                     (eq int *empty-type*)
               (type-specifier rtype) (type-specifier node-type))))
          (setf (node-derived-type node) int)
           (when (and (ref-p node)
-                     (member-type-p int)
-                     (null (rest (member-type-members int)))
                      (lambda-var-p (ref-leaf node)))
-            (change-ref-leaf node (find-constant (first (member-type-members int)))))
-         (reoptimize-continuation (node-cont node))))))
-  (values))
-
-(defun set-continuation-type-assertion (cont atype ctype)
-  (declare (type continuation cont) (type ctype atype ctype))
-  (when (eq atype *wild-type*)
-    (return-from set-continuation-type-assertion))
-  (let* ((old-atype (continuation-asserted-type cont))
-         (old-ctype (continuation-type-to-check cont))
-         (new-atype (values-type-intersection old-atype atype))
-         (new-ctype (values-type-intersection old-ctype ctype)))
-    (when (or (type/= old-atype new-atype)
-              (type/= old-ctype new-ctype))
-      (setf (continuation-asserted-type cont) new-atype)
-      (setf (continuation-type-to-check cont) new-ctype)
-      (do-uses (node cont)
-        (setf (block-attributep (block-flags (node-block node))
-                                type-check type-asserted)
-              t))
-      (reoptimize-continuation cont)))
+            (let ((type (single-value-type int)))
+              (when (and (member-type-p type)
+                         (null (rest (member-type-members type))))
+                (change-ref-leaf node (find-constant
+                                       (first (member-type-members type)))))))
+         (reoptimize-continuation cont)))))
   (values))
 
 ;;; This is similar to DERIVE-NODE-TYPE, but asserts that it is an
-;;; error for CONT's value not to be TYPEP to TYPE. If we improve the
-;;; assertion, we set TYPE-CHECK and TYPE-ASSERTED to guarantee that
-;;; the new assertion will be checked.
+;;; error for CONT's value not to be TYPEP to TYPE. We implement it
+;;; moving uses behind a new CAST node. If we improve the assertion,
+;;; we set TYPE-CHECK and TYPE-ASSERTED to guarantee that the new
+;;; assertion will be checked.
 (defun assert-continuation-type (cont type policy)
   (declare (type continuation cont) (type ctype type))
-  (when (eq type *wild-type*)
+  (when (values-subtypep (continuation-derived-type cont) type)
     (return-from assert-continuation-type))
-  (set-continuation-type-assertion cont type (maybe-weaken-check type policy)))
+  (let* ((dest (continuation-dest cont))
+         (prev-cont (node-prev dest)))
+    (aver dest)
+    (with-ir1-environment-from-node dest
+      (let* ((cast (make-cast cont type policy))
+             (checked-value (make-continuation)))
+        (setf (continuation-next prev-cont) cast
+              (node-prev cast) prev-cont)
+        (use-continuation cast checked-value)
+        (link-node-to-previous-continuation dest checked-value)
+        (substitute-continuation checked-value cont)
+        (setf (continuation-dest cont) cast)
+        (reoptimize-continuation cont)))))
 
 ;;; Assert that CALL is to a function of the specified TYPE. It is
 ;;; assumed that the call is legal and has only constants in the
       (t
        (loop
           (let ((succ (block-succ block)))
-            (unless (and succ (null (rest succ)))
+            (unless (singleton-p succ)
               (return)))
 
           (let ((last (block-last block)))
             (typecase last
               (cif
-               (if (memq (continuation-type-check (if-test last))
-                         '(nil :deleted))
-                   ;; FIXME: Remove the test above when the bug 203
-                   ;; will be fixed.
-                   (progn
-                     (flush-dest (if-test last))
-                     (when (unlink-node last)
-                       (return)))
-                   (return)))
+               (flush-dest (if-test last))
+               (when (unlink-node last)
+                 (return)))
               (exit
                (when (maybe-delete-exit last)
                  (return)))))
          (aver (not (block-delete-p block)))
          (ir1-optimize-block block))
 
-       (cond ((block-delete-p block)
+       (cond ((and (block-delete-p block) (block-component block))
               (delete-block block))
              ((and (block-flush-p block) (block-component block))
               (flush-dead-code block))))))
           (when value
             (derive-node-type node (continuation-derived-type value)))))
        (cset
-        (ir1-optimize-set node)))))
+        (ir1-optimize-set node))
+        (cast
+         (ir1-optimize-cast node)))))
 
   (values))
 
 (defun join-successor-if-possible (block)
   (declare (type cblock block))
   (let ((next (first (block-succ block))))
-    (when (block-start next)
+    (when (block-start next) ; NEXT is not an END-OF-COMPONENT marker
       (let* ((last (block-last block))
             (last-cont (node-cont last))
             (next-cont (block-start next)))
                ;; The successor has more than one predecessor.
                (rest (block-pred next))
                ;; The last node's CONT is also used somewhere else.
+                ;; (as in (IF <cond> (M-V-PROG1 ...) (M-V-PROG1 ...)))
                (not (eq (continuation-use last-cont) last))
                ;; The successor is the current block (infinite loop).
                (eq next block)
                         (block-home-lambda next))))
               nil)
              ;; Joining is easy when the successor's START
-             ;; continuation is the same from our LAST's CONT. 
+             ;; continuation is the same from our LAST's CONT.
              ((eq last-cont next-cont)
               (join-blocks block next)
               t)
              ;; If they differ, then we can still join when the last
              ;; continuation has no next and the next continuation
-             ;; has no uses. 
+             ;; has no uses.
              ((and (null (block-start-uses next))
                    (eq (continuation-kind last-cont) :inside-block))
               ;; In this case, we replace the next
                 (setf (block-start next) last-cont)
                 (join-blocks block next))
               t)
+              ((and (null (block-start-uses next))
+                    (not (exit-p (continuation-dest last-cont)))
+                    (null (continuation-lexenv-uses last-cont)))
+               (assert (null (find-uses next-cont)))
+               (when (continuation-dest last-cont)
+                 (substitute-continuation next-cont last-cont))
+               (delete-continuation-use last)
+               (add-continuation-use last next-cont)
+               (setf (continuation-%derived-type next-cont) nil)
+               (join-blocks block next)
+               t)
              (t
               nil))))))
 
                          ;; functional args to determine if they have
                          ;; any side effects.
                           (if (policy node (= safety 3))
-                              (and (ir1-attributep attr flushable)
-                                   (every (lambda (arg)
-                                            ;; FIXME: when bug 203
-                                            ;; will be fixed, remove
-                                            ;; this check
-                                            (member (continuation-type-check arg)
-                                                    '(nil :deleted)))
-                                          (basic-combination-args node))
-                                   (valid-fun-use node
-                                                  (info :function :type
-                                                        (leaf-source-name (ref-leaf (continuation-use (basic-combination-fun node)))))
-                                                  :result-test #'always-subtypep
-                                                  :lossage-fun nil
-                                                  :unwinnage-fun nil))
+                              (ir1-attributep attr flushable)
                               (ir1-attributep attr unsafely-flushable)))
                  (flush-combination node))))))
        (mv-combination
             (flush-dest (set-value node))
             (setf (basic-var-sets var)
                   (delete node (basic-var-sets var)))
-            (unlink-node node)))))))
+            (unlink-node node))))
+        (cast
+         (unless (cast-type-check node)
+           (flush-dest (cast-value node))
+           (unlink-node node))))))
 
   (setf (block-flush-p block) nil)
   (values))
                   (return-from find-result-type (values)))))
              (t
               (use-union (node-derived-type use)))))
-      (let ((int (values-type-intersection
-                 (continuation-asserted-type result)
-                 (use-union))))
+      (let ((int
+             ;; (values-type-intersection
+             ;; (continuation-asserted-type result) ; FIXME -- APD, 2002-01-26
+             (use-union)
+              ;; )
+            ))
        (setf (return-result-type node) int))))
   (values))
 
          (convert-if-if use node)
          (when (continuation-use test) (return)))))
 
-    (when (memq (continuation-type-check test)
-                '(nil :deleted))
-      ;; FIXME: Remove the test above when the bug 203 will be fixed.
-      (let* ((type (continuation-type test))
-             (victim
-              (cond ((constant-continuation-p test)
-                     (if (continuation-value test)
-                         (if-alternative node)
-                         (if-consequent node)))
-                    ((not (types-equal-or-intersect type (specifier-type 'null)))
-                     (if-alternative node))
-                    ((type= type (specifier-type 'null))
-                     (if-consequent node)))))
-        (when victim
-          (flush-dest test)
-          (when (rest (block-succ block))
-            (unlink-blocks block victim))
-          (setf (component-reanalyze (node-component node)) t)
-          (unlink-node node)))))
+    (let* ((type (continuation-type test))
+           (victim
+            (cond ((constant-continuation-p test)
+                   (if (continuation-value test)
+                       (if-alternative node)
+                       (if-consequent node)))
+                  ((not (types-equal-or-intersect type (specifier-type 'null)))
+                   (if-alternative node))
+                  ((type= type (specifier-type 'null))
+                   (if-consequent node)))))
+      (when victim
+        (flush-dest test)
+        (when (rest (block-succ block))
+          (unlink-blocks block victim))
+        (setf (component-reanalyze (node-component node)) t)
+        (unlink-node node))))
   (values))
 
 ;;; Create a new copy of an IF node that tests the value of the node
           (new-block (continuation-starts-block new-cont)))
       (link-node-to-previous-continuation new-node new-cont)
       (setf (continuation-dest new-cont) new-node)
-      (setf (continuation-%externally-checkable-type new-cont) nil)
+      (flush-continuation-externally-checkable-type new-cont)
       (add-continuation-use new-node dummy-cont)
       (setf (block-last new-block) new-node)
 
   (declare (type exit node))
   (let ((value (exit-value node))
        (entry (exit-entry node))
-       (cont (node-cont node)))
+        (cont (node-cont node)))
     (when (and entry
               (eq (node-home-lambda node) (node-home-lambda entry)))
       (setf (entry-exits entry) (delete node (entry-exits entry)))
-      (prog1
-         (unlink-node node)
-       (when value
-         (collect ((merges))
-           (when (return-p (continuation-dest cont))
-             (do-uses (use value)
-               (when (and (basic-combination-p use)
-                          (eq (basic-combination-kind use) :local))
-                 (merges use))))
-           (substitute-continuation-uses cont value)
-           (dolist (merge (merges))
-             (merge-tail-sets merge))))))))
+      (if value
+          (delete-filter node cont value)
+          (unlink-node node)))))
+
 \f
 ;;;; combination IR1 optimization
 
         (when fun
           (let ((res (funcall fun node)))
             (when res
-              (derive-node-type node res)
+              (derive-node-type node (coerce-to-values res))
               (maybe-terminate-block node nil)))))
 
        (let ((fun (fun-info-optimizer kind)))
         (unless (and fun (funcall fun node))
           (dolist (x (fun-info-transforms kind))
-            #!+sb-show 
+            #!+sb-show
             (when *show-transforms-p*
               (let* ((cont (basic-combination-fun node))
                      (fname (continuation-fun-name cont t)))
 
   (values))
 
-;;; If CALL is to a function that doesn't return (i.e. return type is
-;;; NIL), then terminate the block there, and link it to the component
-;;; tail. We also change the call's CONT to be a dummy continuation to
-;;; prevent the use from confusing things.
+;;; If NODE doesn't return (i.e. return type is NIL), then terminate
+;;; the block there, and link it to the component tail. We also change
+;;; the NODE's CONT to be a dummy continuation to prevent the use from
+;;; confusing things.
 ;;;
 ;;; Except when called during IR1 [FIXME: What does this mean? Except
 ;;; during IR1 conversion? What about IR1 optimization?], we delete
 ;;; the continuation if it has no other uses. (If it does have other
 ;;; uses, we reoptimize.)
 ;;;
-;;; Termination on the basis of a continuation type assertion is
+;;; Termination on the basis of a continuation type is
 ;;; inhibited when:
 ;;; -- The continuation is deleted (hence the assertion is spurious), or
 ;;; -- We are in IR1 conversion (where THE assertions are subject to
 ;;;    weakening.)
-(defun maybe-terminate-block (call ir1-converting-not-optimizing-p)
-  (declare (type basic-combination call))
-  (let* ((block (node-block call))
-        (cont (node-cont call))
+(defun maybe-terminate-block (node ir1-converting-not-optimizing-p)
+  (declare (type (or basic-combination cast) node))
+  (let* ((block (node-block node))
+        (cont (node-cont node))
         (tail (component-tail (block-component block)))
         (succ (first (block-succ block))))
-    (unless (or (and (eq call (block-last block)) (eq succ tail))
+    (unless (or (and (eq node (block-last block)) (eq succ tail))
                (block-delete-p block))
-      (when (or (and (eq (continuation-asserted-type cont) *empty-type*)
-                    (not (or ir1-converting-not-optimizing-p
-                             (eq (continuation-kind cont) :deleted))))
-               (eq (node-derived-type call) *empty-type*))
+      (when (or (and (not (or ir1-converting-not-optimizing-p
+                             (eq (continuation-kind cont) :deleted)))
+                    (eq (continuation-derived-type cont) *empty-type*))
+               (eq (node-derived-type node) *empty-type*))
        (cond (ir1-converting-not-optimizing-p
-              (delete-continuation-use call)
+              (delete-continuation-use node)
               (cond
                ((block-last block)
-                (aver (and (eq (block-last block) call)
+                (aver (and (eq (block-last block) node)
                            (eq (continuation-kind cont) :block-start))))
                (t
-                (setf (block-last block) call)
+                (setf (block-last block) node)
                 (link-blocks block (continuation-starts-block cont)))))
              (t
-              (node-ends-block call)
-              (delete-continuation-use call)
+              (node-ends-block node)
+              (delete-continuation-use node)
               (if (eq (continuation-kind cont) :unused)
                   (delete-continuation cont)
                   (reoptimize-continuation cont))))
-       
+
        (unlink-blocks block (first (block-succ block)))
        (setf (component-reanalyze (block-component block)) t)
        (aver (not (block-succ block)))
        (link-blocks block tail)
-       (add-continuation-use call (make-continuation))
+       (add-continuation-use node (make-continuation))
        t))))
 
 ;;; This is called both by IR1 conversion and IR1 optimization when
                                               predicate)
                               (let ((dest (continuation-dest (node-cont call))))
                                 (and dest (not (if-p dest)))))))
-                ;; FIXME: This SYMBOLP is part of a literal
-                ;; translation of a test in the old CMU CL
-                ;; source, and it's not quite clear what
-                ;; the old source meant. Did it mean "has a
-                ;; valid name"? Or did it mean "is an
-                ;; ordinary function name, not a SETF
-                ;; function"? Either way, the old CMU CL
-                ;; code probably didn't deal with SETF
-                ;; functions correctly, and neither does
-                ;; this new SBCL code, and that should be fixed.
-               (when (symbolp (leaf-source-name leaf))
-                  (let ((dummies (make-gensym-list
-                                  (length (combination-args call)))))
-                    (transform-call call
-                                    `(lambda ,dummies
-                                      (,(leaf-source-name leaf)
-                                       ,@dummies))
-                                    (leaf-source-name leaf))))))))))
+               (let ((name (leaf-source-name leaf))
+                      (dummies (make-gensym-list
+                                (length (combination-args call)))))
+                  (transform-call call
+                                  `(lambda ,dummies
+                                     (,@(if (symbolp name)
+                                            `(,name)
+                                            `(funcall #',name))
+                                        ,@dummies))
+                                  (leaf-source-name leaf)))))))))
   (values))
 \f
 ;;;; known function optimization
                    (policy node (> speed inhibit-warnings))))
         (*compiler-error-context* node))
     (cond ((or (not constrained)
-              (valid-fun-use node type :strict-result t))
+              (valid-fun-use node type))
           (multiple-value-bind (severity args)
               (catch 'give-up-ir1-transform
                 (transform-call node
        (when (type/= int var-type)
          (setf (leaf-type leaf) int)
          (dolist (ref (leaf-refs leaf))
-           (derive-node-type ref int))))
+           (derive-node-type ref (make-single-value-type int))
+            (let* ((cont (node-cont ref))
+                   (dest (continuation-dest cont)))
+              ;; KLUDGE: LET var substitution
+              (when (combination-p dest)
+                (reoptimize-continuation cont))))))
       (values))))
 
 ;;; Figure out the type of a LET variable that has sets. We compute
       (let ((type (continuation-type (set-value set))))
         (res type)
         (when (node-reoptimize set)
-          (derive-node-type set type)
+          (derive-node-type set (make-single-value-type type))
           (setf (node-reoptimize set) nil))))
     (propagate-to-refs var (res)))
   (values))
            (setf (continuation-reoptimize iv) nil)
            (propagate-from-sets var (continuation-type iv)))))))
 
-  (derive-node-type node (continuation-type (set-value node)))
+  (derive-node-type node (make-single-value-type
+                          (continuation-type (set-value node))))
   (values))
 
 ;;; Return true if the value of REF will always be the same (and is
 ;;; replace the variable reference's CONT with the arg continuation.
 ;;; This is inhibited when:
 ;;; -- CONT has other uses, or
-;;; -- CONT receives multiple values, or
 ;;; -- the reference is in a different environment from the variable, or
-;;; -- either continuation has a funky TYPE-CHECK annotation.
-;;; -- the continuations have incompatible assertions, so the new asserted type
-;;;    would be NIL.
-;;; -- the VAR's DEST has a different policy than the ARG's (think safety).
+;;; -- CONT carries unknown number of values, or
+;;; -- DEST is return or exit, or
+;;; -- DEST is sensitive to the number of values and ARG return non-one value.
 ;;;
 ;;; We change the REF to be a reference to NIL with unused value, and
 ;;; let it be flushed as dead code. A side effect of this substitution
   (declare (type continuation arg) (type lambda-var var))
   (let* ((ref (first (leaf-refs var)))
         (cont (node-cont ref))
-        (cont-atype (continuation-asserted-type cont))
-         (cont-ctype (continuation-type-to-check cont))
         (dest (continuation-dest cont)))
     (when (and (eq (continuation-use cont) ref)
               dest
-              (continuation-single-value-p cont)
+               (typecase dest
+                 (cast
+                  (and (type-single-value-p (continuation-derived-type arg))
+                       (multiple-value-bind (pdest pprev)
+                           (principal-continuation-end cont)
+                         (declare (ignore pdest))
+                         (continuation-single-value-p pprev))))
+                 (mv-combination
+                  (or (eq (basic-combination-fun dest) cont)
+                      (and (eq (basic-combination-kind dest) :local)
+                           (type-single-value-p (continuation-derived-type arg)))))
+                 ((or creturn exit)
+                  nil)
+                 (t
+                  ;; (AVER (CONTINUATION-SINGLE-VALUE-P CONT))
+                  t))
               (eq (node-home-lambda ref)
-                  (lambda-home (lambda-var-home var)))
-              (member (continuation-type-check arg) '(t nil))
-              (member (continuation-type-check cont) '(t nil))
-              (not (eq (values-type-intersection
-                        cont-atype
-                        (continuation-asserted-type arg))
-                       *empty-type*))
-              (eq (lexenv-policy (node-lexenv dest))
-                  (lexenv-policy (node-lexenv (continuation-dest arg)))))
+                  (lambda-home (lambda-var-home var))))
       (aver (member (continuation-kind arg)
                    '(:block-start :deleted-block-start :inside-block)))
-      (set-continuation-type-assertion arg cont-atype cont-ctype)
       (setf (node-derived-type ref) *wild-type*)
       (change-ref-leaf ref (find-constant nil))
       (substitute-continuation arg cont)
 ;;; derived-type information for the arg to all the VAR's refs.
 ;;;
 ;;; Substitution is inhibited when the arg leaf's derived type isn't a
-;;; subtype of the argument's asserted type. This prevents type
-;;; checking from being defeated, and also ensures that the best
-;;; representation for the variable can be used.
+;;; subtype of the argument's leaf type. This prevents type checking
+;;; from being defeated, and also ensures that the best representation
+;;; for the variable can be used.
 ;;;
 ;;; Substitution of individual references is inhibited if the
 ;;; reference is in a different component from the home. This can only
          (when (ref-p use)
            (let ((leaf (ref-leaf use)))
              (when (and (constant-reference-p use)
-                        (values-subtypep (leaf-type leaf)
-                                         (continuation-asserted-type arg)))
+                         (csubtypep (leaf-type leaf)
+                                    ;; (NODE-DERIVED-TYPE USE) would
+                                    ;; be better -- APD, 2003-05-15
+                                    (leaf-type var)))
                (propagate-to-refs var (continuation-type arg))
                (let ((use-component (node-component use)))
                  (substitute-leaf-if
                   leaf var))
                t)))))
        ((and (null (rest (leaf-refs var)))
-            (substitute-single-use-continuation arg var)))
+             (substitute-single-use-continuation arg var)))
        (t
        (propagate-to-refs var (continuation-type arg))))))
 
-  (when (every #'null (combination-args call))
+  (when (every #'not (combination-args call))
     (delete-let fun))
 
   (values))
                    (propagate-from-sets var type)
                    (propagate-to-refs var type)))
              vars
-               (append types
-                       (make-list (max (- (length vars) nvals) 0)
-                                  :initial-element (specifier-type 'null))))))
+              (adjust-list types
+                           (length vars)
+                           (specifier-type 'null)))))
     (setf (continuation-reoptimize arg) nil))
   (values))
 
        (args (basic-combination-args node)))
 
     (unless (and (ref-p ref) (constant-reference-p ref)
-                args (null (rest args)))
+                (singleton-p args))
       (return-from ir1-optimize-mv-call))
 
     (multiple-value-bind (min max)
        (let ((fun-cont (basic-combination-fun call)))
          (setf (continuation-dest fun-cont) use)
           (setf (combination-fun use) fun-cont)
-         (setf (continuation-%externally-checkable-type fun-cont) nil))
+         (flush-continuation-externally-checkable-type fun-cont))
        (setf (combination-kind use) :local)
        (setf (functional-kind fun) :let)
        (flush-dest (first (basic-combination-args call)))
       (let ((args (combination-args use)))
        (dolist (arg args)
          (setf (continuation-dest arg) node)
-          (setf (continuation-%externally-checkable-type arg) nil))
+          (flush-continuation-externally-checkable-type arg))
        (setf (combination-args use) nil)
        (flush-dest list)
        (setf (combination-args node) args))
           (declare (ignore ,@dummies))
           val))
       nil))
+
+;;; TODO:
+;;; - CAST chains;
+(defun ir1-optimize-cast (cast &optional do-not-optimize)
+  (declare (type cast cast))
+  (let* ((value (cast-value cast))
+         (value-type (continuation-derived-type value))
+         (atype (cast-asserted-type cast))
+         (int (values-type-intersection value-type atype)))
+    (derive-node-type cast int)
+    (when (eq int *empty-type*)
+      (unless (eq value-type *empty-type*)
+
+        ;; FIXME: Do it in one step.
+        (filter-continuation
+         value
+         `(multiple-value-call #'list 'dummy))
+        (filter-continuation
+         value
+         ;; FIXME: Derived type.
+         `(%compile-time-type-error 'dummy
+                                    ',(type-specifier (coerce-to-values atype))
+                                    ',(type-specifier value-type)))
+        ;; KLUDGE: FILTER-CONTINUATION does not work for
+        ;; non-returning functions, so we declare the return type of
+        ;; %COMPILE-TIME-TYPE-ERROR to be * and derive the real type
+        ;; here.
+        (derive-node-type (continuation-use value) *empty-type*)
+        (maybe-terminate-block (continuation-use value) nil)
+        ;; FIXME: Is it necessary?
+        (aver (null (block-pred (node-block cast))))
+        (setf (block-delete-p (node-block cast)) t)
+        (return-from ir1-optimize-cast)))
+    (when (eq (node-derived-type cast) *empty-type*)
+      (maybe-terminate-block cast nil))
+
+    (flet ((delete-cast ()
+             (let ((cont (node-cont cast)))
+               (delete-filter cast cont value)
+               (reoptimize-continuation cont)
+               (when (continuation-single-value-p cont)
+                 (note-single-valuified-continuation cont))
+               (when (not (continuation-dest cont))
+                 (reoptimize-continuation-uses cont)))))
+      (cond
+        ((and (not do-not-optimize)
+              (values-subtypep value-type
+                               (cast-asserted-type cast)))
+         (delete-cast)
+         (return-from ir1-optimize-cast t))
+        ((and (cast-%type-check cast)
+              (values-subtypep value-type
+                               (cast-type-to-check cast)))
+         (setf (cast-%type-check cast) nil)))))
+
+  (unless do-not-optimize
+    (setf (node-reoptimize cast) nil)))