SHIFTF macroexpansion has colliding gensym counters

Bug #1873556 reported by Michał "phoe" Herda on 2020-04-18
This bug affects 2 people
Affects Status Importance Assigned to Milestone

Bug Description

* (progn (print (macroexpand-1 '(shiftf a b c d))) nil)

(LET* ((#:OUT449 A) (#:NEW1 B) (#:NEW1 C) (#:NEW1 D))
  (SETQ A #:NEW1)
  (SETQ B #:NEW1)
  (SETQ C #:NEW1)
  (VALUES #:OUT449))

Multiple gensyms with the same name are generated. (If I print this without *PRINT-GENSYM*, this code will not work correctly if read back and evaluated.)

Michał "phoe" Herda (phoe-krk) wrote :

That is because explicitly binds the gensym counter to 1 on each making of gensyms. Why is that required?

Douglas Katzman (dougk) wrote :

it's trying to address the issue of reproducbility but probably in a not-so-great way.
It may be enough at this point that we're binding the counter around each toplevel form. And also around a file as a whole which may be redundant.
I don't think an intended use of gensym is to let you remove the "#:" and have the resulting form "just work" when read back in. *gensym-counter* does say "*... can be either assigned or bound at any time"

I prefer that any invocation of a setf expander starts numbering its gensyms from 1, but this case does look wonky. Maybe other devs have a strong opinion that binding is expressly the wrong thing.

Stas Boukarev (stassats) wrote :

I've known about this for some time, but since it's only making it difficult to read macroexpansions it doesn't bother me much.

Changed in sbcl:
importance: Undecided → Wishlist
Douglas Katzman (dougk) wrote :

it's sufficiently tricky that there is no good solution.
There are people who believe that reproducible builds are a thing.
Then there's SBCL, putting random gensyms into the debug info for a tiny function
  :VARS #(1152921916923721740 "EXPR" 8192 "G424" 9216 "G435")
depending on whether PCL needed to compile a method which caused the gensym counter to change while compiling something else.

Michał "phoe" Herda (phoe-krk) wrote :

Out of the two evils, I'd pick the lesser one: I think I would prefer gensyms that increase non-monotonically (such as G424 and G435) over gensyms whose names collide (such as G1 and G1).

I think it is always wrong to expect code to be readable without problems without *print-gensym* (and/or *print-readably*, *print-circle*). I don't want to support "you might be able to read this with *print-gensym* set to nil, 99% of the time, but some other times you won't" -- I'd much rather be able to tell people "well don't do that then".

If you want reproducibility, you need to have deterministic numbering. And while we might be able to have non-colliding numbering in most cases, I don't want to have to guarantee it. But, importantly, if you have reproducibility, you can get readability with appropriate use of printer and reader control variables; if you don't have reproducibility, no amount of fiddling with control variables will give it to you.

To post a comment you must log in.
This report contains Public information  Edit
Everyone can see this information.

Other bug subscribers