diff options
| author | Stefan Monnier | 2017-03-24 09:21:52 -0400 |
|---|---|---|
| committer | Lars Brinkhoff | 2017-03-30 18:31:27 +0200 |
| commit | 390612eb7ab8ccf0792fda7c48b7056c5cda9b06 (patch) | |
| tree | 71766520a963d9c970cd72e8a2022302661a9f94 | |
| parent | 43cb754a3109ac9afaeab1f08e35673b078a7a09 (diff) | |
| download | emacs-scratch/record.tar.gz emacs-scratch/record.zip | |
Backward compatibility with pre-existing struct instances.scratch/record
* lisp/emacs-lisp/cl-lib.el (cl--old-struct-type-of): New function.
(cl-old-struct-compat-mode): New minor mode.
* lisp/emacs-lisp/cl-macs.el (cl-defstruct): Pass `record' to
cl-struct-define to signal use of record objects.
* lisp/emacs-lisp/cl-preloaded.el (cl--struct-get-class,
cl-struct-define): Enable legacy defstruct compatibility.
* test/lisp/emacs-lisp/cl-lib-tests.el (cl-lib-old-struct,
old-struct): New tests.
* doc/lispref/elisp.texi, doc/lispref/records.texi: Document
`old-struct-compat'.
Diffstat (limited to '')
| -rw-r--r-- | doc/lispref/elisp.texi | 1 | ||||
| -rw-r--r-- | doc/lispref/records.texi | 17 | ||||
| -rw-r--r-- | lisp/emacs-lisp/cl-lib.el | 36 | ||||
| -rw-r--r-- | lisp/emacs-lisp/cl-macs.el | 4 | ||||
| -rw-r--r-- | lisp/emacs-lisp/cl-preloaded.el | 6 | ||||
| -rw-r--r-- | test/lisp/emacs-lisp/cl-lib-tests.el | 23 |
6 files changed, 84 insertions, 3 deletions
diff --git a/doc/lispref/elisp.texi b/doc/lispref/elisp.texi index 0f7efb6f187..3a348aae98e 100644 --- a/doc/lispref/elisp.texi +++ b/doc/lispref/elisp.texi | |||
| @@ -423,6 +423,7 @@ Sequences, Arrays, and Vectors | |||
| 423 | Records | 423 | Records |
| 424 | 424 | ||
| 425 | * Record Functions:: Functions for records. | 425 | * Record Functions:: Functions for records. |
| 426 | * Backward Compatibility:: Compatibility for cl-defstruct. | ||
| 426 | 427 | ||
| 427 | Hash Tables | 428 | Hash Tables |
| 428 | 429 | ||
diff --git a/doc/lispref/records.texi b/doc/lispref/records.texi index 822fd2bf36e..9a5d900cfc9 100644 --- a/doc/lispref/records.texi +++ b/doc/lispref/records.texi | |||
| @@ -26,7 +26,8 @@ evaluating it is the same record. This does not evaluate or even | |||
| 26 | examine the slots. @xref{Self-Evaluating Forms}. | 26 | examine the slots. @xref{Self-Evaluating Forms}. |
| 27 | 27 | ||
| 28 | @menu | 28 | @menu |
| 29 | * Record Functions:: Functions for records. | 29 | * Record Functions:: Functions for records. |
| 30 | * Backward Compatibility:: Compatibility for cl-defstruct. | ||
| 30 | @end menu | 31 | @end menu |
| 31 | 32 | ||
| 32 | @node Record Functions | 33 | @node Record Functions |
| @@ -98,3 +99,17 @@ the copied record, are also visible in the original record. | |||
| 98 | @end group | 99 | @end group |
| 99 | @end example | 100 | @end example |
| 100 | @end defun | 101 | @end defun |
| 102 | |||
| 103 | @node Backward Compatibility | ||
| 104 | @section Backward Compatibility | ||
| 105 | |||
| 106 | Code compiled with older versions of @code{cl-defstruct} that | ||
| 107 | doesn't use records may run into problems when used in a new Emacs. | ||
| 108 | To alleviate this, Emacs detects when an old @code{cl-defstruct} is | ||
| 109 | used, and enables a mode in which @code{type-of} handles old struct | ||
| 110 | objects as if they were records. | ||
| 111 | |||
| 112 | @defun cl-old-struct-compat-mode arg | ||
| 113 | If @var{arg} is positive, enable backward compatibility with old-style | ||
| 114 | structs. | ||
| 115 | @end defun | ||
diff --git a/lisp/emacs-lisp/cl-lib.el b/lisp/emacs-lisp/cl-lib.el index 8c4455a3dad..1f8615fad3e 100644 --- a/lisp/emacs-lisp/cl-lib.el +++ b/lisp/emacs-lisp/cl-lib.el | |||
| @@ -639,6 +639,42 @@ If ALIST is non-nil, the new pairs are prepended to it." | |||
| 639 | (require 'cl-macs) | 639 | (require 'cl-macs) |
| 640 | (require 'cl-seq)) | 640 | (require 'cl-seq)) |
| 641 | 641 | ||
| 642 | (defun cl--old-struct-type-of (orig-fun object) | ||
| 643 | (or (and (vectorp object) | ||
| 644 | (let ((tag (aref object 0))) | ||
| 645 | (when (and (symbolp tag) | ||
| 646 | (string-prefix-p "cl-struct-" (symbol-name tag))) | ||
| 647 | (unless (eq (symbol-function tag) | ||
| 648 | :quick-object-witness-check) | ||
| 649 | ;; Old-style old-style struct: | ||
| 650 | ;; Convert to new-style old-style struct! | ||
| 651 | (let* ((type (intern (substring (symbol-name tag) | ||
| 652 | (length "cl-struct-")))) | ||
| 653 | (class (cl--struct-get-class type))) | ||
| 654 | ;; If the `cl-defstruct' was recompiled after the code | ||
| 655 | ;; which constructed `object', `cl--struct-get-class' may | ||
| 656 | ;; not have called `cl-struct-define' and setup the tag | ||
| 657 | ;; symbol for us. | ||
| 658 | (unless (eq (symbol-function tag) | ||
| 659 | :quick-object-witness-check) | ||
| 660 | (set tag class) | ||
| 661 | (fset tag :quick-object-witness-check)))) | ||
| 662 | (cl--class-name (symbol-value tag))))) | ||
| 663 | (funcall orig-fun object))) | ||
| 664 | |||
| 665 | ;;;###autoload | ||
| 666 | (define-minor-mode cl-old-struct-compat-mode | ||
| 667 | "Enable backward compatibility with old-style structs. | ||
| 668 | This can be needed when using code byte-compiled using the old | ||
| 669 | macro-expansion of `cl-defstruct' that used vectors objects instead | ||
| 670 | of record objects." | ||
| 671 | :global t | ||
| 672 | (cond | ||
| 673 | (cl-old-struct-compat-mode | ||
| 674 | (advice-add 'type-of :around #'cl--old-struct-type-of)) | ||
| 675 | (t | ||
| 676 | (advice-remove 'type-of #'cl--old-struct-type-of)))) | ||
| 677 | |||
| 642 | ;; Local variables: | 678 | ;; Local variables: |
| 643 | ;; byte-compile-dynamic: t | 679 | ;; byte-compile-dynamic: t |
| 644 | ;; End: | 680 | ;; End: |
diff --git a/lisp/emacs-lisp/cl-macs.el b/lisp/emacs-lisp/cl-macs.el index c282938a9bf..25c9f999920 100644 --- a/lisp/emacs-lisp/cl-macs.el +++ b/lisp/emacs-lisp/cl-macs.el | |||
| @@ -2847,8 +2847,8 @@ non-nil value, that slot cannot be set via `setf'. | |||
| 2847 | ;; struct as a parent. | 2847 | ;; struct as a parent. |
| 2848 | (eval-and-compile | 2848 | (eval-and-compile |
| 2849 | (cl-struct-define ',name ,docstring ',include-name | 2849 | (cl-struct-define ',name ,docstring ',include-name |
| 2850 | ',type ,(eq named t) ',descs ',tag-symbol ',tag | 2850 | ',(or type 'record) ,(eq named t) ',descs |
| 2851 | ',print-auto)) | 2851 | ',tag-symbol ',tag ',print-auto)) |
| 2852 | ',name))) | 2852 | ',name))) |
| 2853 | 2853 | ||
| 2854 | ;;; Add cl-struct support to pcase | 2854 | ;;; Add cl-struct support to pcase |
diff --git a/lisp/emacs-lisp/cl-preloaded.el b/lisp/emacs-lisp/cl-preloaded.el index 7432dd4978d..ab6354de7cd 100644 --- a/lisp/emacs-lisp/cl-preloaded.el +++ b/lisp/emacs-lisp/cl-preloaded.el | |||
| @@ -110,6 +110,12 @@ | |||
| 110 | ;;;###autoload | 110 | ;;;###autoload |
| 111 | (defun cl-struct-define (name docstring parent type named slots children-sym | 111 | (defun cl-struct-define (name docstring parent type named slots children-sym |
| 112 | tag print) | 112 | tag print) |
| 113 | (unless type | ||
| 114 | ;; Legacy defstruct, using tagged vectors. Enable backward compatibility. | ||
| 115 | (cl-old-struct-compat-mode 1)) | ||
| 116 | (if (eq type 'record) | ||
| 117 | ;; Defstruct using record objects. | ||
| 118 | (setq type nil)) | ||
| 113 | (cl-assert (or type (not named))) | 119 | (cl-assert (or type (not named))) |
| 114 | (if (boundp children-sym) | 120 | (if (boundp children-sym) |
| 115 | (add-to-list children-sym tag) | 121 | (add-to-list children-sym tag) |
diff --git a/test/lisp/emacs-lisp/cl-lib-tests.el b/test/lisp/emacs-lisp/cl-lib-tests.el index 26b19e93e42..98c4bd92de6 100644 --- a/test/lisp/emacs-lisp/cl-lib-tests.el +++ b/test/lisp/emacs-lisp/cl-lib-tests.el | |||
| @@ -500,4 +500,27 @@ | |||
| 500 | (should (eq (type-of x) 'foo)) | 500 | (should (eq (type-of x) 'foo)) |
| 501 | (should (eql (foo-x x) 42)))) | 501 | (should (eql (foo-x x) 42)))) |
| 502 | 502 | ||
| 503 | (ert-deftest old-struct () | ||
| 504 | (cl-defstruct foo x) | ||
| 505 | (let ((x [cl-struct-foo]) | ||
| 506 | (saved cl-old-struct-compat-mode)) | ||
| 507 | (cl-old-struct-compat-mode -1) | ||
| 508 | (should (eq (type-of x) 'vector)) | ||
| 509 | |||
| 510 | (cl-old-struct-compat-mode 1) | ||
| 511 | (setq cl-struct-foo (cl--struct-get-class 'foo)) | ||
| 512 | (setf (symbol-function 'cl-struct-foo) :quick-object-witness-check) | ||
| 513 | (should (eq (type-of x) 'foo)) | ||
| 514 | (should (eq (type-of [foo]) 'vector)) | ||
| 515 | |||
| 516 | (cl-old-struct-compat-mode (if saved 1 -1)))) | ||
| 517 | |||
| 518 | (ert-deftest cl-lib-old-struct () | ||
| 519 | (let ((saved cl-old-struct-compat-mode)) | ||
| 520 | (cl-old-struct-compat-mode -1) | ||
| 521 | (cl-struct-define 'foo "" 'cl-structure-object nil nil nil | ||
| 522 | 'cl-struct-foo-tags 'cl-struct-foo t) | ||
| 523 | (should cl-old-struct-compat-mode) | ||
| 524 | (cl-old-struct-compat-mode (if saved 1 -1)))) | ||
| 525 | |||
| 503 | ;;; cl-lib.el ends here | 526 | ;;; cl-lib.el ends here |