1 ;;;; various tests of the new interpreter
3 ;;;; This software is part of the SBCL system. See the README file for
6 ;;;; While most of SBCL is derived from the CMU CL system, the test
7 ;;;; files (like this one) were written from scratch after the fork
10 ;;;; This software is in the public domain and is provided with
11 ;;;; absolutely no warranty. See the COPYING and CREDITS files for
12 ;;;; more information.
14 #-sb-fasteval
(invoke-restart 'run-tests
::skip-file
)
16 (setf sb-ext
:*evaluator-mode
* :interpret
)
18 (in-package sb-interpreter
)
20 (test-util:with-test
(:name
:write-bogus-function-instance
)
22 (sb-pcl::class-prototype
(find-class 'sb-kernel
:interpreted-function
))))
24 (test-util:with-test
(:name
:type-checker-for-function
)
25 ;; The test for (FUNCTION (HAIR) (MORE-HAIR)) is just FUNCTIONP.
26 ;; The test for not that is (NOT FUNCTION).
27 (assert (eq (type-checker (specifier-type '(function (cons) cons
)))
29 (assert (type-checker (specifier-type
30 '(not (function (integer) integer
))))))
32 (defvar *invocation-count
* 0)
34 (defmacro what-cell
(x)
35 (incf *invocation-count
*)
38 (defun foo (x &optional
(howmuch 1)) (incf (what-cell x
) howmuch
))
40 (test-util:with-test
(:name
:interpreter-macro-cache-flush
)
41 (let ((cell (cons 0 0)))
45 (assert (= *invocation-count
* 1)) ; once only
46 (assert (= (car cell
) 3))
47 (defmacro what-cell
(x)
48 (incf *invocation-count
*)
50 ;; Even though INCF's definition is unchanged, the expansion of INCF
51 ;; is invalidated by the change to the definition of WHAT-CELL.
55 (assert (= *invocation-count
* 2)) ; once more
56 (assert (= (cdr cell
) 30))))
58 (test-util:with-test
(:name
:interpreter-eval-if-cond-nil-nil
)
60 (flet ((foo () (setq x
1)))
61 ;; this was accidentally optimizing out the call to FOO
65 (defmacro expect-type-error
(form)
68 (:no-error
() (error "Should have gotten a TYPE-ERROR"))))
70 (defmacro expect-bad-key-error
(form)
73 (assert (search "not in the allowed set"
74 (write-to-string c
:escape nil
))))
75 (:no-error
() (error "Expected an error"))))
77 (defmacro expect-odd-keys-error
(form)
80 (assert (search "odd number" (simple-condition-format-control c
))))
81 (:no-error
() (error "Expected an error"))))
83 (test-util:with-test
(:name
:interpreter-keyword-parsing
)
85 ;; No error - &allow-other-keys was specified.
86 (validate-keywords '(:foo
3 :bar
2 :baz
3) 1 #(:foo
))
88 (expect-odd-keys-error (validate-keywords '(:bar
) 1 #(:foo
)))
90 ;; :ALLOW-OTHER-KEYS key is always allowed even if its value is nil.
91 (validate-keywords '(:a
1 :allow-other-keys nil
:b
2)
92 (ash 3 3) #(:a
:b
:c
))
96 (validate-keywords '(:a
1 :x nil
:b
2) (ash 3 3) #(:a
:b
:c
)))
98 ;; As with all keywords, only the first value matters.
100 (validate-keywords '(:a
1 :allow-other-keys nil
:allow-other-keys t
:x
2)
101 (ash 3 3) #(:a
:b
:c
)))
104 (validate-keywords '(:a
1 :allow-other-keys t
:allow-other-keys nil
:x
2)
105 (ash 3 3) #(:a
:b
:c
))
106 ;; here we short-cicuit after seeing T but still have to check for ODDP
107 (expect-odd-keys-error
108 (validate-keywords '(:a
1 :allow-other-keys t
:allow-other-keys nil
:x
)
109 (ash 3 3) #(:a
:b
:c
)))
111 (defun foo (x &optional b c
&key akey
) (list x b c akey
))
112 ;; The ODDP check occurs only on actual args that remain after
113 ;; processing optional arguments.
114 ;; No errors should result from these calls.
117 (foo 1 2 3 :akey
'hi
))
119 (test-util:with-test
(:name
:interpreter-type-checking
)
121 (expect-type-error (the integer
(values 'a
1 2)))
124 (locally (declare (optimize (safety 0))) (the integer
(values 'a
1 2)))
126 ;; THE returns multiple values even if not a VALUES type-specifier.
127 (let ((l (multiple-value-call #'list
128 (the integer
(values 1 'foo
'bar
)))))
129 (assert (= (length l
) 3)))
131 ;; Too many values in a "strict" THE form are not permitted.
132 (expect-type-error (the (values integer
&optional
) (values 1 2)))
134 ;; A trailing type of which NIL is a member (so LIST,SYMBOL,T at least)
135 ;; causes (THE VALUES ...) to accept absence of a value. By definition the
136 ;; missing values are NIL. While this seems liberal, so far as VALUES
137 ;; expressing a shape similar to DESTRUCTURING-BIND, CLHS draws attention
138 ;; to it specifically:
140 ;; "It is permissible for _form_ to yield a different number of values than
141 ;; are specified by value-type, provided that the values for which types are
142 ;; declared are indeed of those types. Missing values are treated as nil
143 ;; for the purpose of checking their types"
144 (dolist (trailing-type '(symbol t
))
145 (eval `(the (values integer
,trailing-type
) 4))
146 (eval `(the (values integer
,trailing-type
) (values 4 'foo
)))
147 (eval `(the (values integer
,trailing-type
) (values 4 'foo
5))))
149 ;; But a strict THE form does not allow this liberty.
150 (expect-type-error (the (values integer symbol
&optional
) 4))
153 (defun g (x) (length (string x
)))
160 ;; The first binding of X in h is to a symbol, which is fine.
161 ;; The FIXNUM declaration applies to the _second_ binding named X.
164 ;; The SETQ is not valid because X is restricted to fixnum.
168 (declare (fixnum x
))))
171 (the (values integer symbol
&optional string
) (bar)))
173 (the (values integer symbol string
) (bar)))
175 (the (values integer
&optional
) (bar)))
177 (defun bar () (values 1 'hi
))
180 (expect-type-error (foo2)) ; didn't get a string as 3rd value
181 (expect-type-error (foo3)) ; got too many values
184 (the (values integer symbol string
) (bar)))
186 (defun no-vals () (values))
188 (handler-case (let ((x (the integer
(no-vals)))) x
)
190 (:no-error
() (error "Should have gotten an ERROR")))
192 (defmacro nice-macro
(a b
)
193 (declare (type (member :first
:second
) a
))
196 (:second
`(cadr ,b
))))
198 (assert (equal (macroexpand-1 '(nice-macro :first
(x)))
200 ;; macro should fail.
201 (expect-type-error (macroexpand '(nice-macro :third
(x))))
203 ;; SETQ of MUMBLE which is a "free" (not bound) typed special variable
205 (let* ((foo 3) (baz foo
))
206 (declare (special foo mumble
) (real mumble
))
211 (declare (special x
) (integer x
))
216 (locally (declare (special x
))
218 ;; this references the special X, not the lexical X
221 ;; This works due to short-circuiting within TYPEP
222 (let ((x 3)) (declare (type (or integer blurf
) x
)) x
)
224 ;; This fails under a naive OR type parser, but a different parse might
225 ;; equate (OR BLURF INTEGER) with (OR INTEGER BLURF), which just worked fine in the
226 ;; previous test, or could force known types to appear to the left of unknowns
227 ;; in a union. So even dropping the values-specifier-type cache may not make this fail.
228 ;; I'm keeping it in case I change my mind again though.
230 (handler-case (let ((x 3)) (declare (type (or blurf integer
) x
)) x
)
231 (simple-error ()) ; "unknown type"
232 (:no-error
(&rest ignore
) (error "Expected an ERROR"))))
234 (test-util:with-test
(:name
:tagbody-if-optimizer
)
237 (with-output-to-string (*standard-output
*)
250 (macrolet ((foo= (x y
) `(= (the fixnum
,x
) (the fixnum
,y
))))
251 (declare (optimize speed
))
252 (declaim (inline foo-compare
))
253 (defun foo-compare (a b
) (foo= a b
)))
255 (test-util:with-test
(:name
:inline-lexenv-not-too-hairy
)
256 (assert (sb-c::fun-name-inline-expansion
'foo-compare
)))
258 (defmacro use-hairy-env
(x &environment e
)
260 (eql (sb-interpreter::env-from-lexenv e
) :compile
)
261 (sb-int:eval-in-lexenv x e
)))
263 ;;; Assert that USE-HAIRY-ENV can be invoked such that when it calls
264 ;;; EVAL-IN-LEXENV on an environment object that is too complex,
265 ;;; it works anyway. Of course don't actually do this :-)
266 ;;; Arguably the interpreter could be modified such that it only chokes
267 ;;; if you _actually_ try to reference parts of the complex lexenv
268 ;;; that you're not allowed to, but that's a whole other ball of wax.
269 (test-util:with-test
(:name
:eval-in-complex-lexenv
)
271 (funcall (compile nil
'(lambda (a) (cons a
(use-hairy-env (+ 1 2)))))
273 (assert (eql (first answer
) 45))
274 ;; ensure that the lambda environment could not be handled by the interpreter
275 (assert (eql (second answer
) t
))
276 (assert (eql (third answer
) 3))))
278 (test-util:with-test
(:name
:exited-block
)
279 (handler-case (funcall (let ((x 1)) (block b
(lambda () (return-from b
)))))
281 (assert (and (typep c
'sb-int
:simple-control-error
)
282 (search "exited block" (simple-condition-format-control c
)))))
283 (:no-error
(&rest whatever
) (error "Expected an error"))))
285 (test-util:with-test
(:name
:exited-tagbody
)
286 (handler-case (funcall
289 (return-from zot
(let ((x 1)) (lambda () (go foo
))))
292 (assert (and (typep c
'sb-int
:simple-control-error
)
293 (search "exited tagbody"
294 (simple-condition-format-control c
)))))
295 (:no-error
(&rest whatever
) (error "Expected an error"))))
297 (test-util:with-test
(:name
:argless-lambda
)
298 (assert (eq ((lambda () (declare (special *some-var
*)) (setq *some-var
* t
)))
301 (test-util:with-test
(:name
:typecheck-symbol-not-null
)
302 (funcall (lambda (x) (the (and symbol
(not null
)) x
))
306 (test-util:with-test
(:name
:compiled-equalp-method
)
307 (assert (compiled-function-p
308 (sb-kernel:layout-equalp-impl
309 (sb-kernel:find-layout
'testme
)))))
310 (let ((f #'testme-x
))
311 (let ((source-loc (sb-interpreter:fun-source-location f
)))
312 (setf (slot-value source-loc
'sb-c
::namestring
) "myfile.lisp")))
313 (defun foo (s) (testme-x s
))
314 (test-util:with-test
(:name
:source-namestring
)
315 (assert (string= (sb-kernel::function-file-namestring
#'testme-x
)
318 ;; check that implicit compilation happened
319 (assert (= (sb-kernel:widetag-of
#'testme-x
) sb-vm
:simple-fun-widetag
))
320 ;; check that source location namestring was preserved
321 (assert (string= (sb-kernel::function-file-namestring
#'testme-x
)
324 (macrolet ((some-things () ''(bit-vector string
)))
325 (deftype thingz
(&optional d
)
326 `(or ,@(mapcar (lambda (x) `(,x
,d
)) (some-things)))))
327 ;;; FUNCTION-LAMBDA-EXPRESSION could incorrectly return NIL for the second value
328 (test-util:with-test
(:name
:fun-lambda-expr-closure
)
329 (assert (nth-value 1 (function-lambda-expression (sb-int:info
:type
:expander
'thingz
))))
330 ;; and make sure the expander actually works
331 (assert (typep "hey" '(thingz 3))))
333 (defpackage fancypkg
(:use
"CL" "SB-EXT") (:export make-mystruct mystruct-x
))
334 (in-package fancypkg
)
335 (defstruct mystruct
(x 3) y
)
336 (in-package "CL-USER")
337 (lock-package "FANCYPKG")
340 (fancypkg:mystruct-x
(fancypkg:make-mystruct
)))
341 (test-util:with-test
(:name
:jit-compiled-struct-accessor-locked-pkg
)
343 (assert (not (compiled-function-p #'f
)))
344 (assert (compiled-function-p #'fancypkg
:mystruct-x
)))