Fix deadlocks in GC on Windows.
[sbcl.git] / tests / dynamic-extent.impure.lisp
index 93493d4..9b06fd7 100644 (file)
@@ -12,7 +12,7 @@
 ;;;; more information.
 
 (when (eq sb-ext:*evaluator-mode* :interpret)
-  (sb-ext:quit :unix-status 104))
+  (sb-ext:exit :code 104))
 
 (load "compiler-test-util.lisp")
 (use-package :ctu)
       sb-ext:*stack-allocate-dynamic-extent* t)
 
 (defmacro defun-with-dx (name arglist &body body)
-  `(defun ,name ,arglist
-     ,@body))
+  (let ((debug-name (sb-int:symbolicate name "-HIGH-DEBUG"))
+        (default-name (sb-int:symbolicate name "-DEFAULT")))
+    `(progn
+       (defun ,debug-name ,arglist
+         (declare (optimize debug))
+         ,@body)
+       (defun ,default-name ,arglist
+        ,@body)
+       (defun ,name (&rest args)
+         (apply #',debug-name args)
+         (apply #',default-name args)))))
 
 (declaim (notinline opaque-identity))
 (defun opaque-identity (x)
   (let ((v (make-array (min n 1))))
     (declare (sb-int:truly-dynamic-extent v))
     (true v)
+    (true v)
     nil))
 
 (defun-with-dx make-array-on-stack-1 ()
   (let ((v (make-array '(42) :element-type 'single-float)))
     (declare (dynamic-extent v))
     (true v)
+    (true v)
     nil))
 
 (defun-with-dx make-array-on-stack-2 (n x)
   (let ((v (make-array n :initial-contents x)))
     (declare (sb-int:truly-dynamic-extent v))
     (true v)
+    (true v)
     nil))
 
 (defun-with-dx make-array-on-stack-3 (x y z)
                        :element-type t :initial-contents x)))
     (declare (sb-int:truly-dynamic-extent v))
     (true v)
+    (true v)
     nil))
 
 (defun-with-dx make-array-on-stack-4 ()
   (let ((v (make-array 3 :initial-contents '(1 2 3))))
     (declare (sb-int:truly-dynamic-extent v))
     (true v)
+    (true v)
     nil))
 
 (defun-with-dx make-array-on-stack-5 ()
   (let ((v (make-array 3 :initial-element 12 :element-type t)))
     (declare (sb-int:truly-dynamic-extent v))
     (true v)
+    (true v)
+    nil))
+
+(defun-with-dx make-array-on-stack-6 ()
+  (let ((v (make-array 3 :initial-element 12 :element-type '(unsigned-byte 8))))
+    (declare (sb-int:truly-dynamic-extent v))
+    (true v)
+    (true v)
+    nil))
+
+(defun-with-dx make-array-on-stack-7 ()
+  (let ((v (make-array 3 :initial-element 12 :element-type '(signed-byte 8))))
+    (declare (sb-int:truly-dynamic-extent v))
+    (true v)
+    (true v)
+    nil))
+
+(defun-with-dx make-array-on-stack-8 ()
+  (let ((v (make-array 3 :initial-element 12 :element-type 'word)))
+    (declare (sb-int:truly-dynamic-extent v))
+    (true v)
+    (true v)
+    nil))
+
+(defun-with-dx make-array-on-stack-9 ()
+  (let ((v (make-array 3 :initial-element 12.0 :element-type 'single-float)))
+    (declare (sb-int:truly-dynamic-extent v))
+    (true v)
+    (true v)
+    nil))
+
+(defun-with-dx make-array-on-stack-10 ()
+  (let ((v (make-array 3 :initial-element 12.0d0 :element-type 'double-float)))
+    (declare (sb-int:truly-dynamic-extent v))
+    (true v)
+    (true v)
+    nil))
+
+(defun-with-dx make-array-on-stack-11 ()
+  (let ((v (make-array (the integer (opaque-identity 3)) :initial-element 12.0d0 :element-type 'double-float)))
+    (declare (sb-int:truly-dynamic-extent v))
+    (true v)
+    (true v)
     nil))
 
 (defun-with-dx vector-on-stack (x y)
     (declare (dynamic-extent x))
     (unless (equalp (caar x) (make-nested-good :bar *bar*))
       (error "got ~S, wanted ~S" (caar x) (make-nested-good :bar *bar*)))
-    (caar x)))
+    ;; the NESTED instance itself *should* be DX!
+    (copy-nested (caar x))))
 
 (with-test (:name :conservative-nested-dx)
   ;; NESTED-BAD should not stack-allocate :BAR due to the SETF.
 
 (defvar *a-cons* (cons nil nil))
 
-#+stack-allocatable-closures
-(with-test (:name (:no-consing :dx-closures))
+(with-test (:name (:no-consing :dx-closures) :skipped-on '(not :stack-allocatable-closures))
   (assert-no-consing (dxclosure 42)))
 
-#+stack-allocatable-lists
-(with-test (:name (:no-consing :dx-lists))
+(with-test (:name (:no-consing :dx-lists) :skipped-on '(not :stack-allocatable-lists))
   (assert-no-consing (dxlength 1 2 3))
   (assert-no-consing (dxlength t t t t t t))
   (assert-no-consing (dxlength))
 (with-test (:name (:no-consing :dx-value-cell))
   (assert-no-consing (dx-value-cell 13)))
 
-#+stack-allocatable-fixed-objects
-(with-test (:name (:no-consing :dx-fixed-objects))
+(with-test (:name (:no-consing :dx-fixed-objects) :skipped-on '(not :stack-allocatable-fixed-objects))
   (assert-no-consing (cons-on-stack 42))
   (assert-no-consing (make-foo1-on-stack 123))
   (assert-no-consing (nested-good 42))
   (assert-no-consing (dx-handler-bind 2))
   (assert-no-consing (dx-handler-case 2)))
 
-#+stack-allocatable-vectors
-(with-test (:name (:no-consing :dx-vectors))
+(with-test (:name (:no-consing :dx-vectors) :skipped-on '(not :stack-allocatable-vectors))
   (assert-no-consing (force-make-array-on-stack 128))
-  (assert-no-consing (make-array-on-stack-1))
   (assert-no-consing (make-array-on-stack-2 5 '(1 2.0 3 4.0 5)))
   (assert-no-consing (make-array-on-stack-3 9 8 7))
   (assert-no-consing (make-array-on-stack-4))
   (assert-no-consing (make-array-on-stack-5))
   (assert-no-consing (vector-on-stack :x :y)))
 
-#+raw-instance-init-vops
-(with-test (:name (:no-consing :dx-raw-instances) :fails-on :ppc)
+(with-test (:name (:no-consing :specialized-dx-vectors)
+            :fails-on :x86
+            :skipped-on `(not (and :stack-allocatable-vectors
+                                   :c-stack-is-control-stack)))
+  (assert-no-consing (make-array-on-stack-1))
+  (assert-no-consing (make-array-on-stack-6))
+  (assert-no-consing (make-array-on-stack-7))
+  (assert-no-consing (make-array-on-stack-8))
+  (assert-no-consing (make-array-on-stack-9))
+  (assert-no-consing (make-array-on-stack-10))
+  (assert-no-consing (make-array-on-stack-11)))
+
+(with-test (:name (:no-consing :dx-raw-instances) :skipped-on '(or (not :raw-instance-init-vops)
+                                                                   (not (and :gencgc :c-stack-is-control-stack))))
   (let (a b)
     (setf a 1.24 b 1.23d0)
     (assert-no-consing (make-foo2-on-stack a b)))
   (gethash 5 *table*))
 
 ;; This fails on threaded PPC because the hash-table implementation
-;; uses recursive system spinlocks, which cons (see below for test
-;; (:no-consing :spinlock), which also fails on threaded PPC).
-(with-test (:name (:no-consing :hash-tables) :fails-on (and :ppc :sb-thread))
+;; uses recursive system locks, which cons (see below for test
+;; (:no-consing :lock), which also fails on threaded PPC).
+;;
+;; -- That may have been the situation in 2010 when the above comment
+;; was written, but AFAICT now, hash tables use WITH-PINNED-OBJECTS,
+;; which conses on PPC and SPARC when GENCGC is enabled.  So neither is
+;; this actually about threading, nor about PPC.  Yet since we are
+;; failing most of this file on SPARC anyway (for some tests even on
+;; cheneygc), I won't bother to mark this particular test as failing.
+;; It would be nice if someone could go through this file and figure it
+;; all out... --DFL
+(with-test (:name (:no-consing :hash-tables) :fails-on '(and :ppc :sb-thread))
   (assert-no-consing (test-hash-table)))
 
-;;; with-spinlock and with-mutex should use DX and not cons
+;;; Both with-pinned-objects and without-gcing should not cons
+
+(defun call-without-gcing (fun)
+  (sb-sys:without-gcing (funcall fun)))
 
-(defvar *slock* (sb-thread::make-spinlock :name "slocklock"))
+(defun call-with-pinned-object (fun obj)
+  (sb-sys:with-pinned-objects (obj)
+    (funcall fun obj)))
 
-(defun test-spinlock ()
-  (sb-thread::with-spinlock (*slock*)
-    (true *slock*)))
+(with-test (:name (:no-consing :without-gcing))
+  (assert-no-consing (call-without-gcing (lambda ()))))
+
+(with-test (:name (:no-consing :with-pinned-objects))
+  (assert-no-consing (call-with-pinned-object #'identity 42)))
+
+;;; with-mutex should use DX and not cons
 
 (defvar *mutex* (sb-thread::make-mutex :name "mutexlock"))
 
   (sb-thread:with-mutex (*mutex*)
     (true *mutex*)))
 
-#+sb-thread
-(with-test (:name (:no-consing :mutex) :fails-on :ppc)
+(with-test (:name (:no-consing :mutex) :fails-on :ppc :skipped-on '(not :sb-thread))
   (assert-no-consing (test-mutex)))
-
-#+sb-thread
-(with-test (:name (:no-consing :spinlock) :fails-on :ppc)
-  (assert-no-consing (test-spinlock)))
-
 \f
 
 ;;; Bugs found by Paul F. Dietz
   (bdowning-2005-iv-16))
 
 (declaim (inline my-nconc))
-(defun-with-dx my-nconc (&rest lists)
+(defun my-nconc (&rest lists)
   (declare (dynamic-extent lists))
   (apply #'nconc lists))
 (defun-with-dx my-nconc-caller (a b c)
                          nil)))
     (assert-notes 0 `(lambda (list)
                        (declare (optimize (space 0)))
-                       (sort list #'<)))
+                       (sort list (lambda (x y) ; shut unrelated notes up
+                                    (< (truly-the fixnum x)
+                                       (truly-the fixnum y))))))
     (assert-notes 0 `(lambda (other)
                        #'(lambda (s c n)
                            (ignore-errors (funcall other s c n)))))))
              (flet ((bar () t))
                (cons #'bar (lambda () (declare (dynamic-extent #'bar))))))
           'sb-ext:compiler-note)))
+
+(with-test (:name :bug-586105 :fails-on '(not (and :stack-allocatable-vectors
+                                                   :stack-allocatable-lists)))
+  (flet ((test (x)
+           (let ((vec (make-array 1 :initial-contents (list (list x)))))
+             (declare (dynamic-extent vec))
+             (assert (eql x (car (aref vec 0)))))))
+    (assert-no-consing (test 42))))
 \f
+(defun bug-681092 ()
+  (declare (optimize speed))
+  (let ((c 0))
+    (flet ((bar () c))
+      (declare (dynamic-extent #'bar))
+      (do () ((list) (bar))
+        (setf c 10)
+        (return (bar))))))
+(with-test (:name :bug-681092)
+  (assert (= 10 (bug-681092))))
+
+;;;; &REST lists should stop DX propagation -- not required by ANSI,
+;;;; but required by sanity.
+
+(declaim (inline rest-stops-dx))
+(defun-with-dx rest-stops-dx (&rest args)
+  (declare (dynamic-extent args))
+  (apply #'opaque-identity args))
+
+(defun-with-dx rest-stops-dx-ok ()
+  (equal '(:foo) (rest-stops-dx (list :foo))))
+
+(with-test (:name :rest-stops-dynamic-extent)
+  (assert (rest-stops-dx-ok)))
+
+;;;; These tests aren't strictly speaking DX, but rather &REST -> &MORE
+;;;; conversion.
+(with-test (:name :rest-to-more-conversion)
+  (let ((f1 (compile nil `(lambda (f &rest args)
+                            (apply f args)))))
+    (assert-no-consing (assert (eql f1 (funcall f1 #'identity f1)))))
+  (let ((f2 (compile nil `(lambda (f1 f2 &rest args)
+                            (values (apply f1 args) (apply f2 args))))))
+    (assert-no-consing (multiple-value-bind (a b)
+                           (funcall f2 (lambda (x y z) (+ x y z)) (lambda (x y z) (- x y z))
+                                    1 2 3)
+                         (assert (and (eql 6 a) (eql -4 b))))))
+  (let ((f3 (compile nil `(lambda (f &optional x &rest args)
+                            (when x
+                              (apply f x args))))))
+    (assert-no-consing (assert (eql 42 (funcall f3
+                                                (lambda (a b c) (+ a b c))
+                                                11
+                                                10
+                                                21)))))
+  (let ((f4 (compile nil `(lambda (f &optional x &rest args &key y &allow-other-keys)
+                            (apply f y x args)))))
+    (assert-no-consing (funcall f4 (lambda (y x yk y2 b c)
+                                     (assert (eq y 'y))
+                                     (assert (= x 2))
+                                     (assert (eq :y yk))
+                                     (assert (eq y2 'y))
+                                     (assert (eq b 'b))
+                                     (assert (eq c 'c)))
+                                2 :y 'y 'b 'c)))
+  (let ((f5 (compile nil `(lambda (a b c &rest args)
+                            (apply #'list* a b c args)))))
+    (assert (equal '(1 2 3 4 5 6 7) (funcall f5 1 2 3 4 5 6 '(7)))))
+  (let ((f6 (compile nil `(lambda (x y)
+                            (declare (optimize speed))
+                            (concatenate 'string x y)))))
+    (assert (equal "foobar" (funcall f6 "foo" "bar"))))
+  (let ((f7 (compile nil `(lambda (&rest args)
+                            (lambda (f)
+                              (apply f args))))))
+    (assert (equal '(a b c d e f) (funcall (funcall f7 'a 'b 'c 'd 'e 'f) 'list))))
+  (let ((f8 (compile nil `(lambda (&rest args)
+                            (flet ((foo (f)
+                                     (apply f args)))
+                              #'foo)))))
+    (assert (equal '(a b c d e f) (funcall (funcall f8 'a 'b 'c 'd 'e 'f) 'list))))
+  (let ((f9 (compile nil `(lambda (f &rest args)
+                            (flet ((foo (g)
+                                     (apply g args)))
+                              (declare (dynamic-extent #'foo))
+                              (funcall f #'foo))))))
+    (assert (equal '(a b c d e f)
+                   (funcall f9 (lambda (f) (funcall f 'list)) 'a 'b 'c 'd 'e 'f))))
+  (let ((f10 (compile nil `(lambda (f &rest args)
+                            (flet ((foo (g)
+                                     (apply g args)))
+                              (funcall f #'foo))))))
+    (assert (equal '(a b c d e f)
+                   (funcall f10 (lambda (f) (funcall f 'list)) 'a 'b 'c 'd 'e 'f))))
+  (let ((f11 (compile nil `(lambda (x y z)
+                             (block out
+                               (labels ((foo (x &rest rest)
+                                          (apply (lambda (&rest rest2)
+                                                   (return-from out (values-list rest2)))
+                                                 x rest)))
+                                (if x
+                                    (foo x y z)
+                                    (foo y z x))))))))
+    (multiple-value-bind (a b c) (funcall f11 1 2 3)
+      (assert (eql a 1))
+      (assert (eql b 2))
+      (assert (eql c 3)))))
+
+(defun opaque-funcall (function &rest arguments)
+  (apply function arguments))
+
+(with-test (:name :implicit-value-cells)
+  (flet ((test-it (type input output)
+           (let ((f (compile nil `(lambda (x)
+                                    (declare (type ,type x))
+                                    (flet ((inc ()
+                                             (incf x)))
+                                      (declare (dynamic-extent #'inc))
+                                      (list (opaque-funcall #'inc) x))))))
+             (assert (equal (funcall f input)
+                            (list output output))))))
+    (let ((width sb-vm:n-word-bits))
+      (test-it t (1- most-positive-fixnum) most-positive-fixnum)
+      (test-it `(unsigned-byte ,(1- width)) (ash 1 (- width 2)) (1+ (ash 1 (- width 2))))
+      (test-it `(signed-byte ,width) (ash -1 (- width 2)) (1+ (ash -1 (- width 2))))
+      (test-it `(unsigned-byte ,width) (ash 1 (1- width)) (1+ (ash 1 (1- width))))
+      (test-it 'single-float 3f0 4f0)
+      (test-it 'double-float 3d0 4d0)
+      (test-it '(complex single-float) #c(3f0 4f0) #c(4f0 4f0))
+      (test-it '(complex double-float) #c(3d0 4d0) #c(4d0 4d0)))))
+
+(with-test (:name :sap-implicit-value-cells)
+  (let ((f (compile nil `(lambda (x)
+                           (declare (type system-area-pointer x))
+                           (flet ((inc ()
+                                    (setf x (sb-sys:sap+ x 16))))
+                             (declare (dynamic-extent #'inc))
+                             (list (opaque-funcall #'inc) x)))))
+        (width sb-vm:n-machine-word-bits))
+    (assert (every (lambda (x)
+                     (sb-sys:sap= x (sb-sys:int-sap (+ 16 (ash 1 (1- width))))))
+                   (funcall f (sb-sys:int-sap (ash 1 (1- width))))))))
+
+(with-test (:name :&more-bounds)
+  ;; lp#1154946
+  (assert (not (funcall (compile nil '(lambda (&rest args) (car args))))))
+  (assert (not (funcall (compile nil '(lambda (&rest args) (nth 6 args))))))
+  (assert (not (funcall (compile nil '(lambda (&rest args) (elt args 10))))))
+  (assert (not (funcall (compile nil '(lambda (&rest args) (cadr args))))))
+  (assert (not (funcall (compile nil '(lambda (&rest args) (third args)))))))