But if you evaluate it, that doesn't happen, which is probably even
worse.
And this is even stranger, IMO:
(defun example-bug2 ()
(eq (make-text-button
"example" nil
'action (lambda (_) (message "action 1")))
(make-text-button
"example" nil
'action (lambda (_) (message "action 2")))))
(defun example-bug3 ()
(eq "example" "example"))
(defun example-bug4 ()
(let ((str1 "example")
(str2 "example"))
(eq str1 str2)))
(list (example-bug2) (example-bug3) (example-bug4))
when compiled, last form returns (t nil t) in emacs 27,
when compiled, last form returns (nil nil t) in emacs 28.
when evaluated, last form returns (nil nil nil) in emacs 28.
For comparison, example-bug4 is valid Common Lisp and will return nil in
every Common Lisp implementation I know (I tested with ACL and SBCL),
regardless of whether compiled or evaluated. I'm reasonably confident
there's somewhere in the Hyperspec where that behaviour may be specified
(I trust some CL pope will find it for me;-) )