1 ;;;; package lock tests with side effects
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.
16 (load "assertoid.lisp")
17 (use-package "ASSERTOID")
20 (sb-ext:quit :unix-status 104)
22 ;;;; Our little labrats and a few utilities
24 (defpackage :test-used)
26 (defpackage :test-unused)
28 (defpackage :test-aux (:export #:noslot #:noslot2))
59 (defvar *uninterned* "UNINTERNED")
60 (defvar *interned* "INTERNED")
62 (defun maybe-unintern (name package)
63 (let ((s (find-symbol name package)))
65 (unintern s package))))
67 (defun set-test-locks (lock-p)
68 (dolist (p '(:test :test-aux :test-delete))
69 (when (find-package p)
71 (sb-ext:lock-package p)
72 (sb-ext:unlock-package p)))))
75 "Reset TEST package to a known state, ensure that TEST-DELETE exists."
76 (unless (find-package :test-delete)
77 (make-package :test-delete))
78 (sb-ext:with-unlocked-packages (:test :test-aux)
79 (dolist (s '(test:nosymbol-macro
80 test:noclass test:nostruct test:nostruct2 test:nocondition))
83 (intern (symbol-name s) :test))
84 (rename-package (find-package :test) :test)
85 (unexport (intern "INTERNAL" :test) :test)
86 (intern *interned* :test)
87 (use-package :test-used :test)
88 (export 'test::external :test)
89 (unuse-package :test-unused :test)
90 (defclass test:class () ())
91 (defun test:function () 'test:function)
92 (defmacro test:macro () ''test:macro)
93 (defparameter test:*special* 'test:*special*)
94 (defconstant test:constant 'test:constant)
95 (intern "UNUSED" :test)
96 (dolist (s '(test:nocondition-slot test:noclass-slot test:nostruct-slot
97 test-aux:noslot test-aux:noslot2))
100 (fmakunbound 'test:unused)
101 (makunbound 'test:unused)))
102 (maybe-unintern *uninterned* :test)
103 (maybe-unintern "NOT-FROM-TEST" :test)
104 (defconstant test:num 0)
105 (define-symbol-macro test:symbol-macro "SYMBOL-MACRO")
106 (defun test:numfun (n) n)
107 (defun test:car (cons) (cl:car cons))
108 (defun (setf test:cdr) (obj cons) (setf (cl:cdr cons) obj))
109 (assert (not (find-symbol *uninterned* :test)))))
111 (defun tmp-fmakunbound (x)
112 "FMAKUNDBOUND x, then restore the original binding."
113 (let ((f (fdefinition x)))
115 (ignore-errors (setf (fdefinition x) f))))
117 (defmacro with-error-info ((string &rest args) &body forms)
118 `(handler-bind ((error (lambda (e)
119 (format t ,string ,@args)
125 ;;; A collection of forms that are legal both with and without package
127 (defvar *legal-forms*
128 '(;; package alterations that don't actually mutate the package
129 (intern *interned* :test)
130 (import 'test:unused :test)
131 (shadowing-import 'test:shadowed :test)
132 (export 'test:unused :test)
133 (unexport 'test::internal :test)
134 (let ((p (find-package :test)))
135 (rename-package p :test))
136 (use-package :test-used :test)
137 (unuse-package :test-unused :test)
138 (shadow "SHADOWED" :test)
139 (let ((s (with-unlocked-packages (:test)
140 (let ((s (intern *uninterned* :test)))
145 ;; binding and altering value
146 (let ((test:function 123))
147 (assert (eql test:function 123)))
148 (let ((test:*special* :foo))
149 (assert (eql test:*special* :foo)))
151 (setf test:*special* :quux)
152 (assert (eql test:*special* :quux)))
153 (let ((test:unused :zot))
154 (assert (eql test:unused :zot)))
157 (symbol-macrolet ((test:function :sym-ok))
158 (assert (eql test:function :sym-ok)))
159 (symbol-macrolet ((test:unused :sym-ok2))
160 (assert (eql test:unused :sym-ok2)))
162 ;; binding as a function
163 (flet ((test:*special* () :yes))
164 (assert (eql (test:*special*) :yes)))
165 (flet ((test:unused () :yes!))
166 (assert (eql (test:unused) :yes!)))
167 (labels ((test:*special* () :yes))
168 (assert (eql (test:*special*) :yes)))
169 (labels ((test:unused () :yes!))
170 (assert (eql (test:unused) :yes!)))
172 ;; binding as a macro
173 (macrolet ((test:*special* () :ok))
174 (assert (eql (test:*special*) :ok)))
177 ;;; A collection of forms that cause runtime package lock violations
178 ;;; on TEST, and will also signal an error on LOAD even if first
179 ;;; compiled with COMPILE-FILE with TEST unlocked.
180 (defvar *illegal-runtime-forms*
181 '(;; package alterations
182 (intern *uninterned* :test)
183 (import 'not-from-test :test)
184 (export 'test::internal :test)
185 (unexport 'test:external :test)
186 (shadowing-import 'not-from-test :test)
187 (let ((p (find-package :test)))
188 (rename-package p :test '(:test-nick)))
189 (use-package :test-unused :test)
190 (unuse-package :test-used :test)
191 (shadow 'not-from-test :test)
192 (unintern (or (find-symbol *interned* :test) (error "bugo")) :test)
193 (delete-package :test-delete)
195 ;; defining or undefining as a function
196 (defun test:unused () 'foo)
197 (setf (fdefinition 'test:unused) (lambda () 'bar))
198 (setf (symbol-function 'test:unused) (lambda () 'quux))
199 (tmp-fmakunbound 'test:function)
201 ;; defining or undefining as a macro or compiler macro
202 (defmacro test:unused () ''foo)
203 (setf (macro-function 'test:unused) (constantly 'foo))
204 (define-compiler-macro test:unused (&whole form arg)
206 (setf (compiler-macro-function 'test:unused) (constantly 'foo))
208 ;; type-specifier or structure
210 (defstruct test:nostruct test:nostruct-slot)
211 ;; test creation as well, since the structure-class won't be
212 ;; finalized before that
213 (make-nostruct :nostruct-slot :foo))
214 (defclass test:noclass ()
215 ((slot :initform nil :accessor test:noclass-slot)))
216 (deftype test:notype () 'string)
217 (define-condition test:nocondition (error)
218 ((slot :initform nil :accessor test:nocondition-slot)))
221 (define-symbol-macro test:nosymbol-macro 'foo)
223 ;; declaration proclamation
224 (proclaim '(declaration test:unused))
227 (declaim (special test:nospecial))
228 (proclaim '(special test:nospecial))
231 (declaim (type fixnum test:num))
232 (proclaim '(type fixnum test:num))
235 (declaim (ftype (function (fixnum) fixnum) test:numfun))
236 (proclaim '(ftype (function (fixnum) fixnum) test:numfun))
239 (defsetf test:car rplaca) ; strictly speaking wrong, but ok as a test
240 (defsetf test:car (cons) (new-car)
241 `(setf (car ,cons) ,new-car))
242 (define-setf-expander test:car (place)
243 (multiple-value-bind (dummies vals newval setter getter)
244 (get-setf-expansion place)
245 (let ((store (gensym)))
249 `(progn (rplaca ,getter ,store) ,store)
252 ;; setf function names
253 (defun (setf test:function) (obj)
255 (tmp-fmakunbound '(setf test:cdr))
257 ;; define-method-combination
258 (define-method-combination test:unused)
261 (setf (find-class 'test:class) (find-class 'standard-class))
264 ;;; Forms that cause violations on two distinct packages.
265 (defvar *illegal-double-forms*
266 '((defclass test:noclass () ((x :accessor test-aux:noslot)))
267 (define-condition test:nocondition (error)
268 ((x :accessor test-aux:noslot2)))))
270 ;;; A collection of forms that cause compile-time package lock
271 ;;; violations on TEST, and will not signal an error on LOAD if first
272 ;;; compiled by COMPILE-FILE with test unlocked. CAR is the affected
273 ;;; symbol, CDR the form affecting it.
274 (defvar *illegal-compile-time-forms-alist*
277 ;; binding as a function
278 (test:function . (flet ((test:function () :shite))
280 (test:function . (labels ((test:function () :shite))
282 (test:macro . (flet ((test:macro () :shite))
284 (test:macro . (labels ((test:macro () :shite))
288 (test:function . (macrolet ((test:function () :yuk))
290 (test:macro . (macrolet ((test:macro () :yuk))
294 (test:function . (flet (((setf test:function) (obj)
296 (setf (test:function) 1)))
299 (test:function . (locally
300 (declare (ftype function test:function))
305 (declare (type fixnum test:num))
309 (test:nospecial . (locally
310 (declare (special test:nospecial))
314 (test:numfun . (locally
315 (declare (ftype (function (fixnum) fixnum) test:numfun))
318 (defvar *illegal-compile-time-forms* (mapcar #'cdr *illegal-compile-time-forms-alist*))
320 (defvar *illegal-forms* (append *illegal-runtime-forms*
321 *illegal-compile-time-forms*
322 *illegal-double-forms*))
324 ;;;; Running the tests
326 ;;; Unlocked. No errors nowhere.
329 (dolist (form (append *legal-forms* *illegal-forms*))
330 (with-error-info ("~Unlocked form: ~S~%" form)
333 ;;; Locked. Errors for all illegal forms, none for legal.
336 (dolist (form *legal-forms*)
337 (with-error-info ("locked legal form: ~S~%" form)
341 (dolist (form (append *illegal-runtime-forms* *illegal-double-forms*))
342 (with-error-info ("locked illegal runtime form: ~S~%" form)
343 (let ((fun (compile nil `(lambda () ,form))))
344 (assert (raises-error? (funcall fun) sb-ext:package-lock-violation)))))
345 (dolist (pair *illegal-compile-time-forms-alist*)
346 (let ((form (cdr pair)))
347 (with-error-info ("locked illegal compile-time form: ~S~%" form)
348 (assert (raises-error? (compile nil `(lambda () ,form)) sb-ext:package-lock-violation)))))
350 ;;; Locked, WITHOUT-PACKAGE-LOCKS for runtime errors.
353 (dolist (form *illegal-runtime-forms*)
354 (with-error-info ("without-package-locks illegal runtime form: ~S~%" form)
355 (funcall (compile nil `(lambda () (without-package-locks ,form))))))
357 ;;; Locked, WITHOUT-PACKAGE-LOCKS & DISABLE-PACKAGE-LOCKS for compile-time errors.
360 (dolist (pair *illegal-compile-time-forms-alist*)
361 (destructuring-bind (sym . form) pair
362 (with-error-info ("without-package-locks illegal compile-time form: ~S~%" form)
363 (let ((fun (without-package-locks (compile nil `(lambda () ,form)))))
367 (dolist (pair *illegal-compile-time-forms-alist*)
368 (destructuring-bind (sym . form) pair
369 (with-error-info ("disable-package-locks illegal compile-time form: ~S~%" form)
370 (funcall (compile nil `(lambda ()
371 (declare (disable-package-locks ,sym))
374 ;;; Locked, one error per "lexically apparent violated package", also
378 (dolist (form (append *illegal-runtime-forms* *illegal-compile-time-forms*))
379 (with-error-info ("one error per form: ~S~%" form)
381 (handler-bind ((package-lock-violation (lambda (e)
383 (error "multiple errors"))
387 (dolist (form *illegal-double-forms*)
388 (with-error-info ("two errors per form: ~S~%" form)
389 (let ((error-count 0))
390 ;; check that we don't get multiple errors from a single form
391 (handler-bind ((package-lock-violation (lambda (x)
396 (unless (= 2 error-count)
397 (error "expected 2 errors per form, got ~A for ~A"
398 error-count form))))))
400 ;;; COMPILE-FILE when unlocked, LOAD locked -- *illegal-runtime-forms* only
401 (let* ((tmp "package-locks.tmp.lisp")
402 (fasl (compile-file-pathname tmp))
404 (dolist (form *illegal-runtime-forms*)
406 (with-simple-restart (next "~S failed, continue with next test" form)
409 (with-open-file (f tmp :direction :output)
411 (multiple-value-bind (file warnings failure-p) (compile-file tmp)
413 (assert (raises-error? (load fasl) sb-ext:package-lock-violation))))
414 (when (probe-file tmp)
416 (when (probe-file fasl)
417 (delete-file fasl)))))
419 ;;;; Tests for enable-package-locks declarations
422 (dolist (pair *illegal-compile-time-forms-alist*)
423 (destructuring-bind (sym . form) pair
424 (assert (raises-error?
425 (compile nil `(lambda ()
426 (declare (disable-package-locks ,sym))
428 (locally (declare (enable-package-locks ,sym))
430 package-lock-violation))
431 (assert (raises-error?
432 (eval `(locally (declare (disable-package-locks ,sym))
434 (locally (declare (enable-package-locks ,sym))
436 package-lock-violation))))
438 ;;;; Program-errors from lexical violations
441 (dolist (pair *illegal-compile-time-forms-alist*)
442 (destructuring-bind (sym . form) pair
443 (declare (ignore sym))
444 (let ((fun (compile nil `(lambda ()
446 (assert (raises-error? (funcall fun) program-error)))))
448 ;;;; See that trace on functions in locked packages doesn't break
450 (assert (trace test:function :break t))
452 ;;;; No bogus violations from defclass with accessors in a locked
453 ;;;; package. Reported by by Francois-Rene Rideau.
454 (assert (package-locked-p :sb-gray))
455 (multiple-value-bind (fun compile-errors)
459 (defclass fare-class ()
460 ((line-column :initform 0 :reader sb-gray:stream-line-column))))))
461 (assert (not compile-errors))
463 (multiple-value-bind (class run-errors) (ignore-errors (funcall fun))
464 (assert (not run-errors))
465 (assert (eq class (find-class 'fare-class)))))
467 ;;;; No bogus violations from DECLARE's done by PCL behind the
468 ;;;; scenes. Reported by David Wragg on sbcl-help.
471 (defmethod pcl-type-declaration-method-bug ((test:*special* stream))
473 (assert (eq *terminal-io* (pcl-type-declaration-method-bug *terminal-io*)))
474 (assert (raises-error?
475 (eval '(defmethod pcl-type-declaration-method-bug ((test:*special* stream))
476 (declare (type stream test:*special*))
478 package-lock-violation))
481 (sb-ext:quit :unix-status 104)