1.0.36.15: upgraded array element-type of unions and intersections
[sbcl.git] / src / compiler / array-tran.lisp
index 5aaf16a..91ca4c6 100644 (file)
         element-type-specifier)))
 
 (defun upgraded-element-type-specifier (lvar)
-  (type-specifier (extract-upgraded-element-type lvar)))
+  (type-specifier (array-type-upgraded-element-type (lvar-type lvar))))
 
 ;;; Array access functions return an object from the array, hence its type is
 ;;; going to be the array upgraded element type. Secondary return value is the
 ;;; known supertype of the upgraded-array-element-type, if if the exact
 ;;; U-A-E-T is not known. (If it is NIL, the primary return value is as good
 ;;; as it gets.)
-(defun extract-upgraded-element-type (array)
-  (let ((type (lvar-type array)))
-    (cond
-      ;; Note that this IF mightn't be satisfied even if the runtime
-      ;; value is known to be a subtype of some specialized ARRAY, because
-      ;; we can have values declared e.g. (AND SIMPLE-VECTOR UNKNOWN-TYPE),
-      ;; which are represented in the compiler as INTERSECTION-TYPE, not
-      ;; array type.
-      ((array-type-p type)
-       (values (array-type-specialized-element-type type) nil))
-      ;; fix for bug #396. This type logic corresponds to the special case for
-      ;; strings in HAIRY-DATA-VECTOR-REF (generic/vm-tran.lisp)
-      ((csubtypep type (specifier-type 'string))
-       (cond
-         ((csubtypep type (specifier-type '(array character (*))))
-          (values (specifier-type 'character) nil))
-         #!+sb-unicode
-         ((csubtypep type (specifier-type '(array base-char (*))))
-          (values (specifier-type 'base-char) nil))
-         ((csubtypep type (specifier-type '(array nil (*))))
-          (values *empty-type* nil))
-         (t
-          ;; See KLUDGE below.
-          (values *wild-type* (specifier-type 'character)))))
-      (t
-       ;; KLUDGE: there is no good answer here, but at least
-       ;; *wild-type* won't cause HAIRY-DATA-VECTOR-{REF,SET} to be
-       ;; erroneously optimized (see generic/vm-tran.lisp) -- CSR,
-       ;; 2002-08-21
-       (values *wild-type* nil)))))
+(defun array-type-upgraded-element-type (type)
+  (typecase type
+    ;; Note that this IF mightn't be satisfied even if the runtime
+    ;; value is known to be a subtype of some specialized ARRAY, because
+    ;; we can have values declared e.g. (AND SIMPLE-VECTOR UNKNOWN-TYPE),
+    ;; which are represented in the compiler as INTERSECTION-TYPE, not
+    ;; array type.
+    (array-type
+     (values (array-type-specialized-element-type type) nil))
+    ;; Deal with intersection types (bug #316078)
+    (intersection-type
+     (let ((intersection-types (intersection-type-types type))
+           (element-type *wild-type*)
+           (element-supertypes nil))
+       (dolist (intersection-type intersection-types)
+         (multiple-value-bind (cur-type cur-supertype)
+             (array-type-upgraded-element-type intersection-type)
+           ;; According to ANSI, an array may have only one specialized
+           ;; element type - e.g. '(and (array foo) (array bar))
+           ;; is not a valid type unless foo and bar upgrade to the
+           ;; same element type.
+           (cond
+             ((eq cur-type *wild-type*)
+              nil)
+             ((eq element-type *wild-type*)
+              (setf element-type cur-type))
+             ((or (not (csubtypep cur-type element-type))
+                  (not (csubtypep element-type cur-type)))
+              ;; At least two different element types where given, the array
+              ;; is valid iff they represent the same type.
+              ;;
+              ;; FIXME: TYPE-INTERSECTION already takes care of disjoint array
+              ;; types, so I believe this code should be unreachable. Maybe
+              ;; signal a warning / error instead?
+              (setf element-type *empty-type*)))
+           (push (or cur-supertype (type-*-to-t cur-type))
+                 element-supertypes)))
+       (values element-type
+               (when (and (eq *wild-type* element-type) element-supertypes)
+                 (apply #'type-intersection element-supertypes)))))
+    (union-type
+     (let ((union-types (union-type-types type))
+           (element-type *empty-type*)
+           (element-supertypes nil))
+       (dolist (union-type union-types)
+         (multiple-value-bind (cur-type cur-supertype)
+             (array-type-upgraded-element-type union-type)
+           (cond
+             ((eq element-type *wild-type*)
+              nil)
+             ((eq element-type *empty-type*)
+              (setf element-type cur-type))
+             ((or (eq cur-type *wild-type*)
+                  ;; If each of the two following tests fail, it is not
+                  ;; possible to determine the element-type of the array
+                  ;; because more than one kind of element-type was provided
+                  ;; like in '(or (array foo) (array bar)) although a
+                  ;; supertype (or foo bar) may be provided as the second
+                  ;; returned value returned. See also the KLUDGE below.
+                  (not (csubtypep cur-type element-type))
+                  (not (csubtypep element-type cur-type)))
+              (setf element-type *wild-type*)))
+           (push (or cur-supertype (type-*-to-t cur-type))
+                 element-supertypes)))
+       (values element-type
+               (when (eq *wild-type* element-type)
+                 (apply #'type-union element-supertypes)))))
+    (t
+     ;; KLUDGE: there is no good answer here, but at least
+     ;; *wild-type* won't cause HAIRY-DATA-VECTOR-{REF,SET} to be
+     ;; erroneously optimized (see generic/vm-tran.lisp) -- CSR,
+     ;; 2002-08-21
+     (values *wild-type* nil))))
 
-(defun extract-declared-element-type (array)
-  (let ((type (lvar-type array)))
-    (if (array-type-p type)
-        (array-type-element-type type)
-        *wild-type*)))
+(defun array-type-declared-element-type (type)
+  (if (array-type-p type)
+      (array-type-element-type type)
+      *wild-type*))
 
 ;;; The ``new-value'' for array setters must fit in the array, and the
 ;;; return type is going to be the same as the new-value for SETF
    (lexenv-policy (node-lexenv (lvar-dest array)))))
 
 (defun derive-aref-type (array)
-  (multiple-value-bind (uaet other) (extract-upgraded-element-type array)
+  (multiple-value-bind (uaet other)
+      (array-type-upgraded-element-type (lvar-type array))
     (or other uaet)))
 
 (defoptimizer (array-in-bounds-p derive-type) ((array &rest indices))
 ;;; maybe this is just too sloppy for actual type logic.  -- CSR,
 ;;; 2004-02-18
 (defun array-type-dimensions-or-give-up (type)
-  (typecase type
-    (array-type (array-type-dimensions type))
-    (union-type
-     (let ((types (union-type-types type)))
-       ;; there are at least two types, right?
-       (aver (> (length types) 1))
-       (let ((result (array-type-dimensions-or-give-up (car types))))
-         (dolist (type (cdr types) result)
-           (unless (equal (array-type-dimensions-or-give-up type) result)
-             (give-up-ir1-transform
-              "~@<dimensions of arrays in union type ~S do not match~:@>"
-              (type-specifier type)))))))
-    ;; FIXME: intersection type [e.g. (and (array * (*)) (satisfies foo)) ]
-    (t
-     (give-up-ir1-transform
-      "~@<don't know how to extract array dimensions from type ~S~:@>"
-      (type-specifier type)))))
+  (labels ((maybe-array-type-dimensions (type)
+             (typecase type
+               (array-type
+                (array-type-dimensions type))
+               (union-type
+                (let* ((types (remove nil (mapcar #'maybe-array-type-dimensions
+                                                  (union-type-types type))))
+                       (result (car types)))
+                  (dolist (other (cdr types) result)
+                    (unless (equal result other)
+                      (give-up-ir1-transform
+                       "~@<dimensions of arrays in union type ~S do not match~:@>"
+                       (type-specifier type))))))
+               (intersection-type
+                (let* ((types (remove nil (mapcar #'maybe-array-type-dimensions
+                                                  (intersection-type-types type))))
+                       (result (car types)))
+                  (dolist (other (cdr types) result)
+                    (unless (equal result other)
+                      (abort-ir1-transform
+                       "~@<dimensions of arrays in intersection type ~S do not match~:@>"
+                       (type-specifier type)))))))))
+    (or (maybe-array-type-dimensions type)
+        (give-up-ir1-transform
+         "~@<don't know how to extract array dimensions from type ~S~:@>"
+         (type-specifier type)))))
 
 (defun conservative-array-type-complexp (type)
   (typecase type
 ;; with sufficient precision, skip directly to DATA-VECTOR-REF.
 (deftransform aref ((array index) (t t) * :node node)
   (let* ((type (lvar-type array))
-         (element-ctype (extract-upgraded-element-type array)))
+         (element-ctype (array-type-upgraded-element-type type)))
     (cond
       ((and (array-type-p type)
             (null (array-type-complexp type))
             (not (eql element-ctype *wild-type*))
             (eql (length (array-type-dimensions type)) 1))
-       (let* ((declared-element-ctype (extract-declared-element-type array))
+       (let* ((declared-element-ctype (array-type-declared-element-type type))
               (bare-form
                `(data-vector-ref array
                  (%check-bound array (array-dimension array 0) index))))
 (macrolet ((define (name transform-to extra extra-type)
              (declare (ignore extra-type))
              `(deftransform ,name ((array index ,@extra))
-                (let ((type (lvar-type array))
-                      (element-type (extract-upgraded-element-type array))
-                      (declared-type (extract-declared-element-type array)))
+                (let* ((type (lvar-type array))
+                       (element-type (array-type-upgraded-element-type type))
+                       (declared-type (array-type-declared-element-type type)))
                   ;; If an element type has been declared, we want to
                   ;; use that information it for type checking (even
                   ;; if the access can't be optimized due to the array