Re: Testing how array-creation routines interact with continuations, and crashing when the user does something that "is an error". Marc Nieper-Wißkirchen (09 Mar 2023 08:33 UTC)

Re: Testing how array-creation routines interact with continuations, and crashing when the user does something that "is an error". Marc Nieper-Wißkirchen 09 Mar 2023 08:32 UTC

I think it is perfectly fine that the library crashes the system when
one selects an "unsafe" mode of the implementation.

After all, this is what is expected: By selecting an "unsafe" mode, I
ask for maximal efficiency at the expense of comprehensive tests
against programming errors.

Am Mi., 8. März 2023 um 22:19 Uhr schrieb Bradley Lucier <xxxxxx@purdue.edu>:
>
> There are currently no tests in the test suite for how
>
> array-{copy|append|stack|decurry|block}[!]*
>
> interact with continuations, and I'd like to fix that, especially for
> independent implementations of this SRFI.
>
> I started with (pp is pretty-print to the system output port):
>
>  > (import (srfi 231))
>  > (let* ((cont #f)
>         (call-cont #t)
>         (domain (make-interval '#(2 2)))
>         (A_ (lambda (i j)
>               (call-with-current-continuation
>                (lambda (c)
>                  (if (= i j 0)
>                      (set! cont c))
>                  1))))
>         (A (make-array domain A_))
>         (array-list '()))
>    (set! array-list (cons (array-copy A) array-list))
>    (pp 'printing)
>    (for-each (lambda (A) (pp (array->list* A))) array-list)
>    (if call-cont
>        (begin
>          (set! call-cont #f)
>          (cont 4)))
>    (pp (apply eq? array-list)))
> printing
> ((1 1) (1 1))
> printing
> ((4 1) (1 1))
> ((1 1) (1 1))
> #f
>
> which works as expected.
>
> Replacing array-copy with array-copy! in this test causes the library to
> crash when compiled in Gambit with safety checks removed:
>
>  > (let* ((cont #f)
>         (call-cont #t)
>         (domain (make-interval '#(2 2)))
>         (A_ (lambda (i j)
>               (call-with-current-continuation
>                (lambda (c)
>                  (if (= i j 0)
>                      (set! cont c))
>                  1))))
>         (A (make-array domain A_))
>         (array-list '()))
>    (set! array-list (cons (array-copy! A) array-list))
>    (pp 'printing)
>    (for-each (lambda (A) (pp (array->list* A))) array-list)
>    (if call-cont
>        (begin
>          (set! call-cont #f)
>          (cont 4)))
>    (pp (apply eq? array-list)))
> printing
> ((1 1) (1 1))
> printing
> Segmentation fault (core dumped)
>
> I didn't expect this, I just expected that the first and second elements
> of array-list would be eq?.  Running the library interpreted shows the
> problem:
>
>  > (load "generic-arrays.scm")
> "/home/lucier/lang/scheme/srfi-231/srfi-231-temp/generic-arrays.scm"
>  > (let* ((cont #f)
>         (call-cont #t)
>         (domain (make-interval '#(2 2)))
>         (A_ (lambda (i j)
>               (call-with-current-continuation
>                (lambda (c)
>                  (if (= i j 0)
>                      (set! cont c))
>                  1))))
>         (A (make-array domain A_))
>         (array-list '()))
>    (set! array-list (cons (array-copy! A) array-list))
>    (pp 'printing)
>    (for-each (lambda (A) (pp (array->list* A))) array-list)
>    (if call-cont
>        (begin
>          (set! call-cont #f)
>          (cont 4)))
>    (pp (apply eq? array-list)))
> printing
> ((1 1) (1 1))
> *** ERROR IN #<procedure #2>, "generic-arrays.scm"@2534.42 -- (Argument
> 2, k) Out of range
> (vector-set! #(4 1 1 1) 5 1)
> 1>
>
> Here is the code:
>
>                            ;; No checks needed, storage-class-setter is
> vector-set!
>                            (let ((body (%%array-body destination)))
>                              (%%interval-for-each
>                               (case (%%interval-dimension domain)
>                                 ((0)  (let ((index initial-offset))
>                                         (lambda ()
>                                           (vector-set! body index (getter))
>                                           (set! index (fx+ index 1)))))
>    ;; not necessary
>                                 ((1)  (let ((index initial-offset))
>                                         (lambda (i)
>                                           (vector-set! body index
> (getter i))
>                                           (set! index (fx+ index 1)))))
>                                 ((2)  (let ((index initial-offset))
>                                         (lambda (i j)
>                                           (vector-set! body index
> (getter i j))  ;; <<<<<< problem
>                                           (set! index (fx+ index 1)))))
>                                 ((3)  (let ((index initial-offset))
>                                         (lambda (i j k)
>                                           (vector-set! body index
> (getter i j k))
>                                           (set! index (fx+ index 1)))))
>                                 ((4)  (let ((index initial-offset))
>                                         (lambda (i j k l)
>                                           (vector-set! body index
> (getter i j k l))
>                                           (set! index (fx+ index 1)))))
>                                 (else (let ((index initial-offset))
>                                         (lambda multi-index
>                                           (vector-set! body index (apply
> getter multi-index))
>                                           (set! index (fx+ index 1))))))
>                               domain))
>
> So I was using mutated state (the value of index) in a (perhaps vain)
> attempt to speed things up, but this causes index to walk off the end of
> the specialized array result's body the second time the continuation is
> invoked.
>
> The specifications says "Don't do that!", don't invoke the continuation
> of the argument array's getter more than once when calling array-copy!.
>
> On the other hand, I didn't expect the library to crash the system.
>
> Is this OK behavior, to crash when the user does something that "is an
> error", or would it be better to try to catch all such instances while
> possibly slowing down the general case?
>
> Brad