Skip to content

SRFI 231: array-decurry and array-block evaluate getters multiple times for the same multi-index #1105

Description

@gambiteer

I discovered a bug in my mini-arrays.scm implementation, wherein array-assign! would access each element of the source array twice, once to check with the checker and once to hand the destination setter.

I say this is a "bug" because procedures like array-assign!, array-copy, etc., might be copying or assigning an array whose elements are read sequentially from a file (like an image), so each element should be accessed only once or you'll run over the end of the image file.

So I came up with the following test:

(let* ((original-make-array make-array)
       (make-array
        (case-lambda
         ((specifier getter)
          (if (and (interval? specifier)
                   (procedure? getter))
              (let* ((tracker
                      (make-specialized-array specifier u1-storage-class 0))
                     (tracker_
                      (array-getter tracker))
                     (tracker!
                      (array-setter tracker))
                     (new-getter
                      (lambda args
                        (if (eqv? (apply tracker_ args) 0)
                            (begin
                              (apply tracker! 1 args)
                              (apply getter args))
                            (error "getter applied more than once: " specifier getter)))))
                (original-make-array specifier new-getter))
              ;; have original make-array handle the error
              (original-make-array specifier getter)))
         ((specifier getter setter)
          (original-make-array specifier getter setter)))))
  (let* ((A (array-copy (make-array (make-interval '#(2 2)) list) generic-storage-class #t))
         (B (make-array (make-interval '#(2 2)) list))
         (ignore (array-assign! A B))
         (B (make-array (make-interval '#(2 2)) list))
         (ignore (array-append 0 (list A B)))
         (B (make-array (make-interval '#(2 2)) list))
         (ignore (array-stack 0 (list A B)))
         (B (make-array (make-interval '#(2 2)) list))
         (ignore (array-decurry (make-array (make-interval '#(2)) (lambda (i) (list-ref (list A B) i)))))
         (B (make-array (make-interval '#(2 2)) list))
         (B* (make-array (make-interval '#(2 2)) list))
         (ignore (array-block (make-array (make-interval '#(2 2))
                                          (lambda (i j)
                                            (list-ref (list-ref (list (list A B)
                                                                      (list A B*))
                                                                i)
                                                      j)))))
         (B (make-array (make-interval '#(2 2)) list))
         (ignore (array-copy B))
         )
    #t))

This runs in chibi after importing (scheme case-lambda) and (srfi 231), and it fails for both the array-decurry and array-block tests.

I looked a bit at the code and it seems that it accesses one initial element of an array to get information about the domain, etc., and then later, when it's time to copy the array elements to the result array, you access the same initial element again.

I don't have the energy right now to suggest a patch, sorry.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions