Correct the handling of symbols with position in equal

* src/fns.c (internal_equal): Only regard symbols with position
as their symbols when symbols-with-pos-enabled is non-nil.

* doc/lispref/symbols.texi (Symbols with Position): Expand the
description of symbols with position, in particular the way
they work with eq and equal.

* doc/lispref/objects.texi (Equality Predicates): Describe how
eq and equal handle symbols with position.

* test/src/fns-tests.el (fns-tests-equal-symbols-with-position):
New tests for symbols with position.
This commit is contained in:
Alan Mackenzie 2023-09-04 12:51:24 +00:00
parent 55a0f0e047
commit afcb6d0bc7
4 changed files with 72 additions and 19 deletions

View file

@ -2206,6 +2206,10 @@ and the same non-fixnum numeric type, then they 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.
If @var{object1} or @var{object2} is a symbol with position, @code{eq}
regards it as its bare symbol when @code{symbols-with-pos-enabled} is
non-@code{nil} (@pxref{Symbols with Position}).
@example
@group
(eq 'foo 'foo)
@ -2363,6 +2367,13 @@ same sequence of character codes and all these codes are in the range
The @code{equal} function recursively compares the contents of objects
if they are integers, strings, markers, vectors, bool-vectors,
byte-code function objects, char-tables, records, or font objects.
If @var{object1} or @var{object2} is a symbol with position,
@code{equal} regards it as its bare symbol when
@code{symbols-with-pos-enabled} is non-@code{nil}. Otherwise
@code{equal} compares two symbols with position by recursively
comparing their components. @xref{Symbols with Position}.
Other objects are considered @code{equal} only if they are @code{eq}.
For example, two distinct buffers are never considered @code{equal},
even if their textual contents are the same.

View file

@ -783,10 +783,15 @@ Symbol forms whose names start with @samp{#_} are not transformed.
@cindex bare symbol
A @dfn{symbol with position} is a symbol, the @dfn{bare symbol},
together with an unsigned integer called the @dfn{position}. These
objects are intended for use by the byte compiler, which records in
them the position of each symbol occurrence and uses those positions
in warning and error messages.
together with an unsigned integer called the @dfn{position}. Symbols
with position don't themselves have entries in the obarray (though
their bare symbols do; @pxref{Creating Symbols}).
Symbols with position are for the use of the byte compiler, which
records in them the position of each symbol occurrence and uses those
positions in warning and error messages. They shouldn't normally be
used otherwise. Doing so can cause unexpected results with basic
Emacs functions such as @code{eq} and @code{equal}.
The printed representation of a symbol with position uses the hash
notation outlined in @ref{Printed Representation}. It looks like
@ -798,11 +803,20 @@ the compiled Lisp file.
For most purposes, when the flag variable
@code{symbols-with-pos-enabled} is non-@code{nil}, symbols with
positions behave just as bare symbols do. For example, @samp{(eq
#<symbol foo at 12345> foo)} has a value @code{t} when that variable
is set (but @code{nil} when it isn't set). Most of the time in Emacs this
variable is @code{nil}, but the byte compiler binds it to @code{t}
when it runs.
positions behave just as their bare symbols would. For example,
@samp{(eq #<symbol foo at 12345> foo)} has a value @code{t} when the
variable is set; likewise, @code{equal} will treat a symbol with
position argument as its bare symbol.
When @code{symbols-with-pos-enabled} is @code{nil}, any symbols with
position continue to exist, but do not behave as symbols, or have the
other useful properties outlined in the previous paragraph. @code{eq}
returns @code{t} when given identical arguments, and @code{equal}
returns @code{t} when given arguments with @code{equal} components.
Most of the time in Emacs @code{symbols-with-pos-enabled} is
@code{nil}, but the byte compiler and the native compiler bind it to
@code{t} when they run.
Typically, symbols with position are created by the byte compiler
calling the reader function @code{read-positioning-symbols}
@ -810,17 +824,17 @@ calling the reader function @code{read-positioning-symbols}
@code{position-symbol}.
@defvar symbols-with-pos-enabled
When this variable is non-@code{nil}, symbols with position behave
When this variable is non-@code{nil}, a symbol with position behaves
like the contained bare symbol. Emacs runs a little more slowly in
this case.
@end defvar
@defvar print-symbols-bare
When bound to non-@code{nil}, the Lisp printer prints only the bare symbol of
a symbol with position, ignoring the position.
When bound to non-@code{nil}, the Lisp printer prints only the bare
symbol of a symbol with position, ignoring the position.
@end defvar
@defun symbol-with-pos-p symbol.
@defun symbol-with-pos-p symbol
This function returns @code{t} if @var{symbol} is a symbol with
position, @code{nil} otherwise.
@end defun

View file

@ -2774,10 +2774,13 @@ internal_equal (Lisp_Object o1, Lisp_Object o2, enum equal_kind equal_kind,
/* A symbol with position compares the contained symbol, and is
`equal' to the corresponding ordinary symbol. */
if (SYMBOL_WITH_POS_P (o1))
o1 = SYMBOL_WITH_POS_SYM (o1);
if (SYMBOL_WITH_POS_P (o2))
o2 = SYMBOL_WITH_POS_SYM (o2);
if (symbols_with_pos_enabled)
{
if (SYMBOL_WITH_POS_P (o1))
o1 = SYMBOL_WITH_POS_SYM (o1);
if (SYMBOL_WITH_POS_P (o2))
o2 = SYMBOL_WITH_POS_SYM (o2);
}
if (BASE_EQ (o1, o2))
return true;
@ -2825,8 +2828,8 @@ internal_equal (Lisp_Object o1, Lisp_Object o2, enum equal_kind equal_kind,
if (ASIZE (o2) != size)
return false;
/* Compare bignums, overlays, markers, and boolvectors
specially, by comparing their values. */
/* Compare bignums, overlays, markers, boolvectors, and
symbols with position specially, by comparing their values. */
if (BIGNUMP (o1))
return mpz_cmp (*xbignum_val (o1), *xbignum_val (o2)) == 0;
if (OVERLAYP (o1))
@ -2858,6 +2861,11 @@ internal_equal (Lisp_Object o1, Lisp_Object o2, enum equal_kind equal_kind,
if (TS_NODEP (o1))
return treesit_node_eq (o1, o2);
#endif
if (SYMBOL_WITH_POS_P(o1)) /* symbols_with_pos_enabled is false. */
return (BASE_EQ (XSYMBOL_WITH_POS (o1)->sym,
XSYMBOL_WITH_POS (o2)->sym)
&& BASE_EQ (XSYMBOL_WITH_POS (o1)->pos,
XSYMBOL_WITH_POS (o2)->pos));
/* Aside from them, only true vectors, char-tables, compiled
functions, and fonts (font-spec, font-entity, font-object)

View file

@ -98,6 +98,26 @@
(should-not (equal-including-properties #("a" 0 1 (k "v"))
#("b" 0 1 (k "v")))))
(ert-deftest fns-tests-equal-symbols-with-position ()
"Test `eq' and `equal' on symbols with position."
(let ((foo1 (position-symbol 'foo 42))
(foo2 (position-symbol 'foo 666))
(foo3 (position-symbol 'foo 42)))
(let (symbols-with-pos-enabled)
(should (eq foo1 foo1))
(should (equal foo1 foo1))
(should-not (eq foo1 foo2))
(should-not (equal foo1 foo2))
(should-not (eq foo1 foo3))
(should (equal foo1 foo3)))
(let ((symbols-with-pos-enabled t))
(should (eq foo1 foo1))
(should (equal foo1 foo1))
(should (eq foo1 foo2))
(should (equal foo1 foo2))
(should (eq foo1 foo3))
(should (equal foo1 foo3)))))
(ert-deftest fns-tests-reverse ()
(should-error (reverse))
(should-error (reverse 1))