Subject: bug#62117: 29.0.60; cl-letf on a map place has side-effects

From: Augusto Stoffel <[email-masked]>
Date: Sat, 11 Mar 2023 08:44:26 +0100

Consider this example:

--8<---------------cut here---------------start------------->8---
(require 'cl-lib)
(defun f (map)
  (cl-letf (((map-elt map 'a) 1))
     map))

(let ((map '(b 2)))
  (f map)
  map)
=> (b 2 a nil)

(let ((map (make-hash-table)))
  (f map)
  (map-length map))
=> 1
--8<---------------cut here---------------end--------------->8---

I would expect `f' to have no side effects, so get (b 2) and 0
respectively in the two examples.

For comparison, plist-get as a place has no side-effect:

--8<---------------cut here---------------start------------->8---
(defun g (map)
  (cl-letf (((plist-get map 'a) 1))
    map))

(let ((map '(b 2)))
  (g map)
  map)
(b 2)
--8<---------------cut here---------------end--------------->8---

Of course it's usual to treat a nil entry and no entry as equivalent in
Lisp, but this behavior can be a problem e.g. when constructing data to
pass to other programs.