+
;;;; various compiler tests without side effects
;;;; This software is part of the SBCL system. See the README file for
(cl:in-package :cl-user)
+(load "compiler-test-util.lisp")
+
;; The tests in this file assume that EVAL will use the compiler
(when (eq sb-ext:*evaluator-mode* :interpret)
(invoke-restart 'run-tests::skip-file))
;;; on the PPC, we got the magic numbers in undefined_tramp wrong for
;;; a while; fixed by CSR 2002-07-18
-(multiple-value-bind (value error)
- (ignore-errors (some-undefined-function))
- (assert (null value))
- (assert (eq (cell-error-name error) 'some-undefined-function)))
+(with-test (:name :undefined-function-error)
+ (multiple-value-bind (value error)
+ (ignore-errors (some-undefined-function))
+ (assert (null value))
+ (assert (eq (cell-error-name error) 'some-undefined-function))))
;;; Non-symbols shouldn't be allowed as VARs in lambda lists. (Where VAR
;;; is a variable name, as in section 3.4.1 of the ANSI spec.)
(declare (type (alien (* (unsigned 8))) a)
(type (unsigned-byte 32) i))
(deref a i))))
- (compiler-note () (error "The code is not optimized.")))
+ (compiler-note (c)
+ (unless (search "%ASH/RIGHT" (first (simple-condition-format-arguments c)))
+ (error "The code is not optimized."))))
(handler-case
(compile nil '(lambda (x)
(error "bad RANDOM event"))))
;;; 0.8.17.28-sma.1 lost derived type information.
-(with-test (:name "0.8.17.28-sma.1" :fails-on :sparc)
+(with-test (:name :0.8.17.28-sma.1 :fails-on :sparc)
(handler-bind ((sb-ext:compiler-note (lambda (c) (error "~A" c))))
(compile nil
'(lambda (x y v)
(compiler-note () (throw :note nil)))
(error "Unreachable code undetected.")))
+(with-test (:name (:compiler :constraint-propagation :float-bounds-3
+ :LP-894498))
+ (catch :note
+ (handler-case
+ (compile nil '(lambda (x)
+ (declare (type (single-float 0.0) x))
+ (when (> x 0.0)
+ (when (zerop x)
+ (error "This is unreachable.")))))
+ (compiler-note () (throw :note nil)))
+ (error "Unreachable code undetected.")))
+
+(with-test (:name (:compiler :constraint-propagation :float-bounds-4
+ :LP-894498))
+ (catch :note
+ (handler-case
+ (compile nil '(lambda (x y)
+ (declare (type (single-float 0.0) x)
+ (type (single-float (0.0)) y))
+ (when (> x y)
+ (when (zerop x)
+ (error "This is unreachable.")))))
+ (compiler-note () (throw :note nil)))
+ (error "Unreachable code undetected.")))
+
(with-test (:name (:compiler :constraint-propagation :var-eql-to-var-1))
(catch :note
(handler-case
(logand most-positive-fixnum (* x most-positive-fixnum))))
;;; bug 256.b
-(assert (let (warned-p)
+(with-test (:name :propagate-type-through-error-and-binding)
+ (assert (let (warned-p)
(handler-bind ((warning (lambda (w) (setf warned-p t))))
(compile nil
- '(lambda (x)
- (list (let ((y (the real x)))
- (unless (floatp y) (error ""))
- y)
- (integer-length x)))))
- warned-p))
+ '(lambda (x)
+ (list (let ((y (the real x)))
+ (unless (floatp y) (error ""))
+ y)
+ (integer-length x)))))
+ warned-p)))
;; Dead / in safe code
(with-test (:name :safe-dead-/)
(+ 81535869 (the (member 17549.955 #:g35917) p2))))
17549.955)
(+ 81535869 17549.955)))
+
+;;; misc.654
+(assert (eql 2
+ (let ((form '(lambda (p2)
+ (declare (optimize (speed 0) (safety 2) (debug 0) (space 2))
+ (type (member integer eql) p2))
+ (coerce 2 p2))))
+ (funcall (compile nil form) 'integer))))
+
+;;; misc.656
+(assert (eql 2
+ (let ((form '(lambda (p2)
+ (declare (optimize (speed 0) (safety 2) (debug 0) (space 2))
+ (type (member integer mod) p2))
+ (coerce 2 p2))))
+ (funcall (compile nil form) 'integer))))
+
+;;; misc.657
+(assert (eql 2
+ (let ((form '(lambda (p2)
+ (declare (optimize (speed 0) (safety 2) (debug 0) (space 2))
+ (type (member integer values) p2))
+ (coerce 2 p2))))
+ (funcall (compile nil form) 'integer))))
+
+(with-test (:name :string-aref-type)
+ (assert (eq 'character
+ (funcall (compile nil
+ '(lambda (s)
+ (ctu:compiler-derived-type (aref (the string s) 0))))
+ "foo"))))
+
+(with-test (:name :base-string-aref-type)
+ (assert (eq #+sb-unicode 'base-char
+ #-sb-unicode 'character
+ (funcall (compile nil
+ '(lambda (s)
+ (ctu:compiler-derived-type (aref (the base-string s) 0))))
+ (coerce "foo" 'base-string)))))
+
+(with-test (:name :dolist-constant-type-derivation)
+ (assert (equal '(integer 1 3)
+ (funcall (compile nil
+ '(lambda (x)
+ (dolist (y '(1 2 3))
+ (when x
+ (return (ctu:compiler-derived-type y))))))
+ t))))
+
+(with-test (:name :dolist-simple-list-type-derivation)
+ (assert (equal '(integer 1 3)
+ (funcall (compile nil
+ '(lambda (x)
+ (dolist (y (list 1 2 3))
+ (when x
+ (return (ctu:compiler-derived-type y))))))
+ t))))
+
+(with-test (:name :dolist-dotted-constant-list-type-derivation)
+ (let* ((warned nil)
+ (fun (handler-bind ((style-warning (lambda (c) (push c warned))))
+ (compile nil
+ '(lambda (x)
+ (dolist (y '(1 2 3 . 4) :foo)
+ (when x
+ (return (ctu:compiler-derived-type y)))))))))
+ (assert (equal '(integer 1 3) (funcall fun t)))
+ (assert (= 1 (length warned)))
+ (multiple-value-bind (res err) (ignore-errors (funcall fun nil))
+ (assert (not res))
+ (assert (typep err 'type-error)))))
+
+(with-test (:name :constant-list-destructuring)
+ (handler-bind ((sb-ext:compiler-note #'error))
+ (progn
+ (assert (= 10
+ (funcall
+ (compile nil
+ '(lambda ()
+ (destructuring-bind (a (b c) d) '(1 (2 3) 4)
+ (+ a b c d)))))))
+ (assert (eq :feh
+ (funcall
+ (compile nil
+ '(lambda (x)
+ (or x
+ (destructuring-bind (a (b c) d) '(1 "foo" 4)
+ (+ a b c d)))))
+ :feh))))))
+
+;;; Functions with non-required arguments used to end up with
+;;; (&OPTIONAL-DISPATCH ...) as their names.
+(with-test (:name :hairy-function-name)
+ (assert (eq 'read-line (nth-value 2 (function-lambda-expression #'read-line))))
+ (assert (equal "#<FUNCTION READ-LINE>" (princ-to-string #'read-line))))
+
+;;; PROGV + RESTRICT-COMPILER-POLICY
+(with-test (:name :progv-and-restrict-compiler-policy)
+ (let ((sb-c::*policy-restrictions* sb-c::*policy-restrictions*))
+ (restrict-compiler-policy 'debug 3)
+ (let ((fun (compile nil '(lambda (x)
+ (let ((i x))
+ (declare (special i))
+ (list i
+ (progv '(i) (list (+ i 1))
+ i)
+ i))))))
+ (assert (equal '(1 2 1) (funcall fun 1))))))
+
+;;; It used to be possible to confuse the compiler into
+;;; IR2-converting such a call to CONS
+(with-test (:name :late-bound-primitive)
+ (compile nil `(lambda ()
+ (funcall 'cons 1))))
+
+(with-test (:name :hairy-array-element-type-derivation)
+ (compile nil '(lambda (x)
+ (declare (type (and simple-string (satisfies array-has-fill-pointer-p)) x))
+ (array-element-type x))))
+
+(with-test (:name :rest-list-type-derivation)
+ (multiple-value-bind (type derivedp)
+ (funcall (compile nil `(lambda (&rest args)
+ (ctu:compiler-derived-type args)))
+ nil)
+ (assert (eq 'list type))
+ (assert derivedp)))
+
+(with-test (:name :rest-list-type-derivation2)
+ (multiple-value-bind (type derivedp)
+ (funcall (funcall (compile nil `(lambda ()
+ (lambda (&rest args)
+ (ctu:compiler-derived-type args))))))
+ (assert (eq 'list type))
+ (assert derivedp)))
+
+(with-test (:name :rest-list-type-derivation3)
+ (multiple-value-bind (type derivedp)
+ (funcall (funcall (compile nil `(lambda ()
+ (lambda (&optional x &rest args)
+ (unless x (error "oops"))
+ (ctu:compiler-derived-type args)))))
+ t)
+ (assert (eq 'list type))
+ (assert derivedp)))
+
+(with-test (:name :rest-list-type-derivation4)
+ (multiple-value-bind (type derivedp)
+ (funcall (funcall (compile nil `(lambda ()
+ (lambda (&optional x &rest args)
+ (declare (type (or null integer) x))
+ (when x (setf args x))
+ (ctu:compiler-derived-type args)))))
+ 42)
+ (assert (equal '(or cons null integer) type))
+ (assert derivedp)))
+
+(with-test (:name :base-char-typep-elimination)
+ (assert (eq (funcall (compile nil
+ `(lambda (ch)
+ (declare (type base-char ch) (optimize (speed 3) (safety 0)))
+ (typep ch 'base-char)))
+ t)
+ t)))
+
+(with-test (:name :regression-1.0.24.37)
+ (compile nil '(lambda (&key (test (constantly t)))
+ (when (funcall test)
+ :quux))))
+
+;;; Attempt to test a decent cross section of conditions
+;;; and values types to move conditionally.
+(macrolet
+ ((test-comparison (comparator type x y)
+ `(progn
+ ,@(loop for (result-type a b)
+ in '((nil t nil)
+ (nil 0 1)
+ (nil 0.0 1.0)
+ (nil 0d0 0d0)
+ (nil 0.0 0d0)
+ (nil #c(1.0 1.0) #c(2.0 2.0))
+
+ (t t nil)
+ (fixnum 0 1)
+ ((unsigned-byte #.sb-vm:n-word-bits)
+ (1+ most-positive-fixnum)
+ (+ 2 most-positive-fixnum))
+ ((signed-byte #.sb-vm:n-word-bits)
+ -1 (* 2 most-negative-fixnum))
+ (single-float 0.0 1.0)
+ (double-float 0d0 1d0))
+ for lambda = (if result-type
+ `(lambda (x y a b)
+ (declare (,type x y)
+ (,result-type a b))
+ (if (,comparator x y)
+ a b))
+ `(lambda (x y)
+ (declare (,type x y))
+ (if (,comparator x y)
+ ,a ,b)))
+ for args = `(,x ,y ,@(and result-type
+ `(,a ,b)))
+ collect
+ `(progn
+ (eql (funcall (compile nil ',lambda)
+ ,@args)
+ (eval '(,lambda ,@args))))))))
+ (sb-vm::with-float-traps-masked
+ (:divide-by-zero :overflow :inexact :invalid)
+ (let (#+sb-eval (sb-ext:*evaluator-mode* :interpret))
+ (declare (sb-ext:muffle-conditions style-warning))
+ (test-comparison eql t t nil)
+ (test-comparison eql t t t)
+
+ (test-comparison = t 1 0)
+ (test-comparison = t 1 1)
+ (test-comparison = t (1+ most-positive-fixnum) (+ 2 most-positive-fixnum))
+ (test-comparison = fixnum 1 0)
+ (test-comparison = fixnum 0 0)
+ (test-comparison = (unsigned-byte #.sb-vm:n-word-bits) 1 0)
+ (test-comparison = (unsigned-byte #.sb-vm:n-word-bits) 0 0)
+ (test-comparison = (signed-byte #.sb-vm:n-word-bits) 1 0)
+ (test-comparison = (signed-byte #.sb-vm:n-word-bits) 1 1)
+
+ (test-comparison = single-float 0.0 1.0)
+ (test-comparison = single-float 1.0 1.0)
+ (test-comparison = single-float (/ 1.0 0.0) (/ 1.0 0.0))
+ (test-comparison = single-float (/ 1.0 0.0) 1.0)
+ (test-comparison = single-float (/ 0.0 0.0) (/ 0.0 0.0))
+ (test-comparison = single-float (/ 0.0 0.0) 0.0)
+
+ (test-comparison = double-float 0d0 1d0)
+ (test-comparison = double-float 1d0 1d0)
+ (test-comparison = double-float (/ 1d0 0d0) (/ 1d0 0d0))
+ (test-comparison = double-float (/ 1d0 0d0) 1d0)
+ (test-comparison = double-float (/ 0d0 0d0) (/ 0d0 0d0))
+ (test-comparison = double-float (/ 0d0 0d0) 0d0)
+
+ (test-comparison < t 1 0)
+ (test-comparison < t 0 1)
+ (test-comparison < t 1 1)
+ (test-comparison < t (1+ most-positive-fixnum) (+ 2 most-positive-fixnum))
+ (test-comparison < t (+ 2 most-positive-fixnum) (1+ most-positive-fixnum))
+ (test-comparison < fixnum 1 0)
+ (test-comparison < fixnum 0 1)
+ (test-comparison < fixnum 0 0)
+ (test-comparison < (unsigned-byte #.sb-vm:n-word-bits) 1 0)
+ (test-comparison < (unsigned-byte #.sb-vm:n-word-bits) 0 1)
+ (test-comparison < (unsigned-byte #.sb-vm:n-word-bits) 0 0)
+ (test-comparison < (signed-byte #.sb-vm:n-word-bits) 1 0)
+ (test-comparison < (signed-byte #.sb-vm:n-word-bits) 0 1)
+ (test-comparison < (signed-byte #.sb-vm:n-word-bits) 1 1)
+
+ (test-comparison < single-float 0.0 1.0)
+ (test-comparison < single-float 1.0 0.0)
+ (test-comparison < single-float 1.0 1.0)
+ (test-comparison < single-float (/ 1.0 0.0) (/ 1.0 0.0))
+ (test-comparison < single-float (/ 1.0 0.0) 1.0)
+ (test-comparison < single-float 1.0 (/ 1.0 0.0))
+ (test-comparison < single-float (/ 0.0 0.0) (/ 0.0 0.0))
+ (test-comparison < single-float (/ 0.0 0.0) 0.0)
+
+ (test-comparison < double-float 0d0 1d0)
+ (test-comparison < double-float 1d0 0d0)
+ (test-comparison < double-float 1d0 1d0)
+ (test-comparison < double-float (/ 1d0 0d0) (/ 1d0 0d0))
+ (test-comparison < double-float (/ 1d0 0d0) 1d0)
+ (test-comparison < double-float 1d0 (/ 1d0 0d0))
+ (test-comparison < double-float (/ 0d0 0d0) (/ 0d0 0d0))
+ (test-comparison < double-float (/ 0d0 0d0) 0d0)
+ (test-comparison < double-float 0d0 (/ 0d0 0d0))
+
+ (test-comparison > t 1 0)
+ (test-comparison > t 0 1)
+ (test-comparison > t 1 1)
+ (test-comparison > t (1+ most-positive-fixnum) (+ 2 most-positive-fixnum))
+ (test-comparison > t (+ 2 most-positive-fixnum) (1+ most-positive-fixnum))
+ (test-comparison > fixnum 1 0)
+ (test-comparison > fixnum 0 1)
+ (test-comparison > fixnum 0 0)
+ (test-comparison > (unsigned-byte #.sb-vm:n-word-bits) 1 0)
+ (test-comparison > (unsigned-byte #.sb-vm:n-word-bits) 0 1)
+ (test-comparison > (unsigned-byte #.sb-vm:n-word-bits) 0 0)
+ (test-comparison > (signed-byte #.sb-vm:n-word-bits) 1 0)
+ (test-comparison > (signed-byte #.sb-vm:n-word-bits) 0 1)
+ (test-comparison > (signed-byte #.sb-vm:n-word-bits) 1 1)
+
+ (test-comparison > single-float 0.0 1.0)
+ (test-comparison > single-float 1.0 0.0)
+ (test-comparison > single-float 1.0 1.0)
+ (test-comparison > single-float (/ 1.0 0.0) (/ 1.0 0.0))
+ (test-comparison > single-float (/ 1.0 0.0) 1.0)
+ (test-comparison > single-float 1.0 (/ 1.0 0.0))
+ (test-comparison > single-float (/ 0.0 0.0) (/ 0.0 0.0))
+ (test-comparison > single-float (/ 0.0 0.0) 0.0)
+
+ (test-comparison > double-float 0d0 1d0)
+ (test-comparison > double-float 1d0 0d0)
+ (test-comparison > double-float 1d0 1d0)
+ (test-comparison > double-float (/ 1d0 0d0) (/ 1d0 0d0))
+ (test-comparison > double-float (/ 1d0 0d0) 1d0)
+ (test-comparison > double-float 1d0 (/ 1d0 0d0))
+ (test-comparison > double-float (/ 0d0 0d0) (/ 0d0 0d0))
+ (test-comparison > double-float (/ 0d0 0d0) 0d0)
+ (test-comparison > double-float 0d0 (/ 0d0 0d0)))))
+
+(with-test (:name :car-and-cdr-type-derivation-conservative)
+ (let ((f1 (compile nil
+ `(lambda (y)
+ (declare (optimize speed))
+ (let ((x (the (cons fixnum fixnum) (cons 1 2))))
+ (declare (type (cons t fixnum) x))
+ (rplaca x y)
+ (+ (car x) (cdr x))))))
+ (f2 (compile nil
+ `(lambda (y)
+ (declare (optimize speed))
+ (let ((x (the (cons fixnum fixnum) (cons 1 2))))
+ (setf (cdr x) y)
+ (+ (car x) (cdr x)))))))
+ (flet ((test-error (e value)
+ (assert (typep e 'type-error))
+ (assert (eq 'number (type-error-expected-type e)))
+ (assert (eq value (type-error-datum e)))))
+ (let ((v1 "foo")
+ (v2 "bar"))
+ (multiple-value-bind (res err) (ignore-errors (funcall f1 v1))
+ (assert (not res))
+ (test-error err v1))
+ (multiple-value-bind (res err) (ignore-errors (funcall f2 v2))
+ (assert (not res))
+ (test-error err v2))))))
+
+(with-test (:name :array-dimension-derivation-conservative)
+ (let ((f (compile nil
+ `(lambda (x)
+ (declare (optimize speed))
+ (declare (type (array * (4 4)) x))
+ (let ((y x))
+ (setq x (make-array '(4 4)))
+ (adjust-array y '(3 5))
+ (array-dimension y 0))))))
+ (assert (= 3 (funcall f (make-array '(4 4) :adjustable t))))))
+
+(with-test (:name :with-timeout-code-deletion-note)
+ (handler-bind ((sb-ext:code-deletion-note #'error))
+ (compile nil `(lambda ()
+ (sb-ext:with-timeout 0
+ (sleep 1))))))
+
+(with-test (:name :full-warning-for-undefined-type-in-cl)
+ (assert (eq :full
+ (handler-case
+ (compile nil `(lambda (x) (the replace x)))
+ (style-warning ()
+ :style)
+ (warning ()
+ :full)))))
+
+(with-test (:name :single-warning-for-single-undefined-type)
+ (let ((n 0))
+ (handler-bind ((warning (lambda (c)
+ (declare (ignore c))
+ (incf n))))
+ (compile nil `(lambda (x) (the #:no-type x)))
+ (assert (= 1 n))
+ (compile nil `(lambda (x) (the 'fixnum x)))
+ (assert (= 2 n)))))
+
+(with-test (:name :complex-subtype-dumping-in-xc)
+ (assert
+ (= sb-vm:complex-single-float-widetag
+ (sb-kernel:widetag-of
+ (sb-vm:saetp-initial-element-default (sb-c::find-saetp '(complex single-float))))))
+ (assert
+ (= sb-vm:complex-double-float-widetag
+ (sb-kernel:widetag-of
+ (sb-vm:saetp-initial-element-default (sb-c::find-saetp '(complex double-float)))))))
+
+(with-test (:name :complex-single-float-fill)
+ (assert (every (lambda (x) (= #c(1.0 2.0) x))
+ (funcall
+ (compile nil
+ `(lambda (n x)
+ (make-array (list n)
+ :element-type '(complex single-float)
+ :initial-element x)))
+ 10
+ #c(1.0 2.0)))))
+
+(with-test (:name :regression-1.0.28.21)
+ (let ((fun (compile nil `(lambda (x) (typep x '(simple-array * 1))))))
+ (assert (funcall fun (vector 1 2 3)))
+ (assert (funcall fun "abc"))
+ (assert (not (funcall fun (make-array '(2 2)))))))
+
+(with-test (:name :no-silly-compiler-notes-from-character-function)
+ (let (current)
+ (handler-bind ((compiler-note (lambda (e) (error "~S: ~A" current e))))
+ (dolist (name '(char-code char-int character char-name standard-char-p
+ graphic-char-p alpha-char-p upper-case-p lower-case-p
+ both-case-p digit-char-p alphanumericp digit-char-p))
+ (setf current name)
+ (compile nil `(lambda (x)
+ (declare (character x) (optimize speed))
+ (,name x))))
+ (dolist (name '(char= char/= char< char> char<= char>=
+ char-lessp char-greaterp char-not-greaterp
+ char-not-lessp))
+ (setf current name)
+ (compile nil `(lambda (x y)
+ (declare (character x y) (optimize speed))
+ (,name x y)))))))
+
+;;; optimizing make-array
+(with-test (:name (make-array :open-code-initial-contents))
+ (assert (not (ctu:find-named-callees
+ (compile nil
+ `(lambda (x y z)
+ (make-array '(3) :initial-contents (list x y z)))))))
+ (assert (not (ctu:find-named-callees
+ (compile nil
+ `(lambda (x y z)
+ (make-array '3 :initial-contents (vector x y z)))))))
+ (assert (not (ctu:find-named-callees
+ (compile nil
+ `(lambda (x y z)
+ (make-array '3 :initial-contents `(,x ,y ,z))))))))
+
+;;; optimizing array-in-bounds-p
+(with-test (:name :optimize-array-in-bounds-p)
+ (locally
+ (macrolet ((find-callees (&body body)
+ `(ctu:find-named-callees
+ (compile nil
+ '(lambda ()
+ ,@body))
+ :name 'array-in-bounds-p))
+ (must-optimize (&body exprs)
+ `(progn
+ ,@(loop for expr in exprs
+ collect `(assert (not (find-callees
+ ,expr))))))
+ (must-not-optimize (&body exprs)
+ `(progn
+ ,@(loop for expr in exprs
+ collect `(assert (find-callees
+ ,expr))))))
+ (must-optimize
+ ;; in bounds
+ (let ((a (make-array '(1))))
+ (array-in-bounds-p a 0))
+ ;; exceeds upper bound (constant)
+ (let ((a (make-array '(1))))
+ (array-in-bounds-p a 1))
+ ;; exceeds upper bound (interval)
+ (let ((a (make-array '(1))))
+ (array-in-bounds-p a (+ 1 (random 2))))
+ ;; negative lower bound (constant)
+ (let ((a (make-array '(1))))
+ (array-in-bounds-p a -1))
+ ;; negative lower bound (interval)
+ (let ((a (make-array 3))
+ (i (- (random 1) 20)))
+ (array-in-bounds-p a i))
+ ;; multiple known dimensions
+ (let ((a (make-array '(1 1))))
+ (array-in-bounds-p a 0 0))
+ ;; union types
+ (let ((s (the (simple-string 10) (eval "0123456789"))))
+ (array-in-bounds-p s 9)))
+ (must-not-optimize
+ ;; don't trust non-simple array length in safety=1
+ (let ((a (the (array * (10)) (make-array 10 :adjustable t))))
+ (eval `(adjust-array ,a 0))
+ (array-in-bounds-p a 9))
+ ;; same for a union type
+ (let ((s (the (string 10) (make-array 10
+ :element-type 'character
+ :adjustable t))))
+ (eval `(adjust-array ,s 0))
+ (array-in-bounds-p s 9))
+ ;; single unknown dimension
+ (let ((a (make-array (random 20))))
+ (array-in-bounds-p a 10))
+ ;; multiple unknown dimensions
+ (let ((a (make-array (list (random 20) (random 5)))))
+ (array-in-bounds-p a 5 2))
+ ;; some other known dimensions
+ (let ((a (make-array (list 1 (random 5)))))
+ (array-in-bounds-p a 0 2))
+ ;; subscript might be negative
+ (let ((a (make-array 5)))
+ (array-in-bounds-p a (- (random 3) 2)))
+ ;; subscript might be too large
+ (let ((a (make-array 5)))
+ (array-in-bounds-p a (random 6)))
+ ;; unknown upper bound
+ (let ((a (make-array 5)))
+ (array-in-bounds-p a (get-universal-time)))
+ ;; unknown lower bound
+ (let ((a (make-array 5)))
+ (array-in-bounds-p a (- (get-universal-time))))
+ ;; in theory we should be able to optimize
+ ;; the following but the current implementation
+ ;; doesn't cut it because the array type's
+ ;; dimensions get reported as (* *).
+ (let ((a (make-array (list (random 20) 1))))
+ (array-in-bounds-p a 5 2))))))
+
+;;; optimizing (EXPT -1 INTEGER)
+(with-test (:name (expt -1 integer))
+ (dolist (x '(-1 -1.0 -1.0d0))
+ (let ((fun (compile nil `(lambda (x) (expt ,x (the fixnum x))))))
+ (assert (not (ctu:find-named-callees fun)))
+ (dotimes (i 12)
+ (if (oddp i)
+ (assert (eql x (funcall fun i)))
+ (assert (eql (- x) (funcall fun i))))))))
+
+(with-test (:name :float-division-using-exact-reciprocal)
+ (flet ((test (lambda-form arg res &key (check-insts t))
+ (let* ((fun (compile nil lambda-form))
+ (disassembly (with-output-to-string (s)
+ (disassemble fun :stream s))))
+ ;; Let's make sure there is no division at runtime: for x86 and
+ ;; x86-64 that implies an FDIV, DIVSS, or DIVSD instruction, so
+ ;; look for DIV in the disassembly. It's a terrible KLUDGE, but
+ ;; it works.
+ #+(or x86 x86-64)
+ (when check-insts
+ (assert (not (search "DIV" disassembly))))
+ ;; No generic arithmetic!
+ (assert (not (search "GENERIC" disassembly)))
+ (assert (eql res (funcall fun arg))))))
+ (dolist (c '(128 64 32 16 8 4 2 1 1/2 1/4 1/8 1/16 1/32 1/64))
+ (dolist (type '(single-float double-float))
+ (let* ((cf (coerce c type))
+ (arg (- (random (* 2 cf)) cf))
+ (r1 (eval `(/ ,arg ,cf)))
+ (r2 (eval `(/ ,arg ,(- cf)))))
+ (test `(lambda (x) (declare (,type x)) (/ x ,cf)) arg r1)
+ (test `(lambda (x) (declare (,type x)) (/ x ,(- cf))) arg r2)
+ ;; rational args should get optimized as well
+ (test `(lambda (x) (declare (,type x)) (/ x ,c)) arg r1)
+ (test `(lambda (x) (declare (,type x)) (/ x ,(- c))) arg r2))))
+ ;; Also check that inexact reciprocals (1) are not used by default (2) are
+ ;; used with FLOAT-ACCURACY=0.
+ (dolist (type '(single-float double-float))
+ (let ((trey (coerce 3 type))
+ (one (coerce 1 type)))
+ (test `(lambda (x) (declare (,type x)) (/ x 3)) trey one
+ :check-insts nil)
+ (test `(lambda (x)
+ (declare (,type x)
+ (optimize (sb-c::float-accuracy 0)))
+ (/ x 3))
+ trey (eval `(* ,trey (/ ,trey))))))))
+
+(with-test (:name :float-multiplication-by-one)
+ (flet ((test (lambda-form arg &optional (result arg))
+ (let* ((fun1 (compile nil lambda-form))
+ (fun2 (funcall (compile nil `(lambda ()
+ (declare (optimize (sb-c::float-accuracy 0)))
+ ,lambda-form))))
+ (disassembly1 (with-output-to-string (s)
+ (disassemble fun1 :stream s)))
+ (disassembly2 (with-output-to-string (s)
+ (disassemble fun2 :stream s))))
+ ;; Multiplication at runtime should be eliminated only with
+ ;; FLOAT-ACCURACY=0. (To catch SNaNs.)
+ #+(or x86 x86-64)
+ (assert (and (search "MUL" disassembly1)
+ (not (search "MUL" disassembly2))))
+ ;; Not generic arithmetic, please!
+ (assert (and (not (search "GENERIC" disassembly1))
+ (not (search "GENERIC" disassembly2))))
+ (assert (eql result (funcall fun1 arg)))
+ (assert (eql result (funcall fun2 arg))))))
+ (dolist (type '(single-float double-float))
+ (let* ((one (coerce 1 type))
+ (arg (random (* 2 one)))
+ (-r (- arg)))
+ (test `(lambda (x) (declare (,type x)) (* x 1)) arg)
+ (test `(lambda (x) (declare (,type x)) (* x -1)) arg -r)
+ (test `(lambda (x) (declare (,type x)) (* x ,one)) arg)
+ (test `(lambda (x) (declare (,type x)) (* x ,(- one))) arg -r)))))
+
+(with-test (:name :float-addition-of-zero)
+ (flet ((test (lambda-form arg &optional (result arg))
+ (let* ((fun1 (compile nil lambda-form))
+ (fun2 (funcall (compile nil `(lambda ()
+ (declare (optimize (sb-c::float-accuracy 0)))
+ ,lambda-form))))
+ (disassembly1 (with-output-to-string (s)
+ (disassemble fun1 :stream s)))
+ (disassembly2 (with-output-to-string (s)
+ (disassemble fun2 :stream s))))
+ ;; Let's make sure there is no addition at runtime: for x86 and
+ ;; x86-64 that implies an FADD, ADDSS, or ADDSD instruction, so
+ ;; look for the ADDs in the disassembly. It's a terrible KLUDGE,
+ ;; but it works. Unless FLOAT-ACCURACY is zero, we leave the
+ ;; addition in to catch SNaNs.
+ #+x86
+ (assert (and (search "FADD" disassembly1)
+ (not (search "FADD" disassembly2))))
+ #+x86-64
+ (let ((inst (if (typep result 'double-float)
+ "ADDSD" "ADDSS")))
+ (assert (and (search inst disassembly1)
+ (not (search inst disassembly2)))))
+ (assert (eql result (funcall fun1 arg)))
+ (assert (eql result (funcall fun2 arg))))))
+ (test `(lambda (x) (declare (single-float x)) (+ x 0)) 123.45)
+ (test `(lambda (x) (declare (single-float x)) (+ x 0.0)) 543.21)
+ (test `(lambda (x) (declare (single-float x)) (+ x 0.0d0)) 42.00 42.d0)
+ (test `(lambda (x) (declare (double-float x)) (+ x 0)) 123.45d0)
+ (test `(lambda (x) (declare (double-float x)) (+ x 0.0)) 543.21d0)
+ (test `(lambda (x) (declare (double-float x)) (+ x 0.0d0)) 42.d0)))
+
+(with-test (:name :float-substraction-of-zero)
+ (flet ((test (lambda-form arg &optional (result arg))
+ (let* ((fun1 (compile nil lambda-form))
+ (fun2 (funcall (compile nil `(lambda ()
+ (declare (optimize (sb-c::float-accuracy 0)))
+ ,lambda-form))))
+ (disassembly1 (with-output-to-string (s)
+ (disassemble fun1 :stream s)))
+ (disassembly2 (with-output-to-string (s)
+ (disassemble fun2 :stream s))))
+ ;; Let's make sure there is no substraction at runtime: for x86
+ ;; and x86-64 that implies an FSUB, SUBSS, or SUBSD instruction,
+ ;; so look for SUB in the disassembly. It's a terrible KLUDGE,
+ ;; but it works. Unless FLOAT-ACCURACY is zero, we leave the
+ ;; substraction in in to catch SNaNs.
+ #+x86
+ (assert (and (search "FSUB" disassembly1)
+ (not (search "FSUB" disassembly2))))
+ #+x86-64
+ (let ((inst (if (typep result 'double-float)
+ "SUBSD" "SUBSS")))
+ (assert (and (search inst disassembly1)
+ (not (search inst disassembly2)))))
+ (assert (eql result (funcall fun1 arg)))
+ (assert (eql result (funcall fun2 arg))))))
+ (test `(lambda (x) (declare (single-float x)) (- x 0)) 123.45)
+ (test `(lambda (x) (declare (single-float x)) (- x 0.0)) 543.21)
+ (test `(lambda (x) (declare (single-float x)) (- x 0.0d0)) 42.00 42.d0)
+ (test `(lambda (x) (declare (double-float x)) (- x 0)) 123.45d0)
+ (test `(lambda (x) (declare (double-float x)) (- x 0.0)) 543.21d0)
+ (test `(lambda (x) (declare (double-float x)) (- x 0.0d0)) 42.d0)))
+
+(with-test (:name :float-multiplication-by-two)
+ (flet ((test (lambda-form arg &optional (result arg))
+ (let* ((fun1 (compile nil lambda-form))
+ (fun2 (funcall (compile nil `(lambda ()
+ (declare (optimize (sb-c::float-accuracy 0)))
+ ,lambda-form))))
+ (disassembly1 (with-output-to-string (s)
+ (disassemble fun1 :stream s)))
+ (disassembly2 (with-output-to-string (s)
+ (disassemble fun2 :stream s))))
+ ;; Let's make sure there is no multiplication at runtime: for x86
+ ;; and x86-64 that implies an FMUL, MULSS, or MULSD instruction,
+ ;; so look for MUL in the disassembly. It's a terrible KLUDGE,
+ ;; but it works.
+ #+(or x86 x86-64)
+ (assert (and (not (search "MUL" disassembly1))
+ (not (search "MUL" disassembly2))))
+ (assert (eql result (funcall fun1 arg)))
+ (assert (eql result (funcall fun2 arg))))))
+ (test `(lambda (x) (declare (single-float x)) (* x 2)) 123.45 246.9)
+ (test `(lambda (x) (declare (single-float x)) (* x 2.0)) 543.21 1086.42)
+ (test `(lambda (x) (declare (single-float x)) (* x 2.0d0)) 42.00 84.d0)
+ (test `(lambda (x) (declare (double-float x)) (* x 2)) 123.45d0 246.9d0)
+ (test `(lambda (x) (declare (double-float x)) (* x 2.0)) 543.21d0 1086.42d0)
+ (test `(lambda (x) (declare (double-float x)) (* x 2.0d0)) 42.0d0 84.0d0)))
+
+(with-test (:name :bug-392203)
+ ;; Used to hit an AVER in COMVERT-MV-CALL.
+ (assert (zerop
+ (funcall
+ (compile nil
+ `(lambda ()
+ (flet ((k (&rest x) (declare (ignore x)) 0))
+ (multiple-value-call #'k #'k))))))))
+
+(with-test (:name :allocate-closures-failing-aver)
+ (let ((f (compile nil `(lambda ()
+ (labels ((k (&optional x) #'k)))))))
+ (assert (null (funcall f)))))
+
+(with-test (:name :flush-vector-creation)
+ (let ((f (compile nil `(lambda ()
+ (dotimes (i 1024)
+ (vector i i i))
+ t))))
+ (ctu:assert-no-consing (funcall f))))
+
+(with-test (:name :array-type-predicates)
+ (dolist (et sb-kernel::*specialized-array-element-types*)
+ (when et
+ (let* ((v (make-array 3 :element-type et))
+ (fun (compile nil `(lambda ()
+ (list
+ (if (typep ,v '(simple-array ,et (*)))
+ :good
+ :bad)
+ (if (typep (elt ,v 0) '(simple-array ,et (*)))
+ :bad
+ :good))))))
+ (assert (equal '(:good :good) (funcall fun)))))))
+
+(with-test (:name :truncate-float)
+ (let ((s (compile nil `(lambda (x)
+ (declare (single-float x))
+ (truncate x))))
+ (d (compile nil `(lambda (x)
+ (declare (double-float x))
+ (truncate x))))
+ (s-inlined (compile nil '(lambda (x)
+ (declare (type (single-float 0.0s0 1.0s0) x))
+ (truncate x))))
+ (d-inlined (compile nil '(lambda (x)
+ (declare (type (double-float 0.0d0 1.0d0) x))
+ (truncate x)))))
+ ;; Check that there is no generic arithmetic
+ (assert (not (search "GENERIC"
+ (with-output-to-string (out)
+ (disassemble s :stream out)))))
+ (assert (not (search "GENERIC"
+ (with-output-to-string (out)
+ (disassemble d :stream out)))))
+ ;; Check that we actually inlined the call when we were supposed to.
+ (assert (not (search "UNARY-TRUNCATE"
+ (with-output-to-string (out)
+ (disassemble s-inlined :stream out)))))
+ (assert (not (search "UNARY-TRUNCATE"
+ (with-output-to-string (out)
+ (disassemble d-inlined :stream out)))))))
+
+(with-test (:name :make-array-unnamed-dimension-leaf)
+ (let ((fun (compile nil `(lambda (stuff)
+ (make-array (map 'list 'length stuff))))))
+ (assert (equalp #2A((0 0 0) (0 0 0))
+ (funcall fun '((1 2) (1 2 3)))))))
+
+(with-test (:name :fp-decoding-funs-not-flushable-in-safe-code)
+ (dolist (name '(float-sign float-radix float-digits float-precision decode-float
+ integer-decode-float))
+ (let ((fun (compile nil `(lambda (x)
+ (declare (optimize safety))
+ (,name x)
+ nil))))
+ (flet ((test (arg)
+ (unless (eq :error
+ (handler-case
+ (funcall fun arg)
+ (error () :error)))
+ (error "(~S ~S) did not error"
+ name arg))))
+ ;; No error
+ (funcall fun 1.0)
+ ;; Error
+ (test 'not-a-float)
+ (when (member name '(decode-float integer-decode-float))
+ (test sb-ext:single-float-positive-infinity))))))
+
+(with-test (:name :sap-ref-16)
+ (let* ((fun (compile nil `(lambda (x y)
+ (declare (type sb-sys:system-area-pointer x)
+ (type (integer 0 100) y))
+ (sb-sys:sap-ref-16 x (+ 4 y)))))
+ (vector (coerce '(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)
+ '(simple-array (unsigned-byte 8) (*))))
+ (sap (sb-sys:vector-sap vector))
+ (ret (funcall fun sap 0)))
+ ;; test for either endianness
+ (assert (or (= ret (+ (* 5 256) 4)) (= ret (+ (* 4 256) 5))))))
+
+(with-test (:name :coerce-type-warning)
+ (dolist (type '(t (unsigned-byte 8) (unsigned-byte 16) (unsigned-byte 32)
+ (signed-byte 8) (signed-byte 16) (signed-byte 32)))
+ (multiple-value-bind (fun warningsp failurep)
+ (compile nil `(lambda (x)
+ (declare (type simple-vector x))
+ (coerce x '(vector ,type))))
+ (assert (null warningsp))
+ (assert (null failurep))
+ (assert (typep (funcall fun #(1)) `(simple-array ,type (*)))))))
+
+(with-test (:name :truncate-double-float)
+ (let ((fun (compile nil `(lambda (x)
+ (multiple-value-bind (q r)
+ (truncate (coerce x 'double-float))
+ (declare (type unsigned-byte q)
+ (type double-float r))
+ (list q r))))))
+ (assert (equal (funcall fun 1.0d0) '(1 0.0d0)))))
+
+(with-test (:name :set-slot-value-no-warning)
+ (let ((notes 0))
+ (handler-bind ((warning #'error)
+ (sb-ext:compiler-note (lambda (c)
+ (declare (ignore c))
+ (incf notes))))
+ (compile nil `(lambda (x y)
+ (declare (optimize speed safety))
+ (setf (slot-value x 'bar) y))))
+ (assert (= 1 notes))))
+
+(with-test (:name :concatenate-string-opt)
+ (flet ((test (type grep)
+ (let* ((fun (compile nil `(lambda (a b c d e)
+ (concatenate ',type a b c d e))))
+ (args '("foo" #(#\.) "bar" (#\-) "quux"))
+ (res (apply fun args)))
+ (assert (search grep (with-output-to-string (out)
+ (disassemble fun :stream out))))
+ (assert (equal (apply #'concatenate type args)
+ res))
+ (assert (typep res type)))))
+ (test 'string "%CONCATENATE-TO-STRING")
+ (test 'simple-string "%CONCATENATE-TO-STRING")
+ (test 'base-string "%CONCATENATE-TO-BASE-STRING")
+ (test 'simple-base-string "%CONCATENATE-TO-BASE-STRING")))
+
+(with-test (:name :satisfies-no-local-fun)
+ (let ((fun (compile nil `(lambda (arg)
+ (labels ((local-not-global-bug (x)
+ t)
+ (bar (x)
+ (typep x '(satisfies local-not-global-bug))))
+ (bar arg))))))
+ (assert (eq 'local-not-global-bug
+ (handler-case
+ (funcall fun 42)
+ (undefined-function (c)
+ (cell-error-name c)))))))
+
+;;; Prior to 1.0.32.x, dumping a fasl with a function with a default
+;;; argument that is a complex structure (needing make-load-form
+;;; processing) failed an AVER. The first attempt at a fix caused
+;;; doing the same in-core to break.
+(with-test (:name :bug-310132)
+ (compile nil '(lambda (&optional (foo #p"foo/bar")))))
+
+(with-test (:name :bug-309129)
+ (let* ((src '(lambda (v) (values (svref v 0) (vector-pop v))))
+ (warningp nil)
+ (fun (handler-bind ((warning (lambda (c)
+ (setf warningp t) (muffle-warning c))))
+ (compile nil src))))
+ (assert warningp)
+ (handler-case (funcall fun #(1))
+ (type-error (c)
+ ;; we used to put simply VECTOR into EXPECTED-TYPE, rather
+ ;; than explicitly (AND VECTOR (NOT SIMPLE-ARRAY))
+ (assert (not (typep (type-error-datum c) (type-error-expected-type c)))))
+ (:no-error (&rest values)
+ (declare (ignore values))
+ (error "no error")))))
+
+(with-test (:name :unary-round-type-derivation)
+ (let* ((src '(lambda (zone)
+ (multiple-value-bind (h m) (truncate (abs zone) 1.0)
+ (declare (ignore h))
+ (round (* 60.0 m)))))
+ (fun (compile nil src)))
+ (assert (= (funcall fun 0.5) 30))))
+
+(with-test (:name :bug-525949)
+ (let* ((src '(lambda ()
+ (labels ((always-one () 1)
+ (f (z)
+ (let ((n (funcall z)))
+ (declare (fixnum n))
+ (the double-float (expt n 1.0d0)))))
+ (f #'always-one))))
+ (warningp nil)
+ (fun (handler-bind ((warning (lambda (c)
+ (setf warningp t) (muffle-warning c))))
+ (compile nil src))))
+ (assert (not warningp))
+ (assert (= 1.0d0 (funcall fun)))))
+
+(with-test (:name :%array-data-vector-type-derivation)
+ (let* ((f (compile nil
+ `(lambda (ary)
+ (declare (type (simple-array (unsigned-byte 32) (3 3)) ary))
+ (setf (aref ary 0 0) 0))))
+ (text (with-output-to-string (s)
+ (disassemble f :stream s))))
+ (assert (not (search "OBJECT-NOT-SIMPLE-ARRAY-UNSIGNED-BYTE-32-ERROR" text)))))
+
+(with-test (:name :array-storage-vector-type-derivation)
+ (let ((f (compile nil
+ `(lambda (ary)
+ (declare (type (simple-array (unsigned-byte 32) (3 3)) ary))
+ (ctu:compiler-derived-type (array-storage-vector ary))))))
+ (assert (equal '(simple-array (unsigned-byte 32) (9))
+ (funcall f (make-array '(3 3) :element-type '(unsigned-byte 32)))))))
+
+(with-test (:name :bug-523612)
+ (let ((fun
+ (compile nil
+ `(lambda (&key toff)
+ (make-array 3 :element-type 'double-float
+ :initial-contents
+ (if toff (list toff 0d0 0d0) (list 0d0 0d0 0d0)))))))
+ (assert (equalp (vector 0.0d0 0.0d0 0.0d0) (funcall fun :toff nil)))
+ (assert (equalp (vector 2.3d0 0.0d0 0.0d0) (funcall fun :toff 2.3d0)))))
+
+(with-test (:name :bug-309788)
+ (let ((fun
+ (compile nil
+ `(lambda (x)
+ (declare (optimize speed))
+ (let ((env nil))
+ (typep x 'fixnum env))))))
+ (assert (not (ctu:find-named-callees fun)))))
+
+(with-test (:name :bug-309124)
+ (let ((fun
+ (compile nil
+ `(lambda (x)
+ (declare (integer x))
+ (declare (optimize speed))
+ (cond ((typep x 'fixnum)
+ "hala")
+ ((typep x 'fixnum)
+ "buba")
+ ((typep x 'bignum)
+ "hip")
+ (t
+ "zuz"))))))
+ (assert (equal (list "hala" "hip")
+ (sort (ctu:find-code-constants fun :type 'string)
+ #'string<)))))
+
+(with-test (:name :bug-316078)
+ (let ((fun
+ (compile nil
+ `(lambda (x)
+ (declare (type (and simple-bit-vector (satisfies bar)) x)
+ (optimize speed))
+ (elt x 5)))))
+ (assert (not (ctu:find-named-callees fun)))
+ (assert (= 1 (funcall fun #*000001)))
+ (assert (= 0 (funcall fun #*000010)))))
+
+(with-test (:name :mult-by-one-in-float-acc-zero)
+ (assert (eql 1.0 (funcall (compile nil `(lambda (x)
+ (declare (optimize (sb-c::float-accuracy 0)))
+ (* x 1.0)))
+ 1)))
+ (assert (eql -1.0 (funcall (compile nil `(lambda (x)
+ (declare (optimize (sb-c::float-accuracy 0)))
+ (* x -1.0)))
+ 1)))
+ (assert (eql 1.0d0 (funcall (compile nil `(lambda (x)
+ (declare (optimize (sb-c::float-accuracy 0)))
+ (* x 1.0d0)))
+ 1)))
+ (assert (eql -1.0d0 (funcall (compile nil `(lambda (x)
+ (declare (optimize (sb-c::float-accuracy 0)))
+ (* x -1.0d0)))
+ 1))))
+
+(with-test (:name :dotimes-non-integer-counter-value)
+ (assert (raises-error? (dotimes (i 8.6)) type-error)))
+
+(with-test (:name :bug-454681)
+ ;; This used to break due to reference to a dead lambda-var during
+ ;; inline expansion.
+ (assert (compile nil
+ `(lambda ()
+ (multiple-value-bind (iterator+977 getter+978)
+ (does-not-exist-but-does-not-matter)
+ (flet ((iterator+976 ()
+ (funcall iterator+977)))
+ (declare (inline iterator+976))
+ (let ((iterator+976 #'iterator+976))
+ (funcall iterator+976))))))))
+
+(with-test (:name :complex-float-local-fun-args)
+ ;; As of 1.0.27.14, the lambda below failed to compile due to the
+ ;; compiler attempting to pass unboxed complex floats to Z and the
+ ;; MOVE-ARG method not expecting the register being used as a
+ ;; temporary frame pointer. Reported by sykopomp in #lispgames,
+ ;; reduced test case provided by _3b`.
+ (compile nil '(lambda (a)
+ (labels ((z (b c)
+ (declare ((complex double-float) b c))
+ (* b (z b c))))
+ (loop for i below 10 do
+ (setf a (z a a)))))))
+
+(with-test (:name :bug-309130)
+ (assert (eq :warning
+ (handler-case
+ (compile nil `(lambda () (svref (make-array 8 :adjustable t) 1)))
+ ((and warning (not style-warning)) ()
+ :warning))))
+ (assert (eq :warning
+ (handler-case
+ (compile nil `(lambda (x)
+ (declare (optimize (debug 0)))
+ (declare (type vector x))
+ (list (fill-pointer x) (svref x 1))))
+ ((and warning (not style-warning)) ()
+ :warning))))
+ (assert (eq :warning
+ (handler-case
+ (compile nil `(lambda (x)
+ (list (vector-push (svref x 0) x))))
+ ((and warning (not style-warning)) ()
+ :warning))))
+ (assert (eq :warning
+ (handler-case
+ (compile nil `(lambda (x)
+ (list (vector-push-extend (svref x 0) x))))
+ ((and warning (not style-warning)) ()
+ :warning)))))
+
+(with-test (:name :bug-646796)
+ (assert 42
+ (funcall
+ (compile nil
+ `(lambda ()
+ (load-time-value (the (values fixnum) 42)))))))
+
+(with-test (:name :bug-654289)
+ ;; Test that compile-times don't explode when quoted constants
+ ;; get big.
+ (labels ((time-n (n)
+ (gc :full t) ; Let's not confuse the issue with GC
+ (let* ((tree (make-tree (expt 10 n) nil))
+ (t0 (get-internal-run-time))
+ (f (compile nil `(lambda (x) (eq x (quote ,tree)))))
+ (t1 (get-internal-run-time)))
+ (assert (funcall f tree))
+ (- t1 t0)))
+ (make-tree (n acc)
+ (cond ((zerop n) acc)
+ (t (make-tree (1- n) (cons acc acc))))))
+ (let* ((times (loop for i from 0 upto 4
+ collect (time-n i)))
+ (max-small (reduce #'max times :end 3))
+ (max-big (reduce #'max times :start 3)))
+ ;; This way is hopefully fairly CPU-performance insensitive.
+ (unless (> (+ (truncate internal-time-units-per-second 10)
+ (* 2 max-small))
+ max-big)
+ (error "Bad scaling or test? ~S" times)))))
+
+(with-test (:name :bug-309063)
+ (let ((fun (compile nil `(lambda (x)
+ (declare (type (integer 0 0) x))
+ (ash x 100)))))
+ (assert (zerop (funcall fun 0)))))
+
+(with-test (:name :bug-655872)
+ (let ((f (compile nil `(lambda (x)
+ (declare (optimize (safety 3)))
+ (aref (locally (declare (optimize (safety 0)))
+ (coerce x '(simple-vector 128)))
+ 60))))
+ (long (make-array 100 :element-type 'fixnum)))
+ (dotimes (i 100)
+ (setf (aref long i) i))
+ ;; 1. COERCE doesn't check the length in unsafe code.
+ (assert (eql 60 (funcall f long)))
+ ;; 2. The compiler doesn't trust the length from COERCE
+ (assert (eq :caught
+ (handler-case
+ (funcall f (list 1 2 3))
+ (sb-int:invalid-array-index-error (e)
+ (assert (eql 60 (type-error-datum e)))
+ (assert (equal '(integer 0 (3)) (type-error-expected-type e)))
+ :caught))))))
+
+(with-test (:name :bug-655203-regression)
+ (let ((fun (compile nil
+ `(LAMBDA (VARIABLE)
+ (LET ((CONTINUATION
+ (LAMBDA
+ (&OPTIONAL DUMMY &REST OTHER)
+ (DECLARE (IGNORE OTHER))
+ (PRIN1 DUMMY)
+ (PRIN1 VARIABLE))))
+ (FUNCALL CONTINUATION (LIST 1 2)))))))
+ ;; This used to signal a bogus type-error.
+ (assert (equal (with-output-to-string (*standard-output*)
+ (funcall fun t))
+ "(1 2)T"))))
+
+(with-test (:name :constant-concatenate-compile-time)
+ (flet ((make-lambda (n)
+ `(lambda (x)
+ (declare (optimize (speed 3) (space 0)))
+ (concatenate 'string x ,(make-string n)))))
+ (let* ((l0 (make-lambda 1))
+ (l1 (make-lambda 10))
+ (l2 (make-lambda 100))
+ (l3 (make-lambda 1000))
+ (t0 (get-internal-run-time))
+ (f0 (compile nil l0))
+ (t1 (get-internal-run-time))
+ (f1 (compile nil l1))
+ (t2 (get-internal-run-time))
+ (f2 (compile nil l2))
+ (t3 (get-internal-run-time))
+ (f3 (compile nil l3))
+ (t4 (get-internal-run-time))
+ (d0 (- t1 t0))
+ (d1 (- t2 t1))
+ (d2 (- t3 t2))
+ (d3 (- t4 t3))
+ (short-avg (/ (+ d0 d1 d2) 3)))
+ (assert (and f1 f2 f3))
+ (assert (< d3 (* 10 short-avg))))))
+
+(with-test (:name :bug-384892)
+ (assert (equal
+ '(function (fixnum fixnum &key (:k1 (member nil t)))
+ (values (member t) &optional))
+ (sb-kernel:%simple-fun-type
+ (compile nil `(lambda (x y &key k1)
+ (declare (fixnum x y))
+ (declare (boolean k1))
+ (declare (ignore x y k1))
+ t))))))
+
+(with-test (:name :bug-309448)
+ ;; Like all tests trying to verify that something doesn't blow up
+ ;; compile-times this is bound to be a bit brittle, but at least
+ ;; here we try to establish a decent baseline.
+ (flet ((time-it (lambda want)
+ (gc :full t) ; let's keep GCs coming from other code out...
+ (let* ((start (get-internal-run-time))
+ (fun (dotimes (internal-time-resolution-too-low-workaround
+ #+win32 10
+ #-win32 0
+ (compile nil lambda))
+ (compile nil lambda)))
+ (end (get-internal-run-time))
+ (got (funcall fun)))
+ (unless (eql want got)
+ (error "wanted ~S, got ~S" want got))
+ (- end start))))
+ (let ((time-1/simple
+ ;; This is mostly identical as the next one, but doesn't create
+ ;; hairy unions of numeric types.
+ (time-it `(lambda ()
+ (labels ((bar (baz bim)
+ (let ((n (+ baz bim)))
+ (* n (+ n 1) bim))))
+ (let ((a (bar 1 1))
+ (b (bar 1 1))
+ (c (bar 1 1)))
+ (- (+ a b) c))))
+ 6))
+ (time-1/hairy
+ (time-it `(lambda ()
+ (labels ((bar (baz bim)
+ (let ((n (+ baz bim)))
+ (* n (+ n 1) bim))))
+ (let ((a (bar 1 1))
+ (b (bar 1 5))
+ (c (bar 1 15)))
+ (- (+ a b) c))))
+ -3864)))
+ (assert (>= (* 10 (1+ time-1/simple)) time-1/hairy)))
+ (let ((time-2/simple
+ ;; This is mostly identical as the next one, but doesn't create
+ ;; hairy unions of numeric types.
+ (time-it `(lambda ()
+ (labels ((sum-d (n)
+ (let ((m (truncate 999 n)))
+ (/ (* n m (1+ m)) 2))))
+ (- (+ (sum-d 3)
+ (sum-d 3))
+ (sum-d 3))))
+ 166833))
+ (time-2/hairy
+ (time-it `(lambda ()
+ (labels ((sum-d (n)
+ (let ((m (truncate 999 n)))
+ (/ (* n m (1+ m)) 2))))
+ (- (+ (sum-d 3)
+ (sum-d 5))
+ (sum-d 15))))
+ 233168)))
+ (assert (>= (* 10 (1+ time-2/simple)) time-2/hairy)))))
+
+(with-test (:name :regression-1.0.44.34)
+ (compile nil '(lambda (z &rest args)
+ (declare (dynamic-extent args))
+ (flet ((foo (w v) (list v w)))
+ (setq z 0)
+ (flet ((foo ()
+ (foo z args)))
+ (declare (sb-int:truly-dynamic-extent #'foo))
+ (call #'foo nil))))))
+
+(with-test (:name :bug-713626)
+ (let ((f (eval '(constantly 42))))
+ (handler-bind ((warning #'error))
+ (assert (= 42 (funcall (compile nil `(lambda () (funcall ,f 1 2 3)))))))))
+
+(with-test (:name :known-fun-allows-other-keys)
+ (handler-bind ((warning #'error))
+ (funcall (compile nil '(lambda () (directory "." :allow-other-keys t))))
+ (funcall (compile nil `(lambda () (directory "." :bar t :allow-other-keys t))))))
+
+(with-test (:name :bug-551227)
+ ;; This function causes constraint analysis to perform a
+ ;; ref-substitution that alters the A referred to in (G A) at in the
+ ;; consequent of the IF to refer to be NUMBER, from the
+ ;; LET-converted inline-expansion of MOD. This leads to attempting
+ ;; to CLOSE-OVER a variable that simply isn't in scope when it is
+ ;; referenced.
+ (compile nil '(lambda (a)
+ (if (let ((s a))
+ (block :block
+ (map nil
+ (lambda (e)
+ (return-from :block
+ (f (mod a e))))
+ s)))
+ (g a)))))
+
+(with-test (:name :funcall-lambda-inlined)
+ (assert (not
+ (ctu:find-code-constants
+ (compile nil
+ `(lambda (x y)
+ (+ x (funcall (lambda (z) z) y))))
+ :type 'function))))
+
+(with-test (:name :bug-720382)
+ (let ((w 0))
+ (let ((f
+ (handler-bind (((and warning (not style-warning))
+ (lambda (c) (incf w))))
+ (compile nil `(lambda (b) ((lambda () b) 1))))))
+ (assert (= w 1))
+ (assert (eq :error
+ (handler-case (funcall f 0)
+ (error () :error)))))))
+
+(with-test (:name :multiple-args-to-function)
+ (let ((form `(flet ((foo (&optional (x 13)) x))
+ (funcall (function foo 42))))
+ #+sb-eval (*evaluator-mode* :interpret))
+ #+sb-eval
+ (assert (eq :error
+ (handler-case (eval form)
+ (error () :error))))
+ (multiple-value-bind (fun warn fail)
+ (compile nil `(lambda () ,form))
+ (assert (and warn fail))
+ (assert (eq :error
+ (handler-case (funcall fun)
+ (error () :error)))))))
+
+;;; This doesn't test LVAR-FUN-IS directly, but captures it
+;;; pretty accurately anyways.
+(with-test (:name :lvar-fun-is)
+ (dolist (fun (list
+ (lambda (x) (member x x :test #'eq))
+ (lambda (x) (member x x :test 'eq))
+ (lambda (x) (member x x :test #.#'eq))))
+ (assert (equal (list #'sb-kernel:%member-eq)
+ (ctu:find-named-callees fun))))
+ (dolist (fun (list
+ (lambda (x)
+ (declare (notinline eq))
+ (member x x :test #'eq))
+ (lambda (x)
+ (declare (notinline eq))
+ (member x x :test 'eq))
+ (lambda (x)
+ (declare (notinline eq))
+ (member x x :test #.#'eq))))
+ (assert (member #'sb-kernel:%member-test
+ (ctu:find-named-callees fun)))))
+
+(with-test (:name :delete-to-delq-opt)
+ (dolist (fun (list (lambda (x y)
+ (declare (list y))
+ (delete x y :test #'eq))
+ (lambda (x y)
+ (declare (fixnum x) (list y))
+ (delete x y))
+ (lambda (x y)
+ (declare (symbol x) (list y))
+ (delete x y :test #'eql))))
+ (assert (equal (list #'sb-int:delq)
+ (ctu:find-named-callees fun)))))
+
+(with-test (:name :bug-767959)
+ ;; This used to signal an error.
+ (compile nil `(lambda ()
+ (declare (optimize sb-c:store-coverage-data))
+ (assoc
+ nil
+ '((:ordinary . ordinary-lambda-list))))))
+
+(with-test (:name :member-on-long-constant-list)
+ ;; This used to blow stack with a sufficiently long list.
+ (let ((cycle (list t)))
+ (nconc cycle cycle)
+ (compile nil `(lambda (x)
+ (member x ',cycle)))))
+
+(with-test (:name :bug-722734)
+ (assert (raises-error?
+ (funcall (compile
+ nil
+ '(lambda ()
+ (eql (make-array 6)
+ (list unbound-variable-1 unbound-variable-2))))))))
+
+(with-test (:name :bug-771673)
+ (assert (equal `(the foo bar) (macroexpand `(truly-the foo bar))))
+ ;; Make sure the compiler doesn't use THE, and check that setf-expansions
+ ;; work.
+ (let ((f (compile nil `(lambda (x y)
+ (setf (truly-the fixnum (car x)) y)))))
+ (let* ((cell (cons t t)))
+ (funcall f cell :ok)
+ (assert (equal '(:ok . t) cell)))))
+
+(with-test (:name (:bug-793771 +))
+ (let ((f (compile nil `(lambda (x y)
+ (declare (type (single-float 2.0) x)
+ (type (single-float (0.0)) y))
+ (+ x y)))))
+ (assert (equal `(function ((single-float 2.0) (single-float (0.0)))
+ (values (single-float 2.0) &optional))
+ (sb-kernel:%simple-fun-type f)))))
+
+(with-test (:name (:bug-793771 -))
+ (let ((f (compile nil `(lambda (x y)
+ (declare (type (single-float * 2.0) x)
+ (type (single-float (0.0)) y))
+ (- x y)))))
+ (assert (equal `(function ((single-float * 2.0) (single-float (0.0)))
+ (values (single-float * 2.0) &optional))
+ (sb-kernel:%simple-fun-type f)))))
+
+(with-test (:name (:bug-793771 *))
+ (let ((f (compile nil `(lambda (x)
+ (declare (type (single-float (0.0)) x))
+ (* x 0.1)))))
+ (assert (equal `(function ((single-float (0.0)))
+ (values (or (member 0.0) (single-float (0.0))) &optional))
+ (sb-kernel:%simple-fun-type f)))))
+
+(with-test (:name (:bug-793771 /))
+ (let ((f (compile nil `(lambda (x)
+ (declare (type (single-float (0.0)) x))
+ (/ x 3.0)))))
+ (assert (equal `(function ((single-float (0.0)))
+ (values (or (member 0.0) (single-float (0.0))) &optional))
+ (sb-kernel:%simple-fun-type f)))))
+
+(with-test (:name (:bug-486812 single-float))
+ (compile nil `(lambda ()
+ (sb-kernel:make-single-float -1))))
+
+(with-test (:name (:bug-486812 double-float))
+ (compile nil `(lambda ()
+ (sb-kernel:make-double-float -1 0))))
+
+(with-test (:name :bug-729765)
+ (compile nil `(lambda (a b)
+ (declare ((integer 1 1) a)
+ ((integer 0 1) b)
+ (optimize debug))
+ (lambda () (< b a)))))
+
+;; Actually tests the assembly of RIP-relative operands to comparison
+;; functions (one of the few x86 instructions that have extra bytes
+;; *after* the mem operand's effective address, resulting in a wrong
+;; offset).
+(with-test (:name :cmpps)
+ (let ((foo (compile nil `(lambda (x)
+ (= #C(2.0 3.0) (the (complex single-float) x))))))
+ (assert (funcall foo #C(2.0 3.0)))
+ (assert (not (funcall foo #C(1.0 2.0))))))
+
+(with-test (:name :cmppd)
+ (let ((foo (compile nil `(lambda (x)
+ (= #C(2d0 3d0) (the (complex double-float) x))))))
+ (assert (funcall foo #C(2d0 3d0)))
+ (assert (not (funcall foo #C(1d0 2d0))))))
+
+(with-test (:name :lvar-externally-checkable-type-nil)
+ ;; Used to signal a BUG during compilation.
+ (let ((fun (compile nil `(lambda (a) (parse-integer "12321321" (the (member :start) a) 1)))))
+ (multiple-value-bind (i p) (funcall fun :start)
+ (assert (= 2321321 i))
+ (assert (= 8 p)))
+ (multiple-value-bind (i e) (ignore-errors (funcall fun :end))
+ (assert (not i))
+ (assert (typep e 'type-error)))))
+
+(with-test (:name :simple-type-error-in-bound-propagation-a)
+ (compile nil `(lambda (i)
+ (declare (unsigned-byte i))
+ (expt 10 (expt 7 (- 2 i))))))
+
+(with-test (:name :simple-type-error-in-bound-propagation-b)
+ (assert (equal `(FUNCTION (UNSIGNED-BYTE)
+ (VALUES (SINGLE-FLOAT -1F0 1F0) &OPTIONAL))
+ (sb-kernel:%simple-fun-type
+ (compile nil `(lambda (i)
+ (declare (unsigned-byte i))
+ (cos (expt 10 (+ 4096 i)))))))))
+
+(with-test (:name :fixed-%more-arg-values)
+ (let ((fun (compile nil `(lambda (&rest rest)
+ (declare (optimize (safety 0)))
+ (apply #'cons rest)))))
+ (assert (equal '(car . cdr) (funcall fun 'car 'cdr)))))
+
+(with-test (:name :bug-826970)
+ (let ((fun (compile nil `(lambda (a b c)
+ (declare (type (member -2 1) b))
+ (array-in-bounds-p a 4 b c)))))
+ (assert (funcall fun (make-array '(5 2 2)) 1 1))))
+
+(with-test (:name :bug-826971)
+ (let* ((foo "foo")
+ (fun (compile nil `(lambda (p1 p2)
+ (schar (the (eql ,foo) p1) p2)))))
+ (assert (eql #\f (funcall fun foo 0)))))
+
+(with-test (:name :bug-738464)
+ (multiple-value-bind (fun warn fail)
+ (compile nil `(lambda ()
+ (flet ((foo () 42))
+ (declare (ftype non-function-type foo))
+ (foo))))
+ (assert (eql 42 (funcall fun)))
+ (assert (and warn (not fail)))))
+
+(with-test (:name :bug-832005)
+ (let ((fun (compile nil `(lambda (x)
+ (declare (type (complex single-float) x))
+ (+ #C(0.0 1.0) x)))))
+ (assert (= (funcall fun #C(1.0 2.0))
+ #C(1.0 3.0)))))
+
+;; A refactoring 1.0.12.18 caused lossy computation of primitive
+;; types for member types.
+(with-test (:name :member-type-primitive-type)
+ (let ((fun (compile nil `(lambda (p1 p2 p3)
+ (if p1
+ (the (member #c(1.2d0 1d0)) p2)
+ (the (eql #c(1.0 1.0)) p3))))))
+ (assert (eql (funcall fun 1 #c(1.2d0 1d0) #c(1.0 1.0))
+ #c(1.2d0 1.0d0)))))
+
+;; Fall-through jump elimination made control flow fall through to trampolines.
+;; Reported by Eric Marsden on sbcl-devel@ 2011.10.26, with a test case
+;; reproduced below (triggered a corruption warning and a memory fault).
+(with-test (:name :bug-883500)
+ (funcall (compile nil `(lambda (a)
+ (declare (type (integer -50 50) a))
+ (declare (optimize (speed 0)))
+ (mod (mod a (min -5 a)) 5)))
+ 1))
+
+;; Test for literals too large for the ISA (e.g. (SIGNED-BYTE 13) on SPARC).
+#+sb-unicode
+(with-test (:name :bug-883519)
+ (compile nil `(lambda (x)
+ (declare (type character x))
+ (eql x #\U0010FFFF))))
+
+;; Wide fixnum platforms had buggy address computation in atomic-incf/aref
+(with-test (:name :bug-887220)
+ (let ((incfer (compile
+ nil
+ `(lambda (vector index)
+ (declare (type (simple-array sb-ext:word (4))
+ vector)
+ (type (mod 4) index))
+ (sb-ext:atomic-incf (aref vector index) 1)
+ vector))))
+ (assert (equalp (funcall incfer
+ (make-array 4 :element-type 'sb-ext:word
+ :initial-element 0)
+ 1)
+ #(0 1 0 0)))))
+
+(with-test (:name :catch-interferes-with-debug-names)
+ (let ((fun (funcall
+ (compile nil
+ `(lambda ()
+ (catch 'out
+ (flet ((foo ()
+ (throw 'out (lambda () t))))
+ (foo))))))))
+ (assert (equal '(lambda () :in foo) (sb-kernel:%fun-name fun)))))
+
+(with-test (:name :interval-div-signed-zero)
+ (let ((fun (compile nil
+ `(Lambda (a)
+ (declare (type (member 0 -272413371076) a))
+ (ffloor (the number a) -63243.127451934015d0)))))
+ (multiple-value-bind (q r) (funcall fun 0)
+ (assert (eql -0d0 q))
+ (assert (eql 0d0 r)))))
+
+(with-test (:name :non-constant-keyword-typecheck)
+ (let ((fun (compile nil
+ `(lambda (p1 p3 p4)
+ (declare (type keyword p3))
+ (tree-equal p1 (cons 1 2) (the (member :test) p3) p4)))))
+ (assert (funcall fun (cons 1.0 2.0) :test '=))))
+
+(with-test (:name :truncate-wild-values)
+ (multiple-value-bind (q r)
+ (handler-bind ((warning #'error))
+ (let ((sb-c::*check-consistency* t))
+ (funcall (compile nil
+ `(lambda (a)
+ (declare (type (member 1d0 2d0) a))
+ (block return-value-tag
+ (funcall
+ (the function
+ (catch 'debug-catch-tag
+ (return-from return-value-tag
+ (progn (truncate a)))))))))
+ 2d0)))
+ (assert (eql 2 q))
+ (assert (eql 0d0 r))))
+
+(with-test (:name :boxed-fp-constant-for-full-call)
+ (let ((fun (compile nil
+ `(lambda (x)
+ (declare (double-float x))
+ (unknown-fun 1.0d0 (+ 1.0d0 x))))))
+ (assert (equal '(1.0d0) (ctu:find-code-constants fun :type 'double-float)))))
+
+(with-test (:name :only-one-boxed-constant-for-multiple-uses)
+ (let* ((big (1+ most-positive-fixnum))
+ (fun (compile nil
+ `(lambda (x)
+ (unknown-fun ,big (+ ,big x))))))
+ (assert (= 1 (length (ctu:find-code-constants fun :type `(eql ,big)))))))
+
+(with-test (:name :fixnum+float-coerces-fixnum
+ :skipped-on :x86)
+ (let ((fun (compile nil
+ `(lambda (x y)
+ (declare (fixnum x)
+ (single-float y))
+ (+ x y)))))
+ (assert (not (ctu:find-named-callees fun)))
+ (assert (not (search "GENERIC"
+ (with-output-to-string (s)
+ (disassemble fun :stream s)))))))
+
+(with-test (:name :bug-803508)
+ (compile nil `(lambda ()
+ (print
+ (lambda (bar)
+ (declare (dynamic-extent bar))
+ (foo bar))))))
+
+(with-test (:name :bug-803508-b)
+ (compile nil `(lambda ()
+ (list
+ (lambda (bar)
+ (declare (dynamic-extent bar))
+ (foo bar))))))
+
+(with-test (:name :bug-803508-c)
+ (compile nil `(lambda ()
+ (list
+ (lambda (bar &optional quux)
+ (declare (dynamic-extent bar quux))
+ (foo bar quux))))))
+
+(with-test (:name :cprop-with-constant-but-assigned-to-closure-variable)
+ (compile nil `(lambda (b c d)
+ (declare (type (integer -20545789 207590862) c))
+ (declare (type (integer -1 -1) d))
+ (let ((i (unwind-protect 32 (shiftf d -1))))
+ (or (if (= d c) 2 (= 3 b)) 4)))))
+
+(with-test (:name :bug-913232)
+ (compile nil `(lambda (x)
+ (declare (optimize speed)
+ (type (or (and (or (integer -100 -50)
+ (integer 100 200)) (satisfies foo))
+ (and (or (integer 0 10) (integer 20 30)) a)) x))
+ x))
+ (compile nil `(lambda (x)
+ (declare (optimize speed)
+ (type (and fixnum a) x))
+ x)))
+
+(with-test (:name :bug-959687)
+ (multiple-value-bind (fun warn fail)
+ (compile nil `(lambda (x)
+ (case x
+ (t
+ :its-a-t)
+ (otherwise
+ :somethign-else))))
+ (assert (and warn fail))
+ (assert (not (ignore-errors (funcall fun t)))))
+ (multiple-value-bind (fun warn fail)
+ (compile nil `(lambda (x)
+ (case x
+ (otherwise
+ :its-an-otherwise)
+ (t
+ :somethign-else))))
+ (assert (and warn fail))
+ (assert (not (ignore-errors (funcall fun t))))))
+
+(with-test (:name :bug-924276)
+ (assert (eq :style-warning
+ (handler-case
+ (compile nil `(lambda (a)
+ (cons a (symbol-macrolet ((b 1))
+ (declare (ignorable a))
+ :c))))
+ (style-warning ()
+ :style-warning)))))
+
+(with-test (:name :bug-974406)
+ (let ((fun32 (compile nil `(lambda (x)
+ (declare (optimize speed (safety 0)))
+ (declare (type (integer 53 86) x))
+ (logand (+ x 1032791128) 11007078467))))
+ (fun64 (compile nil `(lambda (x)
+ (declare (optimize speed (safety 0)))
+ (declare (type (integer 53 86) x))
+ (logand (+ x 1152921504606846975)
+ 38046409652025950207)))))
+ (assert (= (funcall fun32 61) 268574721))
+ (assert (= (funcall fun64 61) 60)))
+ (let (result)
+ (do ((width 5 (1+ width)))
+ ((= width 130))
+ (dotimes (extra 4)
+ (let ((fun (compile nil `(lambda (x)
+ (declare (optimize speed (safety 0)))
+ (declare (type (integer 1 16) x))
+ (logand
+ (+ x ,(1- (ash 1 width)))
+ ,(logior (ash 1 (+ width 1 extra))
+ (1- (ash 1 width))))))))
+ (unless (= (funcall fun 16) (logand 15 (1- (ash 1 width))))
+ (push (cons width extra) result)))))
+ (assert (null result))))
+
+;; On x86-64 MOVE-IMMEDIATE of fixnum values into memory either directly
+;; uses a MOV into memory or goes through a temporary register if the
+;; value is larger than a certain number of bits. Check that it respects
+;; the limits of immediate arguments to the MOV instruction (if not, the
+;; assembler will fail an assertion) and doesn't have sign-extension
+;; problems. (The test passes fixnum constants through the MOVE VOP
+;; which calls MOVE-IMMEDIATE.)
+(with-test (:name :constant-fixnum-move)
+ (let ((f (compile nil `(lambda (g)
+ (funcall g
+ ;; The first three args are
+ ;; uninteresting as they are
+ ;; passed in registers.
+ 1 2 3
+ ,@(loop for i from 27 to 32
+ collect (expt 2 i)))))))
+ (assert (every #'plusp (funcall f #'list)))))
+
+(with-test (:name (:malformed-ignore :lp-1000239))
+ (raises-error?
+ (eval '(lambda () (declare (ignore (function . a)))))
+ sb-int:compiled-program-error)
+ (raises-error?
+ (eval '(lambda () (declare (ignore (function a b)))))
+ sb-int:compiled-program-error)
+ (raises-error?
+ (eval '(lambda () (declare (ignore (function)))))
+ sb-int:compiled-program-error)
+ (raises-error?
+ (eval '(lambda () (declare (ignore (a)))))
+ sb-int:compiled-program-error)
+ (raises-error?
+ (eval '(lambda () (declare (ignorable (a b)))))
+ sb-int:compiled-program-error))
+
+(with-test (:name :malformed-type-declaraions)
+ (compile nil '(lambda (a) (declare (type (integer 1 2 . 3) a)))))
+
+(with-test (:name :compiled-program-error-escaped-source)
+ (assert
+ (handler-case
+ (funcall (compile nil `(lambda () (lambda ("foo")))))
+ (sb-int:compiled-program-error (e)
+ (let ((source (read-from-string (sb-kernel::program-error-source e))))
+ (equal source '#'(lambda ("foo"))))))))
+
+(with-test (:name :escape-analysis-for-nlxs)
+ (flet ((test (check lambda &rest args)
+ (let* ((cell-note nil)
+ (fun (handler-bind ((compiler-note
+ (lambda (note)
+ (when (search
+ "Allocating a value-cell at runtime for"
+ (princ-to-string note))
+ (setf cell-note t)))))
+ (compile nil lambda))))
+ (assert (eql check cell-note))
+ (if check
+ (assert
+ (eq :ok
+ (handler-case
+ (dolist (arg args nil)
+ (setf fun (funcall fun arg)))
+ (sb-int:simple-control-error (e)
+ (when (equal
+ (simple-condition-format-control e)
+ "attempt to RETURN-FROM a block or GO to a tag that no longer exists")
+ :ok)))))
+ (ctu:assert-no-consing (apply fun args))))))
+ (test nil `(lambda (x)
+ (declare (optimize speed))
+ (block out
+ (flet ((ex () (return-from out 'out!)))
+ (typecase x
+ (cons (or (car x) (ex)))
+ (t (ex)))))) :foo)
+ (test t `(lambda (x)
+ (declare (optimize speed))
+ (funcall
+ (block nasty
+ (flet ((oops () (return-from nasty t)))
+ #'oops)))) t)
+ (test t `(lambda (r)
+ (declare (optimize speed))
+ (block out
+ (flet ((ex () (return-from out r)))
+ (lambda (x)
+ (typecase x
+ (cons (or (car x) (ex)))
+ (t (ex))))))) t t)
+ (test t `(lambda (x)
+ (declare (optimize speed))
+ (flet ((eh (x)
+ (flet ((meh () (return-from eh 'meh)))
+ (lambda ()
+ (typecase x
+ (cons (or (car x) (meh)))
+ (t (meh)))))))
+ (funcall (eh x)))) t t)))
+
+(with-test (:name (:bug-1050768 :symptom))
+ ;; Used to signal an error.
+ (compile nil
+ `(lambda (string position)
+ (char string position)
+ (array-in-bounds-p string (1+ position)))))
+
+(with-test (:name (:bug-1050768 :cause))
+ (let ((types `((string string)
+ ((or (simple-array character 24) (vector t 24))
+ (or (simple-array character 24) (vector t))))))
+ (dolist (pair types)
+ (destructuring-bind (orig conservative) pair
+ (assert sb-c::(type= (specifier-type cl-user::conservative)
+ (conservative-type (specifier-type cl-user::orig))))))))
+
+(with-test (:name (:smodular64 :wrong-width))
+ (let ((fun (compile nil
+ '(lambda (x)
+ (declare (type (signed-byte 64) x))
+ (sb-c::mask-signed-field 64 (- x 7033717698976965573))))))
+ (assert (= (funcall fun 10038) -7033717698976955535))))
+
+(with-test (:name (:smodular32 :wrong-width))
+ (let ((fun (compile nil '(lambda (x)
+ (declare (type (signed-byte 31) x))
+ (sb-c::mask-signed-field 31 (- x 1055131947))))))
+ (assert (= (funcall fun 10038) -1055121909))))
+
+(with-test (:name :first-open-coded)
+ (let ((fun (compile nil `(lambda (x) (first x)))))
+ (assert (not (ctu:find-named-callees fun)))))
+
+(with-test (:name :second-open-coded)
+ (let ((fun (compile nil `(lambda (x) (second x)))))
+ (assert (not (ctu:find-named-callees fun)))))
+
+(with-test (:name :svref-of-symbol-macro)
+ (compile nil `(lambda (x)
+ (symbol-macrolet ((sv x))
+ (values (svref sv 0) (setf (svref sv 0) 99))))))
+
+;; The compiler used to update the receiving LVAR's type too
+;; aggressively when converting a large constant to a smaller
+;; (potentially signed) one, causing other branches to be
+;; inferred as dead.
+(with-test (:name :modular-cut-constant-to-width)
+ (let ((test (compile nil
+ `(lambda (x)
+ (logand 254
+ (case x
+ ((3) x)
+ ((2 2 0 -2 -1 2) 9223372036854775803)
+ (t 358458651)))))))
+ (assert (= (funcall test -10470605025) 26))))
+
+(with-test (:name :append-type-derivation)
+ (let ((test-cases
+ '((lambda () (append 10)) (integer 10 10)
+ (lambda () (append nil 10)) (integer 10 10)
+ (lambda (x) (append x 10)) (or (integer 10 10) cons)
+ (lambda (x) (append x (cons 1 2))) cons
+ (lambda (x y) (append x (cons 1 2) y)) cons
+ (lambda (x y) (nconc x (the list y) x)) t
+ (lambda (x y) (nconc (the atom x) y)) t
+ (lambda (x y) (nconc (the (or null (eql 10)) x) y)) t
+ (lambda (x y) (nconc (the (or cons vector) x) y)) cons
+ (lambda (x y) (nconc (the sequence x) y)) t
+ (lambda (x y) (print (length y)) (append x y)) sequence
+ (lambda (x y) (print (length y)) (append x y)) sequence
+ (lambda (x y) (append (the (member (a) (b)) x) y)) cons
+ (lambda (x y) (append (the (member (a) (b) c) x) y)) cons
+ (lambda (x y) (append (the (member (a) (b) nil) x) y)) t)))
+ (loop for (function result-type) on test-cases by #'cddr
+ do (assert (sb-kernel:type= (sb-kernel:specifier-type
+ (car (cdaddr (sb-kernel:%simple-fun-type
+ (compile nil function)))))
+ (sb-kernel:specifier-type result-type))))))
+
+(with-test (:name :bug-504121)
+ (compile nil `(lambda (s)
+ (let ((p1 #'upper-case-p))
+ (funcall
+ (lambda (g)
+ (funcall p1 g))))
+ (let ((p2 #'(lambda (char) (upper-case-p char))))
+ (funcall p2 s)))))
+
+(with-test (:name (:bug-504121 :optional-missing))
+ (compile nil `(lambda (s)
+ (let ((p1 #'upper-case-p))
+ (funcall
+ (lambda (g &optional x)
+ (funcall p1 g))))
+ (let ((p2 #'(lambda (char) (upper-case-p char))))
+ (funcall p2 s)))))
+
+(with-test (:name (:bug-504121 :optional-superfluous))
+ (compile nil `(lambda (s)
+ (let ((p1 #'upper-case-p))
+ (funcall
+ (lambda (g &optional x)
+ (funcall p1 g))
+ #\1 2 3))
+ (let ((p2 #'(lambda (char) (upper-case-p char))))
+ (funcall p2 s)))))
+
+(with-test (:name (:bug-504121 :key-odd))
+ (compile nil `(lambda (s)
+ (let ((p1 #'upper-case-p))
+ (funcall
+ (lambda (g &key x)
+ (funcall p1 g))
+ #\1 :x))
+ (let ((p2 #'(lambda (char) (upper-case-p char))))
+ (funcall p2 s)))))
+
+(with-test (:name (:bug-504121 :key-unknown))
+ (compile nil `(lambda (s)
+ (let ((p1 #'upper-case-p))
+ (funcall
+ (lambda (g &key x)
+ (funcall p1 g))
+ #\1 :y 2))
+ (let ((p2 #'(lambda (char) (upper-case-p char))))
+ (funcall p2 s)))))
+
+(with-test (:name :bug-1181684)
+ (compile nil `(lambda ()
+ (let ((hash #xD13CCD13))
+ (setf hash (logand most-positive-word
+ (ash hash 5)))))))
+
+(with-test (:name (:local-&optional-recursive-inline :bug-1180992))
+ (compile nil
+ `(lambda ()
+ (labels ((called (&optional a))
+ (recursed (&optional b)
+ (called)
+ (recursed)))
+ (declare (inline recursed called))
+ (recursed)))))
+
+(with-test (:name :constant-fold-logtest)
+ (assert (equal (sb-kernel:%simple-fun-type
+ (compile nil `(lambda (x)
+ (declare (type (mod 1024) x)
+ (optimize speed))
+ (logtest x 2048))))
+ '(function ((unsigned-byte 10)) (values null &optional)))))
+
+;; type mismatches on LVARs with multiple potential sources used to
+;; be reported as mismatches with the value NIL. Make sure we get
+;; a warning, but that it doesn't complain about a constant NIL ...
+;; of type FIXNUM.
+(with-test (:name (:multiple-use-lvar-interpreted-as-NIL :cast))
+ (block nil
+ (handler-bind ((sb-int:type-warning
+ (lambda (c)
+ (assert
+ (not (search "Constant "
+ (simple-condition-format-control
+ c))))
+ (return))))
+ (compile nil `(lambda (x y z)
+ (declare (type fixnum y z))
+ (aref (if x y z) 0))))
+ (error "Where's my warning?")))
+
+(with-test (:name (:multiple-use-lvar-interpreted-as-NIL catch))
+ (block nil
+ (handler-bind ((style-warning
+ (lambda (c)
+ (assert
+ (not (position
+ nil
+ (simple-condition-format-arguments c))))
+ (return))))
+ (compile nil `(lambda (x y z f)
+ (declare (type fixnum y z))
+ (catch (if x y z) (funcall f)))))
+ (error "Where's my style-warning?")))
+
+;; Smoke test for rightward shifts
+(with-test (:name (:ash/right-signed))
+ (let* ((f (compile nil `(lambda (x y)
+ (declare (type (mod ,(* 2 sb-vm:n-word-bits)) y)
+ (type sb-vm:signed-word x)
+ (optimize speed))
+ (ash x (- y)))))
+ (max (ash most-positive-word -1))
+ (min (- -1 max)))
+ (flet ((test (x y)
+ (assert (= (ash x (- y))
+ (funcall f x y)))))
+ (dotimes (x 32)
+ (dotimes (y (* 2 sb-vm:n-word-bits))
+ (test x y)
+ (test (- x) y)
+ (test (- max x) y)
+ (test (+ min x) y))))))
+
+(with-test (:name (:ash/right-unsigned))
+ (let ((f (compile nil `(lambda (x y)
+ (declare (type (mod ,(* 2 sb-vm:n-word-bits)) y)
+ (type word x)
+ (optimize speed))
+ (ash x (- y)))))
+ (max most-positive-word))
+ (flet ((test (x y)
+ (assert (= (ash x (- y))
+ (funcall f x y)))))
+ (dotimes (x 32)
+ (dotimes (y (* 2 sb-vm:n-word-bits))
+ (test x y)
+ (test (- max x) y))))))
+
+(with-test (:name (:ash/right-fixnum))
+ (let ((f (compile nil `(lambda (x y)
+ (declare (type (mod ,(* 2 sb-vm:n-word-bits)) y)
+ (type fixnum x)
+ (optimize speed))
+ (ash x (- y))))))
+ (flet ((test (x y)
+ (assert (= (ash x (- y))
+ (funcall f x y)))))
+ (dotimes (x 32)
+ (dotimes (y (* 2 sb-vm:n-word-bits))
+ (test x y)
+ (test (- x) y)
+ (test (- most-positive-fixnum x) y)
+ (test (+ most-negative-fixnum x) y))))))
+
+;; expected failure
+(with-test (:name :fold-index-addressing-positive-offset)
+ (let ((f (compile nil `(lambda (i)
+ (if (typep i '(integer -31 31))
+ (aref #. (make-array 63) (+ i 31))
+ (error "foo"))))))
+ (funcall f -31)))
+
+;; 5d3a728 broke something like this in CL-PPCRE
+(with-test (:name :fold-index-addressing-potentially-negative-index)
+ (compile nil `(lambda (index vector)
+ (declare (optimize speed (safety 0))
+ ((simple-array character (*)) vector)
+ ((unsigned-byte 24) index))
+ (aref vector (1+ (mod index (1- (length vector))))))))
+
+(with-test (:name :constant-fold-ash/right-fixnum)
+ (compile nil `(lambda (a b)
+ (declare (type fixnum a)
+ (type (integer * -84) b))
+ (ash a b))))
+
+(with-test (:name :constant-fold-ash/right-word)
+ (compile nil `(lambda (a b)
+ (declare (type word a)
+ (type (integer * -84) b))
+ (ash a b))))
+
+(with-test (:name :nconc-derive-type)
+ (let ((function (compile nil `(lambda (x y)
+ (declare (type (or cons fixnum) x))
+ (nconc x y)))))
+ (assert (equal (sb-kernel:%simple-fun-type function)
+ '(function ((or cons fixnum) t) (values cons &optional))))))
+
+;; make sure that all data-vector-ref-with-offset VOPs are either
+;; specialised on a 0 offset or accept signed indices
+(with-test (:name :data-vector-ref-with-offset-signed-index)
+ (let ((dvr (find-symbol "DATA-VECTOR-REF-WITH-OFFSET" "SB-KERNEL")))
+ (when dvr
+ (assert
+ (null
+ (loop for info in (sb-c::fun-info-templates
+ (sb-c::fun-info-or-lose dvr))
+ for (nil second-arg third-arg) = (sb-c::vop-info-arg-types info)
+ unless (or (typep second-arg '(cons (eql :constant)))
+ (find '(integer 0 0) third-arg :test 'equal)
+ (equal second-arg
+ `(:or ,(sb-c::primitive-type-or-lose
+ 'sb-vm::positive-fixnum)
+ ,(sb-c::primitive-type-or-lose
+ 'fixnum))))
+ collect info))))))
+
+(with-test (:name :data-vector-set-with-offset-signed-index)
+ (let ((dvr (find-symbol "DATA-VECTOR-SET-WITH-OFFSET" "SB-KERNEL")))
+ (when dvr
+ (assert
+ (null
+ (loop for info in (sb-c::fun-info-templates
+ (sb-c::fun-info-or-lose dvr))
+ for (nil second-arg third-arg) = (sb-c::vop-info-arg-types info)
+ unless (or (typep second-arg '(cons (eql :constant)))
+ (find '(integer 0 0) third-arg :test 'equal)
+ (equal second-arg
+ `(:or ,(sb-c::primitive-type-or-lose
+ 'sb-vm::positive-fixnum)
+ ,(sb-c::primitive-type-or-lose
+ 'fixnum))))
+ collect info))))))
+
+(with-test (:name :maybe-inline-ref-to-dead-lambda)
+ (compile nil `(lambda (string)
+ (declare (optimize speed (space 0)))
+ (cond ((every #'digit-char-p string)
+ nil)
+ ((some (lambda (c)
+ (digit-char-p c))
+ string))))))
+
+;; the x87 backend used to sometimes signal FP errors during boxing,
+;; because converting between double and single float values was a
+;; noop (fixed), and no doubt many remaining issues. We now store
+;; the value outside pseudo-atomic, so any SIGFPE should be handled
+;; corrrectly.
+;;
+;; When it fails, this test lands into ldb.
+(with-test (:name :no-overflow-during-allocation)
+ (handler-case (eval '(cosh 90))
+ (floating-point-overflow ()
+ t)))
+
+;; unbounded integer types could break integer arithmetic.
+(with-test (:name :bug-1199127)
+ (compile nil `(lambda (b)
+ (declare (type (integer -1225923945345 -832450738898) b))
+ (declare (optimize (speed 3) (space 3) (safety 2)
+ (debug 0) (compilation-speed 1)))
+ (loop for lv1 below 3
+ sum (logorc2
+ (if (>= 0 lv1)
+ (ash b (min 25 lv1))
+ 0)
+ -2)))))
+
+;; non-trivial modular arithmetic operations would evaluate to wider results
+;; than expected, and never be cut to the right final bitwidth.
+(with-test (:name :bug-1199428-1)
+ (let ((f1 (compile nil `(lambda (a c)
+ (declare (type (integer -2 1217810089) a))
+ (declare (type (integer -6895591104928 -561736648588) c))
+ (declare (optimize (speed 2) (space 0) (safety 2) (debug 0)
+ (compilation-speed 3)))
+ (logandc1 (gcd c)
+ (+ (- a c)
+ (loop for lv2 below 1 count t))))))
+ (f2 (compile nil `(lambda (a c)
+ (declare (notinline - + gcd logandc1))
+ (declare (optimize (speed 1) (space 1) (safety 0) (debug 1)
+ (compilation-speed 3)))
+ (logandc1 (gcd c)
+ (+ (- a c)
+ (loop for lv2 below 1 count t)))))))
+ (let ((a 530436387)
+ (c -4890629672277))
+ (assert (eql (funcall f1 a c)
+ (funcall f2 a c))))))
+
+(with-test (:name :bug-1199428-2)
+ (let ((f1 (compile nil `(lambda (a b)
+ (declare (type (integer -1869232508 -6939151) a))
+ (declare (type (integer -11466348357 -2645644006) b))
+ (declare (optimize (speed 1) (space 0) (safety 2) (debug 2)
+ (compilation-speed 2)))
+ (logand (lognand a -6) (* b -502823994)))))
+ (f2 (compile nil `(lambda (a b)
+ (logand (lognand a -6) (* b -502823994))))))
+ (let ((a -1491588365)
+ (b -3745511761))
+ (assert (eql (funcall f1 a b)
+ (funcall f2 a b))))))
+
+;; win32 is very specific about the order in which catch blocks
+;; must be allocated on the stack
+(with-test (:name :bug-1072739)
+ (let ((f (compile nil
+ `(lambda ()
+ (STRING=
+ (LET ((% 23))
+ (WITH-OUTPUT-TO-STRING (G13908)
+ (PRINC
+ (LET ()
+ (DECLARE (OPTIMIZE (SB-EXT:INHIBIT-WARNINGS 3)))
+ (HANDLER-CASE
+ (WITH-OUTPUT-TO-STRING (G13909) (PRINC %A%B% G13909) G13909)
+ (UNBOUND-VARIABLE NIL
+ (HANDLER-CASE
+ (WITH-OUTPUT-TO-STRING (G13914)
+ (PRINC %A%B% G13914)
+ (PRINC "" G13914)
+ G13914)
+ (UNBOUND-VARIABLE NIL
+ (HANDLER-CASE
+ (WITH-OUTPUT-TO-STRING (G13913)
+ (PRINC %A%B G13913)
+ (PRINC "%" G13913)
+ G13913)
+ (UNBOUND-VARIABLE NIL
+ (HANDLER-CASE
+ (WITH-OUTPUT-TO-STRING (G13912)
+ (PRINC %A% G13912)
+ (PRINC "b%" G13912)
+ G13912)
+ (UNBOUND-VARIABLE NIL
+ (HANDLER-CASE
+ (WITH-OUTPUT-TO-STRING (G13911)
+ (PRINC %A G13911)
+ (PRINC "%b%" G13911)
+ G13911)
+ (UNBOUND-VARIABLE NIL
+ (HANDLER-CASE
+ (WITH-OUTPUT-TO-STRING (G13910)
+ (PRINC % G13910)
+ (PRINC "a%b%" G13910)
+ G13910)
+ (UNBOUND-VARIABLE NIL
+ (ERROR "Interpolation error in \"%a%b%\"
+"))))))))))))))
+ G13908)))
+ "23a%b%")))))
+ (assert (funcall f))))
+
+(with-test (:name :equal-equalp-transforms)
+ (let* ((s "foo")
+ (bit-vector #*11001100)
+ (values `(nil 1 2 "test"
+ ;; Floats duplicated here to ensure we get newly created instances
+ (read-from-string "1.1") (read-from-string "1.2d0")
+ (read-from-string "1.1") (read-from-string "1.2d0")
+ 1.1 1.2d0 '("foo" "bar" "test")
+ #(1 2 3 4) #*101010 (make-broadcast-stream) #p"/tmp/file"
+ ,s (copy-seq ,s) ,bit-vector (copy-seq ,bit-vector)
+ ,(make-hash-table) #\a #\b #\A #\C
+ ,(make-random-state) 1/2 2/3)))
+ ;; Test all permutations of different types
+ (assert
+ (loop
+ for x in values
+ always (loop
+ for y in values
+ always
+ (and (eq (funcall (compile nil `(lambda (x y)
+ (equal (the ,(type-of x) x)
+ (the ,(type-of y) y))))
+ x y)
+ (equal x y))
+ (eq (funcall (compile nil `(lambda (x y)
+ (equalp (the ,(type-of x) x)
+ (the ,(type-of y) y))))
+ x y)
+ (equalp x y))))))
+ (assert
+ (funcall (compile
+ nil
+ `(lambda (x y)
+ (equal (the (cons (or simple-bit-vector simple-base-string))
+ x)
+ (the (cons (or (and bit-vector (not simple-array))
+ (simple-array character (*))))
+ y))))
+ (list (string 'list))
+ (list "LIST")))
+ (assert
+ (funcall (compile
+ nil
+ `(lambda (x y)
+ (equalp (the (cons (or simple-bit-vector simple-base-string))
+ x)
+ (the (cons (or (and bit-vector (not simple-array))
+ (simple-array character (*))))
+ y))))
+ (list (string 'list))
+ (list "lisT")))))
+
+(with-test (:name (restart-case optimize speed compiler-note))
+ (handler-bind ((compiler-note #'error))
+ (compile nil '(lambda ()
+ (declare (optimize speed))
+ (restart-case () (c ()))))
+ (compile nil '(lambda ()
+ (declare (optimize speed))
+ (let (x)
+ (restart-case (setf x (car (compute-restarts)))
+ (c ()))
+ x)))))
+
+(with-test (:name :copy-more-arg
+ :fails-on '(not (or :x86 :x86-64)))
+ ;; copy-more-arg might not copy in the right direction
+ ;; when there are more fixed args than stack frame slots,
+ ;; and thus end up splatting a single argument everywhere.
+ ;; Fixed on x86oids only, but other platforms still start
+ ;; their stack frames at 8 slots, so this is less likely
+ ;; to happen.
+ (let ((limit 33))
+ (labels ((iota (n)
+ (loop for i below n collect i))
+ (test-function (function skip)
+ ;; function should just be (subseq x skip)
+ (loop for i from skip below (+ skip limit) do
+ (let* ((values (iota i))
+ (f (apply function values))
+ (subseq (subseq values skip)))
+ (assert (equal f subseq)))))
+ (make-function (n)
+ (let ((gensyms (loop for i below n collect (gensym))))
+ (compile nil `(lambda (,@gensyms &rest rest)
+ (declare (ignore ,@gensyms))
+ rest)))))
+ (dotimes (i limit)
+ (test-function (make-function i) i)))))
+
+(with-test (:name :apply-aref)
+ (flet ((test (form)
+ (let (warning)
+ (handler-bind ((warning (lambda (c) (setf warning c))))
+ (compile nil `(lambda (x y) (setf (apply #'sbit x y) 10))))
+ (assert (not warning)))))
+ (test `(lambda (x y) (setf (apply #'aref x y) 21)))
+ (test `(lambda (x y) (setf (apply #'bit x y) 1)))
+ (test `(lambda (x y) (setf (apply #'sbit x y) 0)))))
+
+(with-test (:name :warn-on-the-values-constant)
+ (multiple-value-bind (fun warnings-p failure-p)
+ (compile nil
+ ;; The compiler used to elide this test without
+ ;; noting that the type demands multiple values.
+ '(lambda () (the (values fixnum fixnum) 1)))
+ (declare (ignore warnings-p))
+ (assert (functionp fun))
+ (assert failure-p)))
+
+;; quantifiers shouldn't cons themselves.
+(with-test (:name :quantifiers-no-consing)
+ (let ((constantly-t (lambda (x) x t))
+ (constantly-nil (lambda (x) x nil))
+ (list (make-list 1000 :initial-element nil))
+ (vector (make-array 1000 :initial-element nil)))
+ (macrolet ((test (quantifier)
+ (let ((function (make-symbol (format nil "TEST-~A" quantifier))))
+ `(flet ((,function (function sequence)
+ (,quantifier function sequence)))
+ (ctu:assert-no-consing (,function constantly-t list))
+ (ctu:assert-no-consing (,function constantly-nil vector))))))
+ (test some)
+ (test every)
+ (test notany)
+ (test notevery))))