-
Notifications
You must be signed in to change notification settings - Fork 312
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Restore instances to their previous state on failed U-I-F-{R,D}-C
Neither the standard nor the MOP specify what happens when an error is signaled in U-I-F-R-C or U-I-F-D-C and control is transferred outside. This may result in half-baked instances which, respectively, already have their structure or class changed, but whose newly added slots are not yet initialized. There is no good way to "fix" the instances afterwards since, for all purposes, SBCL considers them to be "updated" even though their slot values are undefined. This commit modifies the existing behavior in functions %OBSOLETE-INSTANCE-TRAP and %CHANGE-CLASS by effectively rolling back the transaction of updating an instance or changing its class if U-I-F-{R,D}-C does not exit normally but via a nonlocal exit. The new behavior is actually useful because the programmer can fix their U-I-F-{R,D}-C code up and either attempt to access the instance again (to trigger the update process) or call CHANGE-CLASS on it again.
- Loading branch information
Showing
3 changed files
with
153 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
;;;; Handling errors in UPDATE-INSTANCE-FOR-REDEFINED-CLASS | ||
|
||
;;;; This software is part of the SBCL system. See the README file for | ||
;;;; more information. | ||
;;;; | ||
;;;; While most of SBCL is derived from the CMU CL system, the test | ||
;;;; files (like this one) were written from scratch after the fork | ||
;;;; from CMU CL. | ||
;;;; | ||
;;;; This software is in the public domain and is provided with | ||
;;;; absolutely no warranty. See the COPYING and CREDITS files for | ||
;;;; more information. | ||
|
||
(defclass foo-class (standard-class) ()) | ||
|
||
(defmethod sb-mop:validate-superclass ((c foo-class) (s standard-class)) t) | ||
|
||
(defclass foo-object (standard-object) ()) | ||
|
||
(defmethod shared-initialize :around ((class foo-class) slot-names | ||
&rest rest | ||
&key direct-superclasses) | ||
(apply #'call-next-method | ||
class slot-names | ||
:direct-superclasses | ||
(append (remove (find-class 'standard-object) direct-superclasses) | ||
(list (find-class 'foo-object))) | ||
rest)) | ||
|
||
(defmethod update-instance-for-redefined-class :before | ||
((instance foo-object) | ||
added-slots discarded-slots | ||
property-list | ||
&rest initargs) | ||
(declare (ignore initargs)) | ||
;; This U-I-F-R-C is meant to always signal an error. | ||
(error "expected failure")) | ||
|
||
;;; Define FOO. | ||
(defclass foo () () (:metaclass foo-class)) | ||
|
||
;;; Make an instance of FOO. | ||
(defparameter *foo* (make-instance 'foo)) | ||
|
||
;;; Redefine FOO, causing *FOO* to become obsolete. | ||
(defclass foo () | ||
((slot :initform 42)) | ||
(:metaclass foo-class)) | ||
|
||
;;; This should result in an "expected failure" error, because | ||
;;; the instance is obsolete. | ||
(multiple-value-bind (result error) | ||
(ignore-errors (slot-value *foo* 'slot)) | ||
(assert (null result)) | ||
(assert (string= (princ-to-string error) "expected failure"))) | ||
|
||
;;; This should *also* result in an "expected failure" error, because after | ||
;;; the previous U-I-F-R-C call made a non-local exit, the instance should be | ||
;;; automatically made obsolete once more. | ||
(multiple-value-bind (result error) | ||
(ignore-errors (slot-value *foo* 'slot)) | ||
(assert (null result)) | ||
(assert (string= (princ-to-string error) "expected failure"))) | ||
|
||
;;; Redefine the U-I-F-R-C method to no longer signal an error. | ||
(defmethod update-instance-for-redefined-class :before | ||
((instance foo-object) | ||
added-slots discarded-slots | ||
property-list | ||
&rest initargs) | ||
(declare (ignore initargs))) | ||
|
||
;;; Instance is now updateable. It should now be possible to access the new slot | ||
;;; and fetch its initform-initialized value. | ||
(assert (= 42 (slot-value *foo* 'slot))) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
;;;; Handling errors in UPDATE-INSTANCE-FOR-DIFFERENT-CLASS | ||
|
||
;;;; This software is part of the SBCL system. See the README file for | ||
;;;; more information. | ||
;;;; | ||
;;;; While most of SBCL is derived from the CMU CL system, the test | ||
;;;; files (like this one) were written from scratch after the fork | ||
;;;; from CMU CL. | ||
;;;; | ||
;;;; This software is in the public domain and is provided with | ||
;;;; absolutely no warranty. See the COPYING and CREDITS files for | ||
;;;; more information. | ||
|
||
(defclass foo () ()) | ||
|
||
(defclass bar () ((slot :initform 42))) | ||
|
||
(defmethod update-instance-for-different-class :before | ||
((foo foo) (bar bar) &rest initargs) | ||
(declare (ignore initargs)) | ||
;; This U-I-F-D-C is meant to always signal an error. | ||
(error "expected failure")) | ||
|
||
;;; Make an instance of FOO. | ||
(defparameter *foo* (make-instance 'foo)) | ||
|
||
;;; This should result in an "expected failure" error. | ||
(multiple-value-bind (result error) | ||
(ignore-errors (change-class *foo* 'bar)) | ||
(assert (null result)) | ||
(assert (string= (princ-to-string error) "expected failure"))) | ||
|
||
;;; This should *also* result in an "expected failure" error, because after | ||
;;; the previous U-I-F-D-C call made a non-local exit, the instance should be | ||
;;; automatically restored to its previous class. | ||
(multiple-value-bind (result error) | ||
(ignore-errors (change-class *foo* 'bar)) | ||
(assert (null result)) | ||
(assert (string= (princ-to-string error) "expected failure"))) | ||
|
||
;;; Redefine the U-I-F-D-C method to no longer signal an error. | ||
(defmethod update-instance-for-different-class :before | ||
((foo foo) (bar bar) &rest initargs) | ||
(declare (ignore initargs))) | ||
|
||
;;; It is now possible to change the instance's class. | ||
(change-class *foo* 'bar) | ||
|
||
;;; It should now be possible to access the new slot and fetch its | ||
;;; initform-initialized value. | ||
(assert (= 42 (slot-value *foo* 'slot))) |
9dcaef6
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Found an issue in test
mop-33.impure.lisp
. On L32 and L40, there should be checks if*foo*
is still of typefoo
. On L48 - if it is of typebar
.It would be even better if class
foo
had a slot that would be removed on a change to classbar
, and if the value of that slot was also asserted on L32 and L40.