Opened 16 months ago

Last modified 6 months ago

#1294 new defect

define-record-printer messes with internal defines

Reported by: sjamaan Owned by:
Priority: major Milestone: 5.1
Component: expander Version: 4.11.0
Keywords: Cc:
Estimated difficulty: hard

Description (last modified by sjamaan)

As pointed out by russelw on IRC, the following program fails to compile (and doesn't work in the interpreter, either):

(module main ()
 (import chicken)
 (import scheme)

 (define (h)
  (define (f)
   (g))

  (define-record-printer (foo x port)
   #f)

  (define (g)
   (f))))

This is probably similar to #1274 and #1309.

Change History (6)

comment:1 Changed 16 months ago by sjamaan

The problem here is that define-record-printer isn't a true definition. This code is comparable to the following:

(define (h)
  (define (f)
   (g))

  (display "hello\n")

  (define (g)
   (f)))

Other Schemes will bail out on this, complaining that the definition of g is misplaced. CHICKEN accepts it, so the code from the ticket body should probably also be accepted.

comment:2 Changed 16 months ago by sjamaan

Actually, the snippet above doesn't work in CHICKEN either; the compiler bails out when wrapped in a module, and if you insert a call to (g), it will die at runtime with a "unbound variable: g".

So perhaps it simply needs better error reporting? Other Schemes complain that the define for g is invalid.

comment:3 Changed 13 months ago by sjamaan

  • Estimated difficulty set to hard

comment:4 Changed 13 months ago by sjamaan

  • Description modified (diff)

comment:5 Changed 8 months ago by sjamaan

  • Milestone changed from 4.12.0 to 5.0

This requires sweeping changes, so better not mess with this for the 4.x series.

comment:6 Changed 6 months ago by sjamaan

  • Milestone changed from 5.0 to 5.1

The reason this does not work is that each statement after a define ensures a following set of defines will start a new "letrec" block. Of course, that means forward references in earlier letrec blocks can't "see" definitions of inner letrec blocks. I'm not sure it's worthwhile or even desirable to make this work differently.

On the other hand, I can imagine a system that works more like the toplevel, where each define simply causes the definition to be made, so that any later define can refer back to it (but, that means you could get undefined variable errors at runtime, I think)

The code that deals with this stuff is extremely hairy, though, and the benefit is marginal (and could break other code, too!). Maybe a documentation change is warranted instead of trying to fix this.

Note: See TracTickets for help on using tickets.