#31 call/cc and dynamic-wind "illegal function" errors

open
nobody
None
5
2014-08-16
2011-08-27
Doug Currie
No

When running the r5rs-tests.scm attached, the dynamic-wind test #163 results in:

Error: (tests/r5rs-tests.scm : 472) illegal function

The test is:

(let ((path '())
(c #f))
(let ((add (lambda (s)
(set! path (cons s path)))))
(dynamic-wind
(lambda () (add 'connect))
(lambda ()
(add (call-with-current-continuation
(lambda (c0)
(set! c c0)
'talk1))))
(lambda () (add 'disconnect)))
(if (< (length path) 4)
(c 'talk2)
(reverse path))))

This is similar to the result I get when trying to execute the last form in r5rs_pitfall.scm

;;Not really an error to fail this (Matthias Radestock)
;;If this returns (0 1 0), your map isn't call/cc safe, but is probably
;;tail-recursive. If its (0 0 0), the opposite is true.
(let ((result
(let ()
(define executed-k #f)
(define cont #f)
(define res1 #f)
(define res2 #f)
(set! res1 (map (lambda (x)
(if (= x 0)
(call/cc (lambda (k) (set! cont k) 0))
0))
'(1 0 2)))
(if (not executed-k)
(begin (set! executed-k #t)
(set! res2 res1)
(cont 1)))
res2)))
(if (equal? result '(0 0 0))
(display "Map is call/cc safe, but probably not tail recursive or inefficient.")
(display "Map is not call/cc safe, but probably tail recursive and efficient."))
(newline))

I tried running tinyscheme without the dynamic-wind code in init.scm, and it didn't seem to make a difference for this test.

Discussion

  • Doug Currie
    Doug Currie
    2011-08-27

    r5rs test script

     
    Attachments
  • Doug Currie
    Doug Currie
    2011-08-27

    the pitfalls source

     
    Attachments
  • Doug Currie
    Doug Currie
    2011-08-27

    I've boiled this down to a simple test; without dynamic-wind defined:

    trunk e$ rlwrap ./scheme
    TinyScheme 1.40
    (define cont #f)
    cont
    ts>
    (+ 1 (call/cc (lambda (k) (set! cont k) 0)))
    1
    ts> cont
    #<CONTINUATION>
    ts> (cont 2)
    Error: illegal function

    ts>

    ;; with dynamic-wind (doesn't matter)

    trunk e$ rlwrap ./scheme
    TinyScheme 1.40
    (define cont #f)
    cont
    ts>
    (+ 1 (call/cc (lambda (k) (set! cont k) 0)))
    1
    ts> cont
    #<CLOSURE>
    ts> (cont 2)
    Error: illegal function

    ts>

     
  • Doug Currie
    Doug Currie
    2011-08-28

    Bisecting the repository, it seems the call/cc bug was introduced in checkin 15 which brought the version number to 1.32. I have tracked it down to the use of reverse_in_place(). The following uses of reverse_in_place() are not compatible with call/cc:

    OP_E1ARGS -- this causes the "simple test" problem in comment #1
    OP_LET1 -- this causes the failure in r5rs-tests.scm dynamic-wind test #163
    OP_LET1REC -- not OK based on the similarity with OP_LET1

    By replacing these three occurrences of
    reverse_in_place(sc, sc->NIL, sc->args);
    with
    reverse(sc, sc->args);
    I have eliminated the call/cc test failures.

    The other instances of reverse_in_place() seem OK:

    OP_LET2 -- reverses a list created locally for named let and not captured
    OP_RDLIST & OP_RDDOT -- were in 1.30 and (read) internals not subject to capture by call/cc
    main() -- used for *args*, cannot be captured by call/cc

    -- e