SHIFTF macroexpansion has colliding gensym counters

Bug #1873556 reported by Michał "phoe" Herda
10
This bug affects 2 people
Affects Status Importance Assigned to Milestone
SBCL
New
Wishlist
Unassigned

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.)

Revision history for this message
Michał "phoe" Herda (phoe-krk) wrote :

That is because https://github.com/sbcl/sbcl/blob/f91a81120579aeb010383182a8e9607c3bf0ad97/src/code/setf.lisp#L57 explicitly binds the gensym counter to 1 on each making of gensyms. Why is that required?

Revision history for this message
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.

Revision history for this message
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
Revision history for this message
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.
(https://reproducible-builds.org/)
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.

Revision history for this message
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).

Revision history for this message
Christophe Rhodes (csr21-cantab) wrote :

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  
Everyone can see this information.

Other bug subscribers

Remote bug watches

Bug watches keep track of this bug in other bug trackers.