X-Git-Url: http://repo.macrolet.net/gitweb/?a=blobdiff_plain;f=tests%2Fcompiler.impure.lisp;h=67951711e2df59ea068bf5e82401d6f2deeaef37;hb=f09f67b4233004079affc70de2ef2d49f27ca91a;hp=cb6ec003fa3ece382cb46f015cf0c25b8fd0eb4f;hpb=1a19da9ff50a45962fc2cf42c7a241a2ce97a364;p=sbcl.git diff --git a/tests/compiler.impure.lisp b/tests/compiler.impure.lisp index cb6ec00..6795171 100644 --- a/tests/compiler.impure.lisp +++ b/tests/compiler.impure.lisp @@ -618,7 +618,7 @@ (assert (equal (check-embedded-thes 3 0 2 :a) '(2 :a))) (assert (typep (check-embedded-thes 3 0 4 2.5f0) 'type-error)) -(assert (equal (check-embedded-thes 1 0 4 :b) '(4 :b))) +(assert (equal (check-embedded-thes 1 0 3 :b) '(3 :b))) (assert (typep (check-embedded-thes 1 0 1.0 2.5f0) 'type-error)) @@ -928,6 +928,177 @@ (eval '(labels ((%f (&key x) x)) (%f nil nil))) (error (c) :good) (:no-error (val) (error "no error: ~S" val))) + +;;; PROGV must not bind constants, or violate declared types -- ditto for SET. +(assert (raises-error? (set pi 3))) +(assert (raises-error? (progv '(pi s) '(3 pi) (symbol-value x)))) +(declaim (cons *special-cons*)) +(assert (raises-error? (set '*special-cons* "nope") type-error)) +(assert (raises-error? (progv '(*special-cons*) '("no hope") (car *special-cons*)) type-error)) + +;;; No bogus warnings for calls to functions with complex lambda-lists. +(defun complex-function-signature (&optional x &rest y &key z1 z2) + (cons x y)) +(with-test (:name :complex-call-doesnt-warn) + (handler-bind ((warning #'error)) + (compile nil '(lambda (x) (complex-function-signature x :z1 1 :z2 2))))) + +(with-test (:name :non-required-args-update-info) + (let ((name (gensym "NON-REQUIRE-ARGS-TEST")) + (*evaluator-mode* :compile)) + (eval `(defun ,name (x) x)) + (assert (equal '(function (t) (values t &optional)) + (sb-kernel:type-specifier (sb-int:info :function :type name)))) + (eval `(defun ,name (x &optional y) (or x y))) + (assert (equal '(function (t &optional t) (values t &optional)) + (sb-kernel:type-specifier (sb-int:info :function :type name)))))) + +;;;; inline & maybe inline nested calls + +(defun quux-marker (x) x) +(declaim (inline foo-inline)) +(defun foo-inline (x) (quux-marker x)) +(declaim (maybe-inline foo-maybe-inline)) +(defun foo-maybe-inline (x) (quux-marker x)) +;; Pretty horrible, but does the job +(defun count-full-calls (name function) + (let ((code (with-output-to-string (s) + (disassemble function :stream s))) + (n 0)) + (with-input-from-string (s code) + (loop for line = (read-line s nil nil) + while line + when (search name line) + do (incf n))) + n)) + +(with-test (:name :nested-inline-calls) + (let ((fun (compile nil `(lambda (x) + (foo-inline (foo-inline (foo-inline x))))))) + (assert (= 0 (count-full-calls "FOO-INLINE" fun))) + (assert (= 3 (count-full-calls "QUUX-MARKER" fun))))) + +(with-test (:name :nested-maybe-inline-calls) + (let ((fun (compile nil `(lambda (x) + (declare (optimize (space 0))) + (foo-maybe-inline (foo-maybe-inline (foo-maybe-inline x))))))) + (assert (= 0 (count-full-calls "FOO-MAYBE-INLINE" fun))) + (assert (= 1 (count-full-calls "QUUX-MARKER" fun))))) + +(with-test (:name :inline-calls) + (let ((fun (compile nil `(lambda (x) + (list (foo-inline x) + (foo-inline x) + (foo-inline x)))))) + (assert (= 0 (count-full-calls "FOO-INLINE" fun))) + (assert (= 3 (count-full-calls "QUUX-MARKER" fun))))) + +(with-test (:name :maybe-inline-calls) + (let ((fun (compile nil `(lambda (x) + (declare (optimize (space 0))) + (list (foo-maybe-inline x) + (foo-maybe-inline x) + (foo-maybe-inline x)))))) + (assert (= 0 (count-full-calls "FOO-MAYBE-INLINE" fun))) + (assert (= 1 (count-full-calls "QUUX-MARKER" fun))))) + +(defun file-compile (toplevel-forms &key load) + (let* ((lisp "compile-impure-tmp.lisp") + (fasl (compile-file-pathname lisp))) + (unwind-protect + (progn + (with-open-file (f lisp :direction :output) + (dolist (form toplevel-forms) + (prin1 form f))) + (multiple-value-bind (fasl warn fail) (compile-file lisp) + (when load + (load fasl)) + (values warn fail))) + (ignore-errors (delete-file lisp)) + (ignore-errors (delete-file fasl))))) + +(with-test (:name :bug-405) + ;; These used to break with a TYPE-ERROR + ;; The value NIL is not of type SB-C::PHYSENV. + ;; in MERGE-LETS. + (file-compile + '((LET (outer-let-var) + (lambda () + (print outer-let-var) + (MULTIPLE-VALUE-CALL 'some-function + (MULTIPLE-VALUE-CALL (LAMBDA (a) 'foo) + 1)))))) + (file-compile + '((declaim (optimize (debug 3))) + (defstruct bug-405-foo bar) + (let () + (flet ((i (x) (frob x (bug-405-foo-bar foo)))) + (i :five)))))) + +;;; bug 235a +(declaim (ftype (function (cons) number) bug-235a-aux)) +(declaim (inline bug-235a-aux)) +(defun bug-235a-aux (c) + (the number (car c))) +(with-test (:name :bug-235a) + (let ((fun (compile nil + `(lambda (x y) + (values (locally (declare (optimize (safety 0))) + (bug-235a-aux x)) + (locally (declare (optimize (safety 3))) + (bug-235a-aux y))))))) + (assert + (eq :error + (handler-case + (funcall fun '(:one) '(:two)) + (type-error (e) + (assert (eq :two (type-error-datum e))) + (assert (eq 'number (type-error-expected-type e))) + :error)))))) + +(with-test (:name :compiled-debug-funs-leak) + (sb-ext:gc :full t) + (let ((usage-before (sb-kernel::dynamic-usage))) + (dotimes (x 10000) + (let ((f (compile nil '(lambda () + (error "X"))))) + (handler-case + (funcall f) + (error () nil)))) + (sb-ext:gc :full t) + (let ((usage-after (sb-kernel::dynamic-usage))) + (when (< (+ usage-before 2000000) usage-after) + (error "Leak"))))) + +;;; PROGV compilation and type checking when the declared type +;;; includes a FUNCTION subtype. +(declaim (type (or (function (t) (values boolean &optional)) string) + *hairy-progv-var*)) +(defvar *hairy-progv-var* #'null) +(with-test (:name :hairy-progv-type-checking) + (assert (eq :error + (handler-case + (progv '(*hairy-progv-var*) (list (eval 42)) + *hairy-progv-var*) + (type-error () :error)))) + (assert (equal "GOOD!" + (progv '(*hairy-progv-var*) (list (eval "GOOD!")) + *hairy-progv-var*)))) + +(with-test (:name :fill-complex-single-float) + (assert (every (lambda (x) (eql x #c(-1.0 -2.0))) + (funcall + (lambda () + (make-array 2 + :element-type '(complex single-float) + :initial-element #c(-1.0 -2.0))))))) + +(with-test (:name :make-array-symbol-as-initial-element) + (assert (every (lambda (x) (eq x 'a)) + (funcall + (compile nil + `(lambda () + (make-array 12 :initial-element 'a))))))) ;;;; tests not in the problem domain, but of the consistency of the ;;;; compiler machinery itself @@ -1272,20 +1443,21 @@ ;;; FUNCALL forms in compiler macros, lambda-list parsing (define-compiler-macro test-cmacro-1 - (&whole whole a &optional b &rest c &key d) - (list whole a b c d)) + (&whole whole a (a2) &optional b &rest c &key d) + (list whole a a2 b c d)) -(macrolet ((test (form a b c d) +(macrolet ((test (form a a2 b c d) `(let ((form ',form)) - (destructuring-bind (whole a b c d) + (destructuring-bind (whole a a2 b c d) (funcall (compiler-macro-function 'test-cmacro-1) form nil) (assert (equal whole form)) (assert (eql a ,a)) + (assert (eql a2 ,a2)) (assert (eql b ,b)) (assert (equal c ,c)) (assert (eql d ,d))))) ) - (test (funcall 'test-cmacro-1 1 2 :d 3) 1 2 '(:d 3) 3) - (test (test-cmacro-1 11 12 :d 13) 11 12 '(:d 13) 13)) + (test (funcall 'test-cmacro-1 1 (x) 2 :d 3) 1 'x 2 '(:d 3) 3) + (test (test-cmacro-1 11 (y) 12 :d 13) 11 'y 12 '(:d 13) 13)) ;;; FUNCALL forms in compiler macros, expansions (define-compiler-macro test-cmacro-2 () ''ok) @@ -1551,4 +1723,131 @@ (not (or c d e f g h i j k l m n o p q r s)))))) (wants-many-values 1 42) +;;; constant coalescing + +(defun count-code-constants (x f) + (let ((code (sb-kernel:fun-code-header f)) + (n 0)) + (loop for i from sb-vm::code-constants-offset below (sb-kernel:get-header-data code) + do (when (equal x (sb-kernel:code-header-ref code i)) + (incf n))) + n)) + +(defvar *lambda*) + +(defun compile2 (lambda) + (let* ((lisp "compiler-impure-tmp.lisp") + (fasl (compile-file-pathname lisp))) + (unwind-protect + (progn + (with-open-file (f lisp :direction :output) + (prin1 `(setf *lambda* ,lambda) f)) + (multiple-value-bind (fasl warn fail) (compile-file lisp) + (declare (ignore warn)) + (when fail + (error "File-compiling ~S failed." lambda)) + (let ((*lambda* nil)) + (load fasl) + (values *lambda* (compile nil lambda))))) + (ignore-errors (delete-file lisp)) + (ignore-errors (delete-file fasl))))) + +;; named and unnamed +(defconstant +born-to-coalesce+ '.born-to-coalesce.) +(multiple-value-bind (file-fun core-fun) + (compile2 '(lambda () + (let ((x (cons +born-to-coalesce+ nil)) + (y (cons '.born-to-coalesce. nil))) + (list x y)))) + (assert (= 1 (count-code-constants '.born-to-coalesce. file-fun))) + (assert (= 1 (count-code-constants '.born-to-coalesce. core-fun)))) + +;; some things must retain identity under COMPILE, but we want to coalesce them under COMPILE-FILE +(defun assert-coalescing (constant) + (let ((value (copy-seq (symbol-value constant)))) + (multiple-value-bind (file-fun core-fun) + (compile2 `(lambda () + (let ((x (cons ,constant nil)) + (y (cons ',value nil))) + (list x y)))) + (assert (= 1 (count-code-constants value file-fun))) + (assert (= 2 (count-code-constants value core-fun))) + (let* ((l (funcall file-fun)) + (a (car (first l))) + (b (car (second l)))) + (assert (and (equal value a) + (equal a b) + (eq a b)))) + (let* ((l (funcall core-fun)) + (a (car (first l))) + (b (car (second l)))) + (assert (and (equal value a) + (equal a b) + (not (eq a b)))))))) + +(defconstant +born-to-coalesce2+ "maybe coalesce me!") +(assert-coalescing '+born-to-coalesce2+) + +(defconstant +born-to-coalesce3+ #*01101001011101110100011) +(assert-coalescing '+born-to-coalesce3+) + +(defconstant +born-to-coalesce4+ '(foo bar "zot" 123 (nested "quux") #*0101110010)) +(assert-coalescing '+born-to-coalesce4+) + +(defclass some-constant-thing () ()) + +;;; correct handling of nested things loaded via SYMBOL-VALUE +(defvar *sneaky-nested-thing* (list (make-instance 'some-constant-thing))) +(defconstant +sneaky-nested-thing+ *sneaky-nested-thing*) +(multiple-value-bind (file-fun core-fun) (compile2 '(lambda () +sneaky-nested-thing+)) + (assert (equal *sneaky-nested-thing* (funcall file-fun))) + (assert (equal *sneaky-nested-thing* (funcall core-fun)))) + +;;; catch constant modifications thru undefined variables +(defun sneak-set-dont-set-me (x) + (ignore-errors (setq dont-set-me x))) +(defconstant dont-set-me 42) +(assert (not (sneak-set-dont-set-me 13))) +(assert (= 42 dont-set-me)) +(defun sneak-set-dont-set-me2 (x) + (ignore-errors (setq dont-set-me2 x))) +(defconstant dont-set-me2 (make-instance 'some-constant-thing)) +(assert (not (sneak-set-dont-set-me2 13))) +(assert (typep dont-set-me2 'some-constant-thing)) + +;;; check that non-trivial constants are EQ across different files: this is +;;; not something ANSI either guarantees or requires, but we want to do it +;;; anyways. +(defconstant +share-me-1+ 123.456d0) +(defconstant +share-me-2+ "a string to share") +(defconstant +share-me-3+ (vector 1 2 3)) +(defconstant +share-me-4+ (* 2 most-positive-fixnum)) +(multiple-value-bind (f1 c1) (compile2 '(lambda () (values +share-me-1+ + +share-me-2+ + +share-me-3+ + +share-me-4+ + pi))) + (multiple-value-bind (f2 c2) (compile2 '(lambda () (values +share-me-1+ + +share-me-2+ + +share-me-3+ + +share-me-4+ + pi))) + (flet ((test (fa fb) + (mapc (lambda (a b) + (assert (eq a b))) + (multiple-value-list (funcall fa)) + (multiple-value-list (funcall fb))))) + (test f1 c1) + (test f1 f2) + (test f1 c2)))) + +;;; user-defined satisfies-types cannot be folded +(deftype mystery () '(satisfies mysteryp)) +(defvar *mystery* nil) +(defun mysteryp (x) (eq x *mystery*)) +(defstruct thing (slot (error "missing") :type mystery)) +(defun test-mystery (m) (when (eq :mystery (thing-slot m)) :ok)) +(setf *mystery* :mystery) +(assert (eq :ok (test-mystery (make-thing :slot :mystery)))) + ;;; success