Fix make-array transforms.
[sbcl.git] / tests / ctor.impure.lisp
index f7a6530..f40e27b 100644 (file)
 ;;;; more information.
 
 (load "test-util.lisp")
+(load "compiler-test-util.lisp")
 
 (defpackage "CTOR-TEST"
-  (:use "CL" "TEST-UTIL"))
+  (:use "CL" "TEST-UTIL" "COMPILER-TEST-UTIL"))
 
 (in-package "CTOR-TEST")
 \f
 (assert (= (slot-value (funcall #'(sb-pcl::ctor one-slot-subclass nil :b sb-pcl::\.p0.) 4) 'b) 4))
 
 ;;; Tests for CTOR optimization of non-constant class args and constant class object args
-(defun find-ctor-cache (f)
-  (let ((code (sb-kernel:fun-code-header f)))
-    (loop for i from sb-vm::code-constants-offset below (sb-kernel:get-header-data code)
-          for c = (sb-kernel:code-header-ref code i)
-          do (when (= sb-vm::value-cell-header-widetag (sb-kernel:widetag-of c))
-               (let ((c (sb-vm::value-cell-ref c)))
-                 (when (and (consp c) (eq 'sb-pcl::ctor-cache (car c)))
-                   (return c)))))))
+(defun find-ctor-caches (fun)
+  (remove-if-not (lambda (value)
+                   (and (consp value) (eq 'sb-pcl::ctor-cache (car value))))
+                 (find-value-cell-values fun)))
 
 (let* ((cmacro (compiler-macro-function 'make-instance))
         (opt 0)
               (assert (= 0 opt))
               (let ((f (compile nil `(lambda (class)
                                        (make-instance class :b t)))))
-                (assert (find-ctor-cache f))
+                (assert (= 1 (length (find-ctor-caches f))))
                 (assert (= 1 opt))
                 (assert (typep (funcall f 'one-slot-subclass) 'one-slot-subclass))))
             (with-test (:name (make-instance :constant-class-object))
               (let ((f (compile nil `(lambda ()
                                        (make-instance ,(find-class 'one-slot-subclass) :b t)))))
-                (assert (not (find-ctor-cache f)))
+                (assert (not (find-ctor-caches f)))
                 (assert (= 2 opt))
                 (assert (typep (funcall f) 'one-slot-subclass))))
             (with-test (:name (make-instance :constant-non-std-class-object))
               (let ((f (compile nil `(lambda ()
                                        (make-instance ,(find-class 'structure-object))))))
-                (assert (not (find-ctor-cache f)))
+                (assert (not (find-ctor-caches f)))
                 (assert (= 3 opt))
                 (assert (typep (funcall f) 'structure-object))))
             (with-test (:name (make-instance :constant-non-std-class-name))
               (let ((f (compile nil `(lambda ()
                                        (make-instance 'structure-object)))))
-                (assert (not (find-ctor-cache f)))
+                (assert (not (find-ctor-caches f)))
                 (assert (= 4 opt))
                 (assert (typep (funcall f) 'structure-object)))))
        (setf (compiler-macro-function 'make-instance) cmacro))))
          (classes (loop repeat (* 2 sb-pcl::+ctor-table-max-size+)
                         collect (class-name (eval `(defclass ,(gentemp) () ())))))
          (count 0)
-         (cache (find-ctor-cache f)))
+         (caches (find-ctor-caches f))
+         (cache (pop caches)))
     (assert cache)
+    (assert (not caches))
     (assert (not (cdr cache)))
     (dolist (class classes)
       (assert (typep (funcall f (if (oddp count) class (find-class class))) class))
     (dolist (class classes)
       (assert (typep (funcall f (if (oddp count) class (find-class class))) class))
       (incf count))))
+
+;;; Make sure we get default initargs right with on the FAST-MAKE-INSTANCE path CTORs
+(defclass some-class ()
+  ((aroundp :initform nil :reader aroundp))
+  (:default-initargs :x :success1))
+
+(defmethod shared-initialize :around ((some-class some-class) slots &key (x :fail?))
+  (unless (eq x :success1)
+    (error "Default initarg lossage"))
+  (setf (slot-value some-class 'aroundp) t)
+  (when (next-method-p)
+    (call-next-method)))
+
+(with-test (:name (make-instance :ctor-default-initargs-1))
+  (assert (aroundp (eval `(make-instance 'some-class))))
+  (let ((fun (compile nil `(lambda () (make-instance 'some-class)))))
+    (assert (aroundp (funcall fun)))
+    ;; make sure we tested what we think we tested...
+    (let ((ctors (find-named-callees fun :type 'sb-pcl::ctor)))
+      (assert ctors)
+      (assert (not (cdr ctors)))
+      (assert (find-named-callees (car ctors) :name 'sb-pcl::fast-make-instance)))))
+
+;;; Make sure we get default initargs right with on the FAST-MAKE-INSTANCE path CTORs
+;;; in more interesting cases as well...
+(defparameter *some-counter* 0)
+(let* ((x 'success2))
+  (defclass some-class2 ()
+    ((aroundp :initform nil :reader aroundp))
+    (:default-initargs :x (progn (incf *some-counter*) x))))
+
+(defmethod shared-initialize :around ((some-class some-class2) slots &key (x :fail2?))
+  (unless (eq x 'success2)
+    (error "Default initarg lossage"))
+  (setf (slot-value some-class 'aroundp) t)
+  (when (next-method-p)
+    (call-next-method)))
+
+(with-test (:name (make-instance :ctor-default-initargs-2))
+  (assert (= 0 *some-counter*))
+  (assert (aroundp (eval `(make-instance 'some-class2))))
+  (assert (= 1 *some-counter*))
+  (let ((fun (compile nil `(lambda () (make-instance 'some-class2)))))
+    (assert (= 1 *some-counter*))
+    (assert (aroundp (funcall fun)))
+    (assert (= 2 *some-counter*))
+    ;; make sure we tested what we think we tested...
+    (let ((ctors (find-named-callees fun :type 'sb-pcl::ctor)))
+      (assert ctors)
+      (assert (not (cdr ctors)))
+      (assert (find-named-callees (car ctors) :name 'sb-pcl::fast-make-instance)))))
+
+;;; No compiler notes, please
+(locally (declare (optimize safety))
+  (defclass type-check-thing ()
+    ((slot :type (integer 0) :initarg :slot))))
+(with-test (:name (make-instance :no-compile-note-at-runtime))
+  (let ((fun (compile nil `(lambda (x)
+                             (declare (optimize safety))
+                             (make-instance 'type-check-thing :slot x)))))
+    (handler-bind ((sb-ext:compiler-note #'error))
+      (funcall fun 41)
+      (funcall fun 13))))
+
+;;; NO-APPLICABLE-METHOD called
+(defmethod no-applicable-method ((gf (eql #'make-instance)) &rest args)
+  (cons :no-applicable-method args))
+(with-test (:name :constant-invalid-class-arg)
+  (assert (equal
+           '(:no-applicable-method "FOO" :quux 14)
+           (funcall (compile nil `(lambda (x) (make-instance "FOO" :quux x))) 14)))
+  (assert (equal
+           '(:no-applicable-method 'abc zot 1 bar 2)
+           (funcall (compile nil `(lambda (x y) (make-instance ''abc 'zot x 'bar y)))
+                    1 2))))
+(with-test (:name :variable-invalid-class-arg)
+  (assert (equal
+           '(:no-applicable-method "FOO" :quux 14)
+           (funcall (compile nil `(lambda (c x) (make-instance c :quux x))) "FOO" 14)))
+  (assert (equal
+           '(:no-applicable-method 'abc zot 1 bar 2)
+           (funcall (compile nil `(lambda (c x y) (make-instance c 'zot x 'bar y)))
+                    ''abc 1 2))))
+
+(defclass sneaky-class (standard-class)
+  ())
+
+(defmethod sb-mop:validate-superclass ((class sneaky-class) (super standard-class))
+  t)
+
+(defclass sneaky ()
+  ((dirty :initform nil :accessor dirty-slots)
+   (a :initarg :a :reader sneaky-a)
+   (b :initform "b" :reader sneaky-b)
+   (c :accessor sneaky-c))
+  (:metaclass sneaky-class))
+
+(defvar *supervising* nil)
+
+(defmethod (setf sb-mop:slot-value-using-class)
+    :before (value (class sneaky-class) (instance sneaky) slotd)
+  (unless *supervising*
+    (let ((name (sb-mop:slot-definition-name slotd))
+          (*supervising* t))
+      (when (slot-boundp instance 'dirty)
+        (pushnew name (dirty-slots instance))))))
+
+(with-test (:name (make-instance :setf-slot-value-using-class-hits-other-slots))
+  (let ((fun (compile nil `(lambda (a c)
+                             (let ((i (make-instance 'sneaky :a a)))
+                               (setf (sneaky-c i) c)
+                               i)))))
+    (loop repeat 3
+          do (let ((i (funcall fun "a" "c")))
+               (assert (equal '(c b a) (dirty-slots i)))
+               (assert (equal "a" (sneaky-a i)))
+               (assert (equal "b" (sneaky-b i)))
+               (assert (equal "c" (sneaky-c i)))))))
+
+(defclass bug-728650-base ()
+  ((value
+    :initarg :value
+    :initform nil)))
+
+(defmethod initialize-instance :after ((instance bug-728650-base) &key)
+  (with-slots (value) instance
+    (unless value
+      (error "Impossible! Value slot not initialized in ~S" instance))))
+
+(defclass bug-728650-child-1 (bug-728650-base)
+  ())
+
+(defmethod initialize-instance :around ((instance bug-728650-child-1) &rest initargs &key)
+  (apply #'call-next-method instance :value 'provided-by-child-1 initargs))
+
+(defclass bug-728650-child-2 (bug-728650-base)
+  ())
+
+(defmethod initialize-instance :around ((instance bug-728650-child-2) &rest initargs &key)
+  (let ((foo (make-instance 'bug-728650-child-1)))
+    (apply #'call-next-method instance :value foo initargs)))
+
+(with-test (:name :bug-728650)
+  (let ((child1 (slot-value (make-instance 'bug-728650-child-2) 'value)))
+    (assert (typep child1 'bug-728650-child-1))
+    (assert (eq 'provided-by-child-1 (slot-value child1 'value)))))
+
 \f
 ;;;; success