Subject: Re: bug#38708: [PATCH] Deduplicate flonum and bignum constants in bytecode

From: Pip Cet <[email-masked]>
Date: Sat, 28 Dec 2019 16:36:54 +0000

On Sat, Dec 28, 2019 at 3:49 PM Mattias Engdegård <[email-masked]> wrote:
> 27 dec. 2019 kl. 18.07 skrev Pip Cet <[email-masked]>:
> > I don't have a strong opinion about this (well, I do, actually: 'eq
> > and 'eql should be equal), but my impression from the last time this
> > was discussed is that the problems this causes (different code
> > behavior for byte-compiled code versus evaluated code) outweighed the
> > benefits (very tiny code size reduction).
>
> Thank you for inspecting my change! And sorry, I didn't know this had been debated before. Is there a record of that discussion anywhere?

It was part of the rather extensive eq-vs-eql debate, I'm afraid.

I'll try to be clear about this: I think anything but making eq and
eql synonymous is likely to cause problems that drive programmers away
from Elisp. But there are axioms that programmers can rely on that are
stronger than what eq actually promises. For example, two objects
might be thought either to be eq to each other or not, but code such
as this:

(defun my-not-eq (x y) (not (eq x y)))

(defun always-t ()
  (or (eq 18446744073709551616 18446744073709551616)
      (my-not-eq 18446744073709551616 18446744073709551616)))

(byte-compile 'always-t)
(always-t)

will yield unexpected results.

> > Most importantly, I think that we should be able to be define
> >
> > (defun f () (eq 18446744073709551616 18446744073709551616))
> >
> > That function should always return t on sane systems that have eq =
> > eql, and always return nil on systems that have <64 bits in a fixnum
> > and the old-style eq.
>
> I'm not sure I understand. Surely such a criterion imposes a rather low limit on permissible optimisations?

Yes, it does. I think any change in the behavior of eq, except for
making it equal to eql, is likely to break code that's out there
somewhere (and that doesn't mean we hear about it; more likely, people
end up abandoning Elisp and using JavaScript instead). So the second
best option is to keep the current (pre-patch) behavior in which (eq
1.0 1.0) is reliably nil, IMHO.

Technically, I think code such as

(cond ((eq a b) 1) ((not (eq a b)) 2) (t 3))

is allowed to return 3. We should attempt not to make changes that
make this more likely, though.

(Again, is this really what we want? If you can't modify an eq-based
hash table reliably, because keys might have become eq (and thus
overwrite each other) that weren't eq when you checked the hash table,
what can you use such hash tables for?)

> For example, shouldn't
>
> (eq (ash 1 x) (ash 1 x))
>
> be allowed to be optimised to t (after CSE, say), even if x can be 64, despite the fact that interpreted or low-optimised compiled code would yield nil in that case?
>
> Perhaps the change should really be done on the emacs-27 branch, to avoid changing bignum behaviour, but that is just a slightly weaker version of the same restriction. Unless we decide to turn eq into a synonym for eql, eq is a one-sided conservative approximation of eql for bignums and flonums.

You're right, that is eq's contract. But people do misuse it, and one
of the things they wouldn't expect is that optimization results in
things becoming non-eq that were eq before optimization; I think the
other direction is much less problematic.

> > Anyway, I still think the right course of action here is to fix (or
> > deprecate) eq rather than changing minor details of the byte compiler
> > in incompatible ways. However, if we decide the gain is significant
> > for floating point numbers, let's restrict this to floating point
> > numbers and leave bignums alone?
>
> What would anyone gain from such a restriction? And the change is minor because it's a small thing to do; what I thought looked like an obvious oversight, or one that made more sense back when Elisp didn't have bignums.

Well, Elisp has had floats for a while, and bignum constants appear to
be nonexistent, so far.