unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
* bug#36407: 27.0.50; `plist-get', `equal' etc. and circular "lists"
@ 2019-06-27 21:43 Pip Cet
  2019-06-27 22:51 ` Paul Eggert
  0 siblings, 1 reply; 6+ messages in thread
From: Pip Cet @ 2019-06-27 21:43 UTC (permalink / raw)
  To: 36407

[-- Attachment #1: Type: text/plain, Size: 689 bytes --]

plist-get currently contains this code:

  FOR_EACH_TAIL_SAFE (tail)
    {
      <check for success>
      tail = XCDR (tail);
      if (EQ (tail, li.tortoise))
        break;
    }

I don't understand why the last two lines are there. They're
unnecessary for proper plists; for circular plists, they result in
unintuitive behavior; and they depend on details of the
FOR_EACH_TAIL_SAFE implementation.

Can someone enlighten me?

As a tangential issue, shouldn't `equal' be symmetric?

(let* ((l1 '#1=(0 1 2 . #1#))
       (l2 '(0 1 2 0 1 2 . #1#)))
  (equal l2 l1) => t
  (equal l1 l2) => "List contains a loop" error.
  (plist-get l2 1) => 2
  (plist-get l1 1) => nil

Patches attached.

[-- Attachment #2: 0001-Remove-unnecessary-tortoise-checks.patch --]
[-- Type: text/x-patch, Size: 2326 bytes --]

From 722b621835b1470f420ad9610f80f50f4b31a5c6 Mon Sep 17 00:00:00 2001
From: Pip Cet <pipcet@gmail.com>
Date: Thu, 27 Jun 2019 20:11:52 +0000
Subject: [PATCH] Remove unnecessary tortoise checks.

* src/fns.c (Fplist_get, Fplist_put, Flax_plist_get)
(Flax_plist_put, Fplist_member): Remove unnecessary check.
* src/json.c (lisp_to_json_toplevel_1): Remove unnecessary check.
---
 src/fns.c  | 10 ----------
 src/json.c |  1 -
 2 files changed, 11 deletions(-)

diff --git a/src/fns.c b/src/fns.c
index fd0c7fc71a..2fc000a7f4 100644
--- a/src/fns.c
+++ b/src/fns.c
@@ -2164,8 +2164,6 @@ DEFUN ("plist-get", Fplist_get, Splist_get, 2, 2, 0,
       if (EQ (prop, XCAR (tail)))
 	return XCAR (XCDR (tail));
       tail = XCDR (tail);
-      if (EQ (tail, li.tortoise))
-	break;
     }
 
   return Qnil;
@@ -2208,8 +2206,6 @@ DEFUN ("plist-put", Fplist_put, Splist_put, 3, 3, 0,
 
       prev = tail;
       tail = XCDR (tail);
-      if (EQ (tail, li.tortoise))
-	circular_list (plist);
     }
   CHECK_TYPE (NILP (tail), Qplistp, plist);
   Lisp_Object newcell
@@ -2247,8 +2243,6 @@ DEFUN ("lax-plist-get", Flax_plist_get, Slax_plist_get, 2, 2, 0,
       if (! NILP (Fequal (prop, XCAR (tail))))
 	return XCAR (XCDR (tail));
       tail = XCDR (tail);
-      if (EQ (tail, li.tortoise))
-	circular_list (plist);
     }
 
   CHECK_TYPE (NILP (tail), Qplistp, plist);
@@ -2280,8 +2274,6 @@ DEFUN ("lax-plist-put", Flax_plist_put, Slax_plist_put, 3, 3, 0,
 
       prev = tail;
       tail = XCDR (tail);
-      if (EQ (tail, li.tortoise))
-	circular_list (plist);
     }
   CHECK_TYPE (NILP (tail), Qplistp, plist);
   Lisp_Object newcell = list2 (prop, val);
@@ -3045,8 +3037,6 @@ DEFUN ("plist-member", Fplist_member, Splist_member, 2, 2, 0,
       tail = XCDR (tail);
       if (! CONSP (tail))
 	break;
-      if (EQ (tail, li.tortoise))
-	circular_list (tail);
     }
   CHECK_TYPE (NILP (tail), Qplistp, plist);
   return Qnil;
diff --git a/src/json.c b/src/json.c
index 23234c767d..48820a1cb0 100644
--- a/src/json.c
+++ b/src/json.c
@@ -404,7 +404,6 @@ lisp_to_json_toplevel_1 (Lisp_Object lisp,
               tail = XCDR (tail);
               CHECK_CONS (tail);
               value = XCAR (tail);
-              if (EQ (tail, li.tortoise)) circular_list (lisp);
             }
           else
             {
-- 
2.20.1


[-- Attachment #3: 0001-Make-equal-symmetric.patch --]
[-- Type: text/x-patch, Size: 1587 bytes --]

From 57c8e010cea8dfb8d0e6d54992a7543c640c4f9f Mon Sep 17 00:00:00 2001
From: Pip Cet <pipcet@gmail.com>
Date: Thu, 27 Jun 2019 21:04:18 +0000
Subject: [PATCH] Make `equal' symmetric.

* src/fns.c (internal_equal): Make symmetric. Copy tortoise-hare
  algorithm from lisp.h
---
 src/fns.c | 23 +++++++++++++++++------
 1 file changed, 17 insertions(+), 6 deletions(-)

diff --git a/src/fns.c b/src/fns.c
index 2fc000a7f4..623435445a 100644
--- a/src/fns.c
+++ b/src/fns.c
@@ -2401,16 +2401,27 @@ internal_equal (Lisp_Object o1, Lisp_Object o2, enum equal_kind equal_kind,
 	      return true;
 	  }
       else
-	FOR_EACH_TAIL (o1)
+	for (struct for_each_tail_internal li1 = { o1, 2, 0, 2 },
+	       li2 = { o2, 2, 0, 2 };
+	     CONSP (o1) && CONSP (o2);
+	     (o1 = XCDR (o1),
+	      o2 = XCDR (o2),
+	      ((--li1.q != 0
+		|| (maybe_quit (), 0 < --li1.n)
+		|| (li1.q = li1.n = li1.max <<= 1, li1.n >>= USHRT_WIDTH,
+		    li1.tortoise = (o1), false))
+	       && EQ (o1, li1.tortoise)) ? circular_list (o1) : (void) 0,
+	      ((--li2.q != 0
+		|| (maybe_quit (), 0 < --li2.n)
+		|| (li2.q = li2.n = li2.max <<= 1, li2.n >>= USHRT_WIDTH,
+		    li2.tortoise = (o2), false))
+	       && EQ (o2, li2.tortoise)) ? circular_list (o2) : (void) 0))
 	  {
-	    if (! CONSP (o2))
-	      return false;
+	    if (EQ (o1, o2))
+	      return true;
 	    if (! internal_equal (XCAR (o1), XCAR (o2),
 				  equal_kind, depth + 1, ht))
 	      return false;
-	    o2 = XCDR (o2);
-	    if (EQ (XCDR (o1), o2))
-	      return true;
 	  }
       depth++;
       goto tail_recurse;
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* bug#36407: 27.0.50; `plist-get', `equal' etc. and circular "lists"
  2019-06-27 21:43 bug#36407: 27.0.50; `plist-get', `equal' etc. and circular "lists" Pip Cet
@ 2019-06-27 22:51 ` Paul Eggert
  2019-06-28  8:05   ` Pip Cet
  2019-06-29  2:01   ` Glenn Morris
  0 siblings, 2 replies; 6+ messages in thread
From: Paul Eggert @ 2019-06-27 22:51 UTC (permalink / raw)
  To: Pip Cet; +Cc: 36407

[-- Attachment #1: Type: text/plain, Size: 878 bytes --]

> I don't understand why the last two lines are there.

I thought those lines were needed to avoid an infinite loop in 
pathological cyclic cases. But on further thought you're right, they 
aren't needed. I installed that patch; thanks.

> shouldn't `equal' be symmetric?

Yes, on its domain. But circular lists are outside its domain, and the 
documentation doesn't promise any particular behavior on them. It's OK 
if (equal a b) signals an error and (equal b a) does not. It's even OK 
if (equal a b) signals an error and a later call (equal a b) with 
exactly the same (unchanged) arguments does not (because the stack 
happens to have more room the second time). We still have symmetry in 
the sense that (eq (equal a b) (equal b a)) always either returns t or 
signals an error; it never returns nil.

I installed the attached doc patch to try to make this a bit clearer.

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Improve-equal-and-array-doc.patch --]
[-- Type: text/x-patch; name="0001-Improve-equal-and-array-doc.patch", Size: 3389 bytes --]

From bdbb390ffef9f8b4eab263055723b27edad7b91c Mon Sep 17 00:00:00 2001
From: Paul Eggert <eggert@cs.ucla.edu>
Date: Thu, 27 Jun 2019 15:39:04 -0700
Subject: [PATCH] =?UTF-8?q?Improve=20=E2=80=98equal=E2=80=99=20and=20array?=
 =?UTF-8?q?=20doc?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* doc/lispref/objects.texi (Array Type): Array sizes are
nonnegative fixnums, not arbitrary integers.
(Equality Predicates): Do not say that ‘eq’ equals ‘=’ on bignums.
Do not imply that ‘equal’ must signal an error on circular lists.
---
 doc/lispref/objects.texi | 15 +++++++++------
 1 file changed, 9 insertions(+), 6 deletions(-)

diff --git a/doc/lispref/objects.texi b/doc/lispref/objects.texi
index 745baacc29..2e8e2ee714 100644
--- a/doc/lispref/objects.texi
+++ b/doc/lispref/objects.texi
@@ -964,7 +964,8 @@ Array Type
 
   A string is an array of characters and a vector is an array of
 arbitrary objects.  A bool-vector can hold only @code{t} or @code{nil}.
-These kinds of array may have any length up to the largest integer.
+These kinds of array may have any length up to the largest fixnum,
+subject to system architecture limits and available memory.
 Char-tables are sparse arrays indexed by any valid character code; they
 can hold arbitrary objects.
 
@@ -2085,7 +2086,7 @@ Equality Predicates
 This function returns @code{t} if @var{object1} and @var{object2} are
 the same object, and @code{nil} otherwise.
 
-If @var{object1} and @var{object2} are integers with the same value,
+If @var{object1} and @var{object2} are fixnums with the same value,
 they are considered to be the same object (i.e., @code{eq} returns
 @code{t}).  If @var{object1} and @var{object2} are symbols with the
 same name, they are normally the same object---but see @ref{Creating
@@ -2095,7 +2096,7 @@ Equality Predicates
 are the same object, meaning that a change in the contents of one will
 be reflected by the same change in the contents of the other.
 For other types of objects whose contents cannot be changed (e.g.,
-floats), two arguments with the same contents might or might not be
+bignums and floats), two arguments with the same contents might or might not be
 the same object, and @code{eq} returns @code{t} or @code{nil}
 depending on whether the Lisp interpreter created one object or two.
 
@@ -2258,7 +2259,7 @@ Equality Predicates
 their textual contents are the same.
 @end defun
 
-  The test for equality is implemented recursively; for example, given
+  For @code{equal}, equality is defined recursively; for example, given
 two cons cells @var{x} and @var{y}, @code{(equal @var{x} @var{y})}
 returns @code{t} if and only if both the expressions below return
 @code{t}:
@@ -2268,8 +2269,10 @@ Equality Predicates
 (equal (cdr @var{x}) (cdr @var{y}))
 @end example
 
-Because of this recursive method, circular lists may therefore cause
-infinite recursion (leading to an error).
+Comparing circular lists may therefore cause deep recursion that leads
+to an error, and this may result in counterintuitive behavior such as
+@code{(equal a b)} returning @code{t} whereas @code{(equal b a)}
+signals an error.
 
 @defun equal-including-properties object1 object2
 This function behaves like @code{equal} in all cases but also requires
-- 
2.21.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* bug#36407: 27.0.50; `plist-get', `equal' etc. and circular "lists"
  2019-06-27 22:51 ` Paul Eggert
@ 2019-06-28  8:05   ` Pip Cet
  2019-06-29  2:01   ` Glenn Morris
  1 sibling, 0 replies; 6+ messages in thread
From: Pip Cet @ 2019-06-28  8:05 UTC (permalink / raw)
  To: Paul Eggert; +Cc: 36407-done

On Thu, Jun 27, 2019 at 10:52 PM Paul Eggert <eggert@cs.ucla.edu> wrote:
> aren't needed. I installed that patch; thanks.

Thanks!

> > shouldn't `equal' be symmetric?
>
> Yes, on its domain. But circular lists are outside its domain, and the
> documentation doesn't promise any particular behavior on them. It's OK
> if (equal a b) signals an error and (equal b a) does not. It's even OK
> if (equal a b) signals an error and a later call (equal a b) with
> exactly the same (unchanged) arguments does not (because the stack
> happens to have more room the second time). We still have symmetry in
> the sense that (eq (equal a b) (equal b a)) always either returns t or
> signals an error; it never returns nil.

Thanks for your explanation, that makes perfect sense.

I was confused, in part, by the hash table code in internal_equal,
which appears to be designed to handle circular structures with some
generality.

On further thought, maybe that code is written for DAGs which contain
diamond-shaped subgraphs. However, those don't appear to be working
very well...

> I installed the attached doc patch to try to make this a bit clearer.

Thanks again! I'm closing this bug.





^ permalink raw reply	[flat|nested] 6+ messages in thread

* bug#36407: 27.0.50; `plist-get', `equal' etc. and circular "lists"
  2019-06-27 22:51 ` Paul Eggert
  2019-06-28  8:05   ` Pip Cet
@ 2019-06-29  2:01   ` Glenn Morris
  2019-06-29  4:16     ` Pip Cet
  1 sibling, 1 reply; 6+ messages in thread
From: Glenn Morris @ 2019-06-29  2:01 UTC (permalink / raw)
  To: Paul Eggert; +Cc: 36407, Pip Cet


This causes the test json-serialize/object to fail. Ref eg

https://hydra.nixos.org/build/95582609





^ permalink raw reply	[flat|nested] 6+ messages in thread

* bug#36407: 27.0.50; `plist-get', `equal' etc. and circular "lists"
  2019-06-29  2:01   ` Glenn Morris
@ 2019-06-29  4:16     ` Pip Cet
  2019-06-29  5:02       ` Paul Eggert
  0 siblings, 1 reply; 6+ messages in thread
From: Pip Cet @ 2019-06-29  4:16 UTC (permalink / raw)
  To: Glenn Morris; +Cc: 36407, Paul Eggert

[-- Attachment #1: Type: text/plain, Size: 353 bytes --]

On Sat, Jun 29, 2019 at 2:01 AM Glenn Morris <rgm@gnu.org> wrote:
> This causes the test json-serialize/object to fail. Ref eg
>
> https://hydra.nixos.org/build/95582609

So it does. It actually tests an odd-length circular "plist", which
fails both before and after the change, but fails with a different
error code. Modification to the test attached.

[-- Attachment #2: 0001-Fix-json-serialize-object-test-failure.patch --]
[-- Type: text/x-patch, Size: 1203 bytes --]

From a25a64203e3057b31fd2afc48bd762cc8a807d9d Mon Sep 17 00:00:00 2001
From: Pip Cet <pipcet@gmail.com>
Date: Sat, 29 Jun 2019 04:14:35 +0000
Subject: [PATCH] Fix json-serialize/object test failure.

* test/src/json-tests.el (json-serialize/object): Accept failure with
  different code.
---
 test/src/json-tests.el | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/test/src/json-tests.el b/test/src/json-tests.el
index 7d824b5c95..383991b073 100644
--- a/test/src/json-tests.el
+++ b/test/src/json-tests.el
@@ -76,7 +76,8 @@ json-serialize/object
   (should (equal (json-serialize '(abc [1 2 t] :def :null))
                  "{\"abc\":[1,2,true],\"def\":null}"))
   (should-error (json-serialize '#1=(:a 1 . #1#)) :type 'circular-list)
-  (should-error (json-serialize '#1=(:a 1 :b . #1#)) :type 'circular-list)
+  (should-error (json-serialize '#1=(:a 1 :b . #1#)):type '(circular-list
+                                                            wrong-type-argument))
   (should-error (json-serialize '(:foo "bar" (unexpected-alist-key . 1)))
                 :type 'wrong-type-argument)
   (should-error (json-serialize '((abc . "abc") :unexpected-plist-key "key"))
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* bug#36407: 27.0.50; `plist-get', `equal' etc. and circular "lists"
  2019-06-29  4:16     ` Pip Cet
@ 2019-06-29  5:02       ` Paul Eggert
  0 siblings, 0 replies; 6+ messages in thread
From: Paul Eggert @ 2019-06-29  5:02 UTC (permalink / raw)
  To: Pip Cet, Glenn Morris; +Cc: 36407

Thanks, I installed that fix to the test case.





^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2019-06-29  5:02 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2019-06-27 21:43 bug#36407: 27.0.50; `plist-get', `equal' etc. and circular "lists" Pip Cet
2019-06-27 22:51 ` Paul Eggert
2019-06-28  8:05   ` Pip Cet
2019-06-29  2:01   ` Glenn Morris
2019-06-29  4:16     ` Pip Cet
2019-06-29  5:02       ` Paul Eggert

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).