Why does this counter behave differently in different runtimes?

50 views
Skip to first unread message

Sage Gerard

unread,
Jun 17, 2020, 3:05:28 AM6/17/20
to users\@racket-lang.org
I attached a video demonstrating what I'm seeing. In case it does not load or is not available, I'll summarize here. Forgive any typos; it's been a late night of coding.

Here's a module with an incorrect counter. It's incorrect because it uses begin0, and is therefore expected to return void instead of an incrementing integer.

#lang racket
(provide count!)
(define count!
  (let ([i 0])
    (λ () (begin0
            (set! i (add1 i))
            (~v i)))))

Notice that I added the ~v to format the return value. If I launch Racket v7.7.0.5 using racket -it prog.rkt, (count!) returns the formatted value. But if I remove the ~v, it behaves as written (returning void).

The video shows the behavior with and without ~v, both in DrRacket and racket. DrRacket is the only environment that consistently runs the code correctly.

~slg


repro-2020-06-17_02.53.30.mp4

Alexis King

unread,
Jun 17, 2020, 4:35:57 AM6/17/20
to Sage Gerard, Racket Users
This is quite curious. It appears to be a compiler bug. Here’s a very slightly smaller test case:

    #lang racket/base
    (define count!
      (let ([i 0])
        (λ () (begin0
                (set! i (add1 i))
                (+ i)))))
    (count!)

The fully-expanded program looks fine, so it isn’t the expander’s fault:

    (module counter racket/base
      (#%module-begin
       (module configure-runtime '#%kernel
         (#%module-begin (#%require racket/runtime-config) (#%app configure '#f)))
       (define-values
        (count!)
        (let-values (((i) '0))
          (lambda () (begin0 (set! i (#%app add1 i)) (#%app + i)))))
       (#%app call-with-values (lambda () (#%app count!)) print-values)))

But `raco decompile` reveals that begin0 has been mysteriously replaced with begin in the compiled program:

    (module counter ....
      (require (lib "racket/base.rkt"))
      (provide)
      (define-values
       (count!)
       (let ((local54 '0))
         (begin
           (set! local54 (#%box local54))
           (lambda ()
             '#(count! #<path:/tmp/counter.rkt> 4 4 53 63 #f)
             '(flags: preserves-marks single-result)
             '(captures: (val/ref local54))
             (begin
               (#%set-boxes! (local54) (add1 (#%unbox local54)))
               (+ (#%unbox local54)))))))
      (#%apply-values print-values (count!))
      (void)
      (module (counter configure-runtime) ....
        (require '#%kernel (lib "racket/runtime-config.rkt"))
        (provide)
        (print-as-expression '#t)
        (void)))

It seems perhaps an optimization has gone awry. The bug appears to be quite old: I can reproduce it as far back as 6.1.1. (I didn’t test any versions earlier than that.) Unsurprisingly, the issue does not occur on Racket CS, which is consistent with the hypothesis that this is a compiler bug.

Alexis

George Neuner

unread,
Jun 17, 2020, 6:55:31 AM6/17/20
to Sage Gerard, racket users

It seems to work ... i.e. returns #<void> ... in Windows.  I tried it in 7.6 and 7.7,  both 32 and 64 bit versions.   Not near my Linux machine to try it there. 

The expansion in all cases is the same and seems reasonable:
(module count racket

  (#%module-begin
   (module configure-runtime '#%kernel
     (#%module-begin (#%require racket/runtime-config) (#%app configure '#f)))
   (#%provide count!)
   (define-values
    (lifted/2)
    (begin
      (with-continuation-mark
       contract-continuation-mark-key
       (#%app cons idB12 'no-negative-party)
       (let-values ()
         (#%app
          idX9
          (#%app
           module-name-fixup
           (#%app
            variable-reference->module-source/submod
            (#%variable-reference))
           (#%app list)))))))

   (define-values
    (count!)
    (let-values (((i) '0))
      (lambda () (begin0 (set! i (#%app add1 i)) (#%app lifted/2 i)))))))


Btw:  my Racket installations report only  "7.7", not  "7.7.0.5".  Are you running a snapshot build?

George  

George Neuner

unread,
Jun 17, 2020, 7:38:06 AM6/17/20
to Sage Gerard, racket users

Sorry for the noise:  it behaves as you say returning "!", "2", ...   
Somehow I paths screwed up and was running CS when I thought I was
running regular (bytecode) Racket.

Sigh!
George

Matthew Flatt

unread,
Jun 17, 2020, 9:13:11 AM6/17/20
to Alexis King, Sage Gerard, Racket Users
Yes, clearly a BC compiler bug --- and probably almost as old as
Racket. The bug was specific to `set!` on a locally bound variable as
the first subexpression of `begin0`.

I've pushed a repair.

Thanks!
> --
> You received this message because you are subscribed to the Google Groups
> "Racket Users" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to racket-users...@googlegroups.com.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/racket-users/781BE0E5-E6E5-4F0B-8B46-5227FDFB
> 8835%40gmail.com.

Sage Gerard

unread,
Jun 17, 2020, 11:55:04 AM6/17/20
to mfl...@cs.utah.edu, lexi....@gmail.com, racket...@googlegroups.com
Thank you all so much! I'm glad it wasn't a huge mystery in the end.



-------- Original Message --------

To view this discussion on the web visit https://groups.google.com/d/msgid/racket-users/20200617071306.bc%40sirmail.smtp.cs.utah.edu.

Neil Van Dyke

unread,
Jun 17, 2020, 12:22:08 PM6/17/20
to racket...@googlegroups.com
Compiler bugs have been so blessedly rare in Racket, maybe there should
be a page on the Web, honoring those who found a compiler bug?

I would nominate Sage and Alexis for this one.

And Matthew, though we'd have to make sure he's not mis-incentivized by
the glory of bug-finding, to start making bugs. :)

Reply all
Reply to author
Forward
0 new messages