0.8.9.6.netbsd.2:
[sbcl.git] / src / compiler / checkgen.lisp
index 8b43ae3..2f9f907 100644 (file)
 ;;; templates in the VM definition.
 (defun type-test-cost (type)
   (declare (type ctype type))
 ;;; templates in the VM definition.
 (defun type-test-cost (type)
   (declare (type ctype type))
-  (or (let ((check (type-check-template type)))
+  (or (when (eq type *universal-type*)
+        0)
+      (when (eq type *empty-type*)
+        0)
+      (let ((check (type-check-template type)))
        (if check
            (template-cost check)
            (let ((found (cdr (assoc type *backend-type-predicates*
        (if check
            (template-cost check)
            (let ((found (cdr (assoc type *backend-type-predicates*
 (defun weaken-values-type (type)
   (declare (type ctype type))
   (cond ((eq type *wild-type*) type)
 (defun weaken-values-type (type)
   (declare (type ctype type))
   (cond ((eq type *wild-type*) type)
-        ((values-type-p type)
+        ((not (values-type-p type))
+         (weaken-type type))
+        (t
          (make-values-type :required (mapcar #'weaken-type
                                              (values-type-required type))
                            :optional (mapcar #'weaken-type
                                              (values-type-optional type))
                            :rest (acond ((values-type-rest type)
          (make-values-type :required (mapcar #'weaken-type
                                              (values-type-required type))
                            :optional (mapcar #'weaken-type
                                              (values-type-optional type))
                            :rest (acond ((values-type-rest type)
-                                         (weaken-type it))
-                                        ((values-type-keyp type)
-                                         *universal-type*))))
-        (t (weaken-type type))))
+                                         (weaken-type it)))))))
 \f
 ;;;; checking strategy determination
 
 ;;; Return the type we should test for when we really want to check
 \f
 ;;;; checking strategy determination
 
 ;;; Return the type we should test for when we really want to check
-;;; for TYPE. If speed, space or compilation speed is more important
-;;; than safety, then we return a weaker type if it is easier to
-;;; check. First we try the defined type weakenings, then look for any
-;;; predicate that is cheaper.
+;;; for TYPE. If type checking policy is "fast", then we return a
+;;; weaker type if it is easier to check. First we try the defined
+;;; type weakenings, then look for any predicate that is cheaper.
 (defun maybe-weaken-check (type policy)
   (declare (type ctype type))
 (defun maybe-weaken-check (type policy)
   (declare (type ctype type))
-  (cond ((policy policy (zerop safety))
-         *wild-type*)
-        ((policy policy
-                (and (<= speed safety)
-                     (<= space safety)
-                     (<= compilation-speed safety)))
-        type)
-       (t
-        (weaken-values-type type))))
+  (ecase (policy policy type-check)
+    (0 *wild-type*)
+    (2 (weaken-values-type type))
+    (3 type)))
 
 ;;; This is like VALUES-TYPES, only we mash any complex function types
 ;;; to FUNCTION.
 
 ;;; This is like VALUES-TYPES, only we mash any complex function types
 ;;; to FUNCTION.
 ;;; Switch to disable check complementing, for evaluation.
 (defvar *complement-type-checks* t)
 
 ;;; Switch to disable check complementing, for evaluation.
 (defvar *complement-type-checks* t)
 
-;;; CONT is a continuation we are doing a type check on and TYPES is a
-;;; list of types that we are checking its values against. If we have
-;;; proven that CONT generates a fixed number of values, then for each
+;;; LVAR is an lvar we are doing a type check on and TYPES is a list
+;;; of types that we are checking its values against. If we have
+;;; proven that LVAR generates a fixed number of values, then for each
 ;;; value, we check whether it is cheaper to then difference between
 ;;; the proven type and the corresponding type in TYPES. If so, we opt
 ;;; for a :HAIRY check with that test negated. Otherwise, we try to do
 ;;; value, we check whether it is cheaper to then difference between
 ;;; the proven type and the corresponding type in TYPES. If so, we opt
 ;;; for a :HAIRY check with that test negated. Otherwise, we try to do
 ;;; FIXME: I don't quite understand this, but it looks as though
 ;;; that means type checks are weakened when SPEED=3 regardless of
 ;;; the SAFETY level, which is not the right thing to do.
 ;;; FIXME: I don't quite understand this, but it looks as though
 ;;; that means type checks are weakened when SPEED=3 regardless of
 ;;; the SAFETY level, which is not the right thing to do.
-(defun maybe-negate-check (cont types original-types force-hairy)
-  (declare (type continuation cont) (list types))
-  (multiple-value-bind (ptypes count)
-      (no-fun-values-types (continuation-proven-type cont))
-    (if (eq count :unknown)
-        (if (and (every #'type-check-template types) (not force-hairy))
-            (values :simple types)
-            (values :hairy (mapcar (lambda (x) (list nil x x)) types)))
-        (let ((res (mapcar (lambda (p c a)
-                             (let ((diff (type-difference p c)))
-                               (if (and diff
-                                        (< (type-test-cost diff)
-                                           (type-test-cost c))
-                                        *complement-type-checks*)
-                                   (list t diff a)
-                                   (list nil c a))))
-                           ptypes types original-types)))
-          (cond ((or force-hairy (find-if #'first res))
-                 (values :hairy res))
-                ((every #'type-check-template types)
-                 (values :simple types))
-                (t
-                 (values :hairy res)))))))
-
-;;; Determines whether CONT's assertion is:
+(defun maybe-negate-check (lvar types original-types force-hairy n-required)
+  (declare (type lvar lvar) (list types original-types))
+  (let ((ptypes (values-type-out (lvar-derived-type lvar) (length types))))
+    (multiple-value-bind (hairy-res simple-res)
+        (loop for p in ptypes
+              and c in types
+              and a in original-types
+              and i from 0
+              for cc = (if (>= i n-required)
+                           (type-union c (specifier-type 'null))
+                           c)
+              for diff = (type-difference p cc)
+              collect (if (and diff
+                               (< (type-test-cost diff)
+                                  (type-test-cost cc))
+                               *complement-type-checks*)
+                          (list t diff a)
+                          (list nil cc a))
+              into hairy-res
+              collect cc into simple-res
+              finally (return (values hairy-res simple-res)))
+      (cond ((or force-hairy (find-if #'first hairy-res))
+             (values :hairy hairy-res))
+            ((every #'type-check-template simple-res)
+             (values :simple simple-res))
+            (t
+             (values :hairy hairy-res))))))
+
+;;; Determines whether CAST's assertion is:
 ;;;  -- checkable by the back end (:SIMPLE), or
 ;;;  -- not checkable by the back end, but checkable via an explicit 
 ;;;     test in type check conversion (:HAIRY), or
 ;;;  -- not reasonably checkable at all (:TOO-HAIRY).
 ;;;
 ;;;  -- checkable by the back end (:SIMPLE), or
 ;;;  -- not checkable by the back end, but checkable via an explicit 
 ;;;     test in type check conversion (:HAIRY), or
 ;;;  -- not reasonably checkable at all (:TOO-HAIRY).
 ;;;
-;;; A type is checkable if it either represents a fixed number of
-;;; values (as determined by VALUES-TYPES), or it is the assertion for
-;;; an MV-BIND. A type is simply checkable if all the type assertions
-;;; have a TYPE-CHECK-TEMPLATE. In this :SIMPLE case, the second value
-;;; is a list of the type restrictions specified for the leading
-;;; positional values.
+;;; We may check only fixed number of values; in any case the number
+;;; of generated values is trusted. If we know the number of produced
+;;; values, all of them are checked; otherwise if we know the number
+;;; of consumed -- only they are checked; otherwise the check is not
+;;; performed.
+;;;
+;;; A type is simply checkable if all the type assertions have a
+;;; TYPE-CHECK-TEMPLATE. In this :SIMPLE case, the second value is a
+;;; list of the type restrictions specified for the leading positional
+;;; values.
 ;;;
 ;;;
-;;; We force a check to be hairy even when there are fixed values if
-;;; we are in a context where we may be forced to use the unknown
-;;; values convention anyway. This is because IR2tran can't generate
-;;; type checks for unknown values continuations but people could
-;;; still be depending on the check being done. We only care about
-;;; EXIT and RETURN (not MV-COMBINATION) since these are the only
-;;; contexts where the ultimate values receiver
+;;; Old comment:
+;;;
+;;;    We force a check to be hairy even when there are fixed values
+;;;    if we are in a context where we may be forced to use the
+;;;    unknown values convention anyway. This is because IR2tran can't
+;;;    generate type checks for unknown values lvars but people could
+;;;    still be depending on the check being done. We only care about
+;;;    EXIT and RETURN (not MV-COMBINATION) since these are the only
+;;;    contexts where the ultimate values receiver
 ;;;
 ;;; In the :HAIRY case, the second value is a list of triples of
 ;;; the form:
 ;;;
 ;;; In the :HAIRY case, the second value is a list of triples of
 ;;; the form:
 ;;;
 ;;; If true, the NOT-P flag indicates a test that the corresponding
 ;;; value is *not* of the specified TYPE. ORIGINAL-TYPE is the type
 ;;;
 ;;; If true, the NOT-P flag indicates a test that the corresponding
 ;;; value is *not* of the specified TYPE. ORIGINAL-TYPE is the type
-;;; asserted on this value in the continuation, for use in error
+;;; asserted on this value in the lvar, for use in error
 ;;; messages. When NOT-P is true, this will be different from TYPE.
 ;;;
 ;;; messages. When NOT-P is true, this will be different from TYPE.
 ;;;
-;;; This allows us to take what has been proven about CONT's type into
-;;; consideration. If it is cheaper to test for the difference between
-;;; the derived type and the asserted type, then we check for the
-;;; negation of this type instead.
-(defun continuation-check-types (cont force-hairy)
-  (declare (type continuation cont))
-  (let ((ctype (continuation-type-to-check cont))
-        (atype (continuation-asserted-type cont))
-       (dest (continuation-dest cont)))
+;;; This allows us to take what has been proven about CAST's argument
+;;; type into consideration. If it is cheaper to test for the
+;;; difference between the derived type and the asserted type, then we
+;;; check for the negation of this type instead.
+(defun cast-check-types (cast force-hairy)
+  (declare (type cast cast))
+  (let* ((ctype (coerce-to-values (cast-type-to-check cast)))
+         (atype (coerce-to-values (cast-asserted-type cast)))
+         (dtype (node-derived-type cast))
+         (value (cast-value cast))
+         (lvar (node-lvar cast))
+         (dest (and lvar (lvar-dest lvar)))
+         (n-consumed (cond ((not lvar)
+                            nil)
+                           ((lvar-single-value-p lvar)
+                            1)
+                           ((and (mv-combination-p dest)
+                                 (eq (mv-combination-kind dest) :local))
+                            (let ((fun-ref (lvar-use (mv-combination-fun dest))))
+                              (length (lambda-vars (ref-leaf fun-ref)))))))
+         (n-required (length (values-type-required dtype))))
     (aver (not (eq ctype *wild-type*)))
     (aver (not (eq ctype *wild-type*)))
-    (multiple-value-bind (ctypes count) (no-fun-values-types ctype)
-      (multiple-value-bind (atypes acount) (no-fun-values-types ctype)
-        (aver (eq count acount))
-        (cond ((not (eq count :unknown))
-               (if (or (exit-p dest)
-                       (and (return-p dest)
-                            (multiple-value-bind (ignore count)
-                                (values-types (return-result-type dest))
-                              (declare (ignore ignore))
-                              (eq count :unknown))))
-                   (maybe-negate-check cont ctypes atypes t)
-                   (maybe-negate-check cont ctypes atypes force-hairy)))
-              ((and (mv-combination-p dest)
-                    (eq (basic-combination-kind dest) :local))
-               (aver (values-type-p ctype))
-               (maybe-negate-check cont
-                                   (args-type-optional ctype)
-                                   (args-type-optional atype)
-                                   force-hairy))
-              (t
-               (values :too-hairy nil)))))))
+    (cond ((and (null (values-type-optional dtype))
+                (not (values-type-rest dtype)))
+           ;; we [almost] know how many values are produced
+           (maybe-negate-check value
+                               (values-type-out ctype n-required)
+                               (values-type-out atype n-required)
+                               ;; backend checks only consumed values
+                               (not (eql n-required n-consumed))
+                               n-required))
+          ((lvar-single-value-p lvar)
+           ;; exactly one value is consumed
+           (principal-lvar-single-valuify lvar)
+           (let ((creq (car (args-type-required ctype))))
+             (multiple-value-setq (ctype atype)
+               (if creq
+                   (values creq (car (args-type-required atype)))
+                   (values (car (args-type-optional ctype))
+                           (car (args-type-optional atype)))))
+             (maybe-negate-check value
+                                 (list ctype) (list atype)
+                                 force-hairy
+                                 n-required)))
+          ((and (mv-combination-p dest)
+                (eq (mv-combination-kind dest) :local))
+           ;; we know the number of consumed values
+           (maybe-negate-check value
+                               (adjust-list (values-type-types ctype)
+                                            n-consumed
+                                            *universal-type*)
+                               (adjust-list (values-type-types atype)
+                                            n-consumed
+                                            *universal-type*)
+                               force-hairy
+                               n-required))
+          (t
+           (values :too-hairy nil)))))
 
 ;;; Do we want to do a type check?
 
 ;;; Do we want to do a type check?
-(defun worth-type-check-p (cont)
-  (let ((dest (continuation-dest cont)))
-    (not (or (values-subtypep (continuation-proven-type cont)
-                              (continuation-type-to-check cont))
-             (and (combination-p dest)
-                  (eq (combination-kind dest) :full)
-                  ;; The theory is that the type assertion is from a
-                  ;; declaration in (or on) the callee, so the callee
-                  ;; should be able to do the check. We want to let
-                  ;; the callee do the check, because it is possible
-                  ;; that by the time of call that declaration will be
-                  ;; changed and we do not want to make people
-                  ;; recompile all calls to a function when they were
-                  ;; originally compiled with a bad declaration. (See
-                  ;; also bug 35.)
-                  (values-subtypep (continuation-externally-checkable-type cont)
-                                   (continuation-type-to-check cont)))
-             (and (mv-combination-p dest) ; bug 220
-                  (eq (mv-combination-kind dest) :full))))))
-
-;;; Return true if CONT is a continuation whose type the back end is
+(defun cast-externally-checkable-p (cast)
+  (declare (type cast cast))
+  (let* ((lvar (node-lvar cast))
+         (dest (and lvar (lvar-dest lvar))))
+    (and (combination-p dest)
+         ;; The theory is that the type assertion is from a
+         ;; declaration in (or on) the callee, so the callee should be
+         ;; able to do the check. We want to let the callee do the
+         ;; check, because it is possible that by the time of call
+         ;; that declaration will be changed and we do not want to
+         ;; make people recompile all calls to a function when they
+         ;; were originally compiled with a bad declaration. (See also
+         ;; bug 35.)
+         (or (immediately-used-p lvar cast)
+             (binding* ((ctran (node-next cast) :exit-if-null)
+                        (next (ctran-next ctran)))
+               (and (cast-p next)
+                    (eq (node-dest next) dest)
+                    (eq (cast-type-check next) :external))))
+         (values-subtypep (lvar-externally-checkable-type lvar)
+                          (cast-type-to-check cast)))))
+
+;;; Return true if CAST's value is an lvar whose type the back end is
 ;;; likely to want to check. Since we don't know what template the
 ;;; back end is going to choose to implement the continuation's DEST,
 ;;; we use a heuristic. We always return T unless:
 ;;;  -- nobody uses the value, or
 ;;;  -- safety is totally unimportant, or
 ;;; likely to want to check. Since we don't know what template the
 ;;; back end is going to choose to implement the continuation's DEST,
 ;;; we use a heuristic. We always return T unless:
 ;;;  -- nobody uses the value, or
 ;;;  -- safety is totally unimportant, or
-;;;  -- the continuation is an argument to an unknown function, or
-;;;  -- the continuation is an argument to a known function that has
+;;;  -- the lvar is an argument to an unknown function, or
+;;;  -- the lvar is an argument to a known function that has
 ;;;     no IR2-CONVERT method or :FAST-SAFE templates that are
 ;;;     compatible with the call's type.
 ;;;     no IR2-CONVERT method or :FAST-SAFE templates that are
 ;;;     compatible with the call's type.
-;;;
-;;; We must only return NIL when it is *certain* that a check will not
-;;; be done, since if we pass up this chance to do the check, it will
-;;; be too late. The penalty for being too conservative is duplicated
-;;; type checks. The penalty for erring by being too speculative is
-;;; much nastier, e.g. falling through without ever being able to find
-;;; an appropriate VOP.
-(defun probable-type-check-p (cont)
-  (declare (type continuation cont))
-  (let ((dest (continuation-dest cont)))
+(defun probable-type-check-p (cast)
+  (declare (type cast cast))
+  (let* ((lvar (node-lvar cast))
+         (dest (and lvar (lvar-dest lvar))))
+    (cond ((not dest) nil)
+          (t t))
+    #+nil
     (cond ((or (not dest)
               (policy dest (zerop safety)))
           nil)
     (cond ((or (not dest)
               (policy dest (zerop safety)))
           nil)
                          (when (or val (not win)) (return t)))))))))
          (t t))))
 
                          (when (or val (not win)) (return t)))))))))
          (t t))))
 
-;;; Return a form that we can convert to do a hairy type check of the
-;;; specified TYPES. TYPES is a list of the format returned by
-;;; CONTINUATION-CHECK-TYPES in the :HAIRY case. In place of the
-;;; actual value(s) we are to check, we use 'DUMMY. This constant
-;;; reference is later replaced with the actual values continuation.
+;;; Return a lambda form that we can convert to do a hairy type check
+;;; of the specified TYPES. TYPES is a list of the format returned by
+;;; LVAR-CHECK-TYPES in the :HAIRY case.
 ;;;
 ;;; Note that we don't attempt to check for required values being
 ;;; unsupplied. Such checking is impossible to efficiently do at the
 ;;; source level because our fixed-values conventions are optimized
 ;;; for the common MV-BIND case.
 ;;;
 ;;; Note that we don't attempt to check for required values being
 ;;; unsupplied. Such checking is impossible to efficiently do at the
 ;;; source level because our fixed-values conventions are optimized
 ;;; for the common MV-BIND case.
-;;;
-;;; We can always use MULTIPLE-VALUE-BIND, since the macro is clever
-;;; about binding a single variable.
 (defun make-type-check-form (types)
   (let ((temps (make-gensym-list (length types))))
 (defun make-type-check-form (types)
   (let ((temps (make-gensym-list (length types))))
-    `(multiple-value-bind ,temps 'dummy
+    `(multiple-value-bind ,temps
+         'dummy
        ,@(mapcar (lambda (temp type)
        ,@(mapcar (lambda (temp type)
-                  (let* ((spec
-                          (let ((*unparse-fun-type-simplify* t))
-                            (type-specifier (second type))))
-                         (test (if (first type) `(not ,spec) spec)))
-                    `(unless (typep ,temp ',test)
-                       (%type-check-error
-                        ,temp
-                        ',(type-specifier (third type))))))
-                temps
-                types)
+                   (let* ((spec
+                           (let ((*unparse-fun-type-simplify* t))
+                             (type-specifier (second type))))
+                          (test (if (first type) `(not ,spec) spec)))
+                     `(unless (typep ,temp ',test)
+                        (%type-check-error
+                         ,temp
+                         ',(type-specifier (third type))))))
+                 temps
+                 types)
        (values ,@temps))))
 
        (values ,@temps))))
 
-;;; Splice in explicit type check code immediately before the node
-;;; which is CONT's DEST. This code receives the value(s) that were
-;;; being passed to CONT, checks the type(s) of the value(s), then
-;;; passes them on to CONT.
-(defun convert-type-check (cont types)
-  (declare (type continuation cont) (type list types))
-  (with-ir1-environment-from-node (continuation-dest cont)
-
-    ;; Ensuring that CONT starts a block lets us freely manipulate its uses.
-    (ensure-block-start cont)
-
-    ;; Make a new continuation and move CONT's uses to it.
-    (let* ((new-start (make-continuation))
-          (dest (continuation-dest cont))
-          (prev (node-prev dest)))
-      (continuation-starts-block new-start)
-      (substitute-continuation-uses new-start cont)
-
-      ;; Setting TYPE-CHECK in CONT to :DELETED indicates that the
-      ;; check has been done.
-      (setf (continuation-%type-check cont) :deleted)
-
-      ;; Make the DEST node start its block so that we can splice in
-      ;; the type check code.
-      (when (continuation-use prev)
-       (node-ends-block (continuation-use prev)))
-
-      (let* ((prev-block (continuation-block prev))
-            (new-block (continuation-block new-start))
-            (dummy (make-continuation)))
-
-       ;; Splice in the new block before DEST, giving the new block
-       ;; all of DEST's predecessors.
-       (dolist (block (block-pred prev-block))
-         (change-block-successor block prev-block new-block))
-
-       ;; Convert the check form, using the new block start as START
-       ;; and a dummy continuation as CONT.
-       (ir1-convert new-start dummy (make-type-check-form types))
-
-       ;; TO DO: Why should this be true? -- WHN 19990601
-       (aver (eq (continuation-block dummy) new-block))
-
-       ;; KLUDGE: Comments at the head of this function in CMU CL
-       ;; said that somewhere in here we
-       ;;   Set the new block's start and end cleanups to the *start*
-       ;;   cleanup of PREV's block. This overrides the incorrect
-       ;;   default from WITH-IR1-ENVIRONMENT-FROM-NODE.
-       ;; Unfortunately I can't find any code which corresponds to this.
-       ;; Perhaps it was a stale comment? Or perhaps I just don't
-       ;; understand.. -- WHN 19990521
-
-               (let ((node (continuation-use dummy)))
-         (setf (block-last new-block) node)
-         ;; Change the use to a use of CONT. (We need to use the
-         ;; dummy continuation to get the control transfer right,
-         ;; because we want to go to PREV's block, not CONT's.)
-         (delete-continuation-use node)
-         (add-continuation-use node cont))
-       ;; Link the new block to PREV's block.
-       (link-blocks new-block prev-block))
-
-      ;; MAKE-TYPE-CHECK-FORM generated a form which checked the type
-      ;; of 'DUMMY, not a real form. At this point we convert to the
-      ;; real form by finding 'DUMMY and overwriting it with the new
-      ;; continuation. (We can find 'DUMMY because no LET conversion
-      ;; has been done yet.) The [mv-]combination code from the
-      ;; mv-bind in the check form will be the use of the new check
-      ;; continuation. We substitute for the first argument of this
-      ;; node.
-      (let* ((node (continuation-use cont))
-            (args (basic-combination-args node))
-            (victim (first args)))
-       (aver (and (= (length args) 1)
-                    (eq (constant-value
-                         (ref-leaf
-                          (continuation-use victim)))
-                        'dummy)))
-       (substitute-continuation new-start victim)))
-
-    ;; Invoking local call analysis converts this call to a LET.
-    (locall-analyze-component *current-component*))
+;;; Splice in explicit type check code immediately before CAST. This
+;;; code receives the value(s) that were being passed to CAST-VALUE,
+;;; checks the type(s) of the value(s), then passes them further.
+(defun convert-type-check (cast types)
+  (declare (type cast cast) (type list types))
+  (let ((value (cast-value cast))
+        (length (length types)))
+    (filter-lvar value (make-type-check-form types))
+    (reoptimize-lvar (cast-value cast))
+    (setf (cast-type-to-check cast) *wild-type*)
+    (setf (cast-%type-check cast) nil)
+    (let* ((atype (cast-asserted-type cast))
+           (atype (cond ((not (values-type-p atype))
+                        atype)
+                       ((= length 1)
+                         (single-value-type atype))
+                        (t
+                        (make-values-type
+                          :required (values-type-out atype length)))))
+           (dtype (node-derived-type cast))
+           (dtype (make-values-type
+                   :required (values-type-out dtype length))))
+      (setf (cast-asserted-type cast) atype)
+      (setf (node-derived-type cast) dtype)))
 
   (values))
 
 
   (values))
 
-;;; Emit a type warning for NODE. If the value of NODE is being used
-;;; for a variable binding, we figure out which one for source
-;;; context. If the value is a constant, we print it specially. We
-;;; ignore nodes whose type is NIL, since they are supposed to never
-;;; return.
-(defun emit-type-warning (node)
-  (declare (type node node))
-  (let* ((*compiler-error-context* node)
-        (cont (node-cont node))
-        (atype-spec (type-specifier (continuation-asserted-type cont)))
-        (dtype (node-derived-type node))
-        (dest (continuation-dest cont))
-        (what (when (and (combination-p dest)
-                         (eq (combination-kind dest) :local))
-                (let ((lambda (combination-lambda dest))
-                      (pos (position-or-lose cont (combination-args dest))))
-                  (format nil "~:[A possible~;The~] binding of ~S"
-                          (and (continuation-use cont)
-                               (eq (functional-kind lambda) :let))
-                          (leaf-source-name (elt (lambda-vars lambda)
-                                                 pos)))))))
-    (cond ((eq dtype *empty-type*))
-         ((and (ref-p node) (constant-p (ref-leaf node)))
-          (compiler-warn "~:[This~;~:*~A~] is not a ~<~%~9T~:;~S:~>~%  ~S"
-                         what atype-spec (constant-value (ref-leaf node))))
-         (t
-          (compiler-warn
-           "~:[Result~;~:*~A~] is a ~S, ~<~%~9T~:;not a ~S.~>"
-           what (type-specifier dtype) atype-spec))))
+;;; Check all possible arguments of CAST and emit type warnings for
+;;; those with type errors. If the value of USE is being used for a
+;;; variable binding, we figure out which one for source context. If
+;;; the value is a constant, we print it specially.
+(defun cast-check-uses (cast)
+  (declare (type cast cast))
+  (let* ((lvar (node-lvar cast))
+         (dest (and lvar (lvar-dest lvar)))
+         (value (cast-value cast))
+         (atype (cast-asserted-type cast)))
+    (do-uses (use value)
+      (let ((dtype (node-derived-type use)))
+        (unless (values-types-equal-or-intersect dtype atype)
+          (let* ((*compiler-error-context* use)
+                 (atype-spec (type-specifier atype))
+                 (what (when (and (combination-p dest)
+                                  (eq (combination-kind dest) :local))
+                         (let ((lambda (combination-lambda dest))
+                               (pos (position-or-lose
+                                     lvar (combination-args dest))))
+                           (format nil "~:[A possible~;The~] binding of ~S"
+                                   (and (lvar-has-single-use-p lvar)
+                                        (eq (functional-kind lambda) :let))
+                                   (leaf-source-name (elt (lambda-vars lambda)
+                                                          pos)))))))
+            (cond ((and (ref-p use) (constant-p (ref-leaf use)))
+                   (compiler-warn "~:[This~;~:*~A~] is not a ~<~%~9T~:;~S:~>~%  ~S"
+                                  what atype-spec (constant-value (ref-leaf use))))
+                  (t
+                   (compiler-warn
+                    "~:[Result~;~:*~A~] is a ~S, ~<~%~9T~:;not a ~S.~>"
+                    what (type-specifier dtype) atype-spec))))))))
   (values))
 
 ;;; Loop over all blocks in COMPONENT that have TYPE-CHECK set,
   (values))
 
 ;;; Loop over all blocks in COMPONENT that have TYPE-CHECK set,
-;;; looking for continuations with TYPE-CHECK T. We do two mostly
-;;; unrelated things: detect compile-time type errors and determine if
-;;; and how to do run-time type checks.
+;;; looking for CASTs with TYPE-CHECK T. We do two mostly unrelated
+;;; things: detect compile-time type errors and determine if and how
+;;; to do run-time type checks.
 ;;;
 ;;;
-;;; If there is a compile-time type error, then we mark the
-;;; continuation and emit a warning if appropriate. This part loops
-;;; over all the uses of the continuation, since after we convert the
-;;; check, the :DELETED kind will inhibit warnings about the types of
-;;; other uses.
+;;; If there is a compile-time type error, then we mark the CAST and
+;;; emit a warning if appropriate. This part loops over all the uses
+;;; of the continuation, since after we convert the check, the
+;;; :DELETED kind will inhibit warnings about the types of other uses.
 ;;;
 ;;;
-;;; If a continuation is too complex to be checked by the back end, or
-;;; is better checked with explicit code, then convert to an explicit
+;;; If the cast is too complex to be checked by the back end, or is
+;;; better checked with explicit code, then convert to an explicit
 ;;; test. Assertions that can checked by the back end are passed
 ;;; through. Assertions that can't be tested are flamed about and
 ;;; marked as not needing to be checked.
 ;;; test. Assertions that can checked by the back end are passed
 ;;; through. Assertions that can't be tested are flamed about and
 ;;; marked as not needing to be checked.
 ;;; which may lead to inappropriate template choices due to the
 ;;; modification of argument types.
 (defun generate-type-checks (component)
 ;;; which may lead to inappropriate template choices due to the
 ;;; modification of argument types.
 (defun generate-type-checks (component)
-  (collect ((conts))
+  (collect ((casts))
     (do-blocks (block component)
       (when (block-type-check block)
     (do-blocks (block component)
       (when (block-type-check block)
-       (do-nodes (node cont block)
-         (let ((type-check (continuation-type-check cont)))
-           (unless (member type-check '(nil :deleted))
-             (let ((atype (continuation-asserted-type cont)))
-               (do-uses (use cont)
-                 (unless (values-types-equal-or-intersect
-                          (node-derived-type use) atype)
-                   (unless (policy node (= inhibit-warnings 3))
-                     (emit-type-warning use))))))
-           (when (eq type-check t)
-             (cond ((worth-type-check-p cont)
-                     (conts (cons cont (not (probable-type-check-p cont)))))
-                    ((probable-type-check-p cont)
-                     (setf (continuation-%type-check cont) :deleted))
-                    (t
-                     (setf (continuation-%type-check cont) :no-check))))))
+        ;; CAST-EXTERNALLY-CHECKABLE-P wants the backward pass
+       (do-nodes-backwards (node nil block)
+          (when (and (cast-p node)
+                     (cast-type-check node))
+            (cast-check-uses node)
+            (cond ((cast-externally-checkable-p node)
+                   (setf (cast-%type-check node) :external))
+                  (t
+                   ;; it is possible that NODE was marked :EXTERNAL by
+                   ;; the previous pass
+                   (setf (cast-%type-check node) t)
+                   (casts (cons node (not (probable-type-check-p node))))))))
        (setf (block-type-check block) nil)))
        (setf (block-type-check block) nil)))
-    (dolist (cont (conts))
-      (destructuring-bind (cont . force-hairy) cont
+    (dolist (cast (casts))
+      (destructuring-bind (cast . force-hairy) cast
         (multiple-value-bind (check types)
         (multiple-value-bind (check types)
-            (continuation-check-types cont force-hairy)
+            (cast-check-types cast force-hairy)
           (ecase check
             (:simple)
             (:hairy
           (ecase check
             (:simple)
             (:hairy
-             (convert-type-check cont types))
+             (convert-type-check cast types))
             (:too-hairy
             (:too-hairy
-             (let* ((context (continuation-dest cont))
-                    (*compiler-error-context* context))
-               (when (policy context (>= safety inhibit-warnings))
-                 (compiler-note
+             (let ((*compiler-error-context* cast))
+               (when (policy cast (>= safety inhibit-warnings))
+                 (compiler-notify
                   "type assertion too complex to check:~% ~S."
                   "type assertion too complex to check:~% ~S."
-                  (type-specifier (continuation-asserted-type cont)))))
-             (setf (continuation-%type-check cont) :deleted)))))))
+                  (type-specifier (coerce-to-values (cast-asserted-type cast))))))
+             (setf (cast-type-to-check cast) *wild-type*)
+             (setf (cast-%type-check cast) nil)))))))
   (values))
   (values))