Subject: bug#29220: 26.0.90; eieio-persistent-read fail to restore saved object.

From: Noam Postavsky <[email-masked]>
Date: Mon, 27 May 2019 19:36:04 -0400

Eric Abrahamsen <[email-masked]> writes:

>>> Bah, this is annoyingly difficult -- thank you for trying it out. I
>>> suppose one terrible heuristic would be if the errors were different
>>> between emacs-25, emacs-26, and fix/eieio-persistent. Would you mind
>>> sending me the error output, in case anything jumps out at me?
>>>
>> Hope this could help you.
>
> Thanks. They all end in "Corrupt object on disk", which is an
> eieio-persist problem. I'll keep working on this.

So, I think it would be nice to have this fixed for 26.3.  It sounds
like the fix you're working on is too risky to have on a release branch
regardless of its current status, so here's a patch which makes eieio
objects use symbols as type tags, when eieio-backward-compatibility is
non-nil.  Folks who want the record-with-circular-references as type
tags can still get that by setting eieio-backward-compatibility to nil.

>From f6cc4509073da4ff23a4227d48b33108b1e96828 Mon Sep 17 00:00:00 2001
From: Noam Postavsky <[email-masked]>
Date: Mon, 27 May 2019 19:05:56 -0400
Subject: [PATCH] Use plain symbols for eieio type descriptors (Bug#29220)

Since Emacs 26, eieio objects use a class record (with circular
references) as the type descriptor of the object record.  This causes
problems when reading back an object from a string, because the class
record is not `eq' to the canonical one (which means that read objects
don't satisfy the foo-p predicate).
* lisp/emacs-lisp/eieio.el (make-instance): As a (partial) fix, set
the record's type descriptor to a plain symbol for the type descriptor
when eieio-backward-compatibility is non-nil (the default).
* lisp/emacs-lisp/eieio-core.el (eieio--object-class): Call
eieio--class-object on the type tag when eieio-backward-compatibility
is non-nil.
(eieio-object-p): Use eieio--object-class instead of
eieio--object-class-tag.
* test/lisp/emacs-lisp/eieio-tests/eieio-test-persist.el
(eieio-test-persist-hash-and-vector)
(eieio-test-persist-interior-lists): `persist-test-save-and-compare'
no longer throws an error.
---
 lisp/emacs-lisp/eieio-core.el                          | 11 +++++++----
 lisp/emacs-lisp/eieio.el                               |  3 +++
 test/lisp/emacs-lisp/eieio-tests/eieio-test-persist.el |  6 ++----
 3 files changed, 12 insertions(+), 8 deletions(-)

diff --git a/lisp/emacs-lisp/eieio-core.el b/lisp/emacs-lisp/eieio-core.el
index f879a3999f..4d55ed6e1d 100644
--- a/lisp/emacs-lisp/eieio-core.el
+++ b/lisp/emacs-lisp/eieio-core.el
@@ -117,9 +117,6 @@ (eval-and-compile
 (defsubst eieio--object-class-tag (obj)
   (aref obj 0))
 
-(defsubst eieio--object-class (obj)
-  (eieio--object-class-tag obj))
-
 
 ;;; Important macros used internally in eieio.
 
@@ -132,6 +129,12 @@ (defsubst eieio--class-object (class)
       (or (cl--find-class class) class)
     class))
 
+(defsubst eieio--object-class (obj)
+  (let ((tag (eieio--object-class-tag obj)))
+    (if eieio-backward-compatibility
+        (eieio--class-object tag)
+      tag)))
+
 (defun class-p (x)
   "Return non-nil if X is a valid class vector.
 X can also be is a symbol."
@@ -163,7 +166,7 @@ (defsubst eieio--class-option (class option)
 (defun eieio-object-p (obj)
   "Return non-nil if OBJ is an EIEIO object."
   (and (recordp obj)
-       (eieio--class-p (eieio--object-class-tag obj))))
+       (eieio--class-p (eieio--object-class obj))))
 
 (define-obsolete-function-alias 'object-p 'eieio-object-p "25.1")
 
diff --git a/lisp/emacs-lisp/eieio.el b/lisp/emacs-lisp/eieio.el
index 38436d1f94..864ac2616b 100644
--- a/lisp/emacs-lisp/eieio.el
+++ b/lisp/emacs-lisp/eieio.el
@@ -710,6 +710,9 @@ (cl-defmethod make-instance
     ;; Call the initialize method on the new object with the slots
     ;; that were passed down to us.
     (initialize-instance new-object slots)
+    (when eieio-backward-compatibility
+      ;; Use symbol as type descriptor, for backwards compatibility.
+      (aset new-object 0 class))
     ;; Return the created object.
     new-object))
 
diff --git a/test/lisp/emacs-lisp/eieio-tests/eieio-test-persist.el b/test/lisp/emacs-lisp/eieio-tests/eieio-test-persist.el
index dfaa031844..f8a0aa30dd 100644
--- a/test/lisp/emacs-lisp/eieio-tests/eieio-test-persist.el
+++ b/test/lisp/emacs-lisp/eieio-tests/eieio-test-persist.el
@@ -297,8 +297,7 @@ (ert-deftest eieio-test-persist-hash-and-vector ()
     (aset (car (slot-value class 'janitors)) 1 hans)
     (aset (nth 1 (slot-value class 'janitors)) 1 dierdre)
     (unwind-protect
-        ;; FIXME: This should not error.
-        (should-error (persist-test-save-and-compare class))
+        (persist-test-save-and-compare class)
       (delete-file (oref class file)))))
 
 ;; Extra quotation of lists inside other objects (Gnus registry), also
@@ -335,8 +334,7 @@ (ert-deftest eieio-test-persist-interior-lists ()
     (setf (nth 2 (cadar alst)) john
           (nth 2 (cadadr alst)) alexie)
     (unwind-protect
-        ;; FIXME: Should not error.
-        (should-error (persist-test-save-and-compare thing))
+        (persist-test-save-and-compare thing)
       (delete-file (slot-value thing 'file)))))
 
 ;;; eieio-test-persist.el ends here
-- 
2.11.0