A fixed array does not decay to a slice at a call, so passing one to a function over [$t] meant writing (slice a 0 (len a)) at every call site. (slice a) is the whole of it now and (slice a n) is the tail from n, filled in by check.ml into the three-argument form: same node, same static bound checks, same runtime trap, and on a fixed array the implicit length is the constant (len a) already folds to. Neither backend grew an arity case. A target that is not already a name goes through a slot first, so (slice (f x)) calls f once. at and slice also reach a string, because (bytes s) was the only route to a byte and it is about to start copying. (at s i) is the byte, bounds-checked; (slice s ...) at all three arities answers a string viewing the same bytes, not a [u8], which would be a writable-looking view of storage the program does not own. Neither is a place, and the refusal lives in [indexed] rather than in check_place, which is the part that matters. There are three routes to a Pindex and they share no code: check_place, the single-index set arm that checks its own target, and addr. Asked in check_place, the question is answered for two of them and missed for the one a person writes — a store into a string literal compiled, and the backends disagreed about it. So [indexed] takes a ~place location and asks at every dimension, because (at g 0 0) over a [[2 string]] reaches the string only at the last step. One message, and addr gets it too, so it reads as value-versus-place rather than as a rule about assignment. Slicing an array a call returned is refused at every arity. The view outlives the temporary, both backends print whatever the frame reused, and nothing traps — which was already true of (slice (mk) 0 3) and only survivable while nobody wrote it. (slice (mk)) is short enough to become a habit. An array literal is not this case and stays legal. Two backend cases. emit.ml's element_addr grew the String arm beside the Slice one. x86.ml's index_len had answered None for a string — correct while nothing could index one, and a skipped bounds check the moment something could — and now reads the length word, so both check the same thing.
118 lines
5.3 KiB
Plaintext
118 lines
5.3 KiB
Plaintext
;;;; The prelude's in-place slice algorithms.
|
|
;;;;
|
|
;;;; Every input here is chosen so that a wrong implementation passes nothing.
|
|
;;;; The sort input is unsorted, has duplicates, has negatives and has an odd
|
|
;;;; length, so a comparison with the wrong sense, an off-by-one that drops the
|
|
;;;; last element, and a swap that loses an equal key all show up. The second
|
|
;;;; sort is reverse-sorted, which is the worst case for insertion sort and the
|
|
;;;; case a no-op comparison would pass. The third sorts a *subslice* and then
|
|
;;;; prints the whole owning array: a slice is ptr+len into its owner, so the
|
|
;;;; five elements inside the range must be sorted and the three outside it
|
|
;;;; must be untouched. That last one is the property that dies silently if a
|
|
;;;; slice parameter ever starts being copied.
|
|
|
|
(defvar xs [7 i32])
|
|
(defvar ys [5 i32])
|
|
(defvar zs [8 i32])
|
|
|
|
(defn show [s [i32]] ()
|
|
(dotimes [i (len s)]
|
|
(when (> i 0) (print " "))
|
|
(print (at s i)))
|
|
(println ""))
|
|
|
|
(defn load-xs [] ()
|
|
(set (at xs 0) 5)
|
|
(set (at xs 1) -3)
|
|
(set (at xs 2) 5)
|
|
(set (at xs 3) 0)
|
|
(set (at xs 4) 12)
|
|
(set (at xs 5) -3)
|
|
(set (at xs 6) 7))
|
|
|
|
(defn main [] i32
|
|
(load-xs)
|
|
(show (slice xs 0 (len xs))) ; 5 -3 5 0 12 -3 7
|
|
|
|
;; Reading the whole slice, before anything reorders it.
|
|
(print (sum-i32 (slice xs 0 (len xs)))) (println "") ; 23
|
|
(print (match (min-of (slice xs 0 (len xs))) (Some v) v None 99))
|
|
(println "") ; -3
|
|
(print (match (max-of (slice xs 0 (len xs))) (Some v) v None 99))
|
|
(println "") ; 12
|
|
;; First index, not the last: 5 appears at 0 and at 2.
|
|
(print (match (index-of (slice xs 0 (len xs)) 5) (Some v) v None -1))
|
|
(println "") ; 0
|
|
(print (match (index-of (slice xs 0 (len xs)) 4) (Some v) v None -1))
|
|
(println "") ; -1
|
|
;; An empty slice has no least element, and None is the answer.
|
|
(print (match (min-of (slice xs 3 3)) (Some v) v None 99))
|
|
(println "") ; 99
|
|
|
|
;; Reverse of an odd-length slice: the middle element stays put.
|
|
(reverse (slice xs 0 (len xs)))
|
|
(show (slice xs 0 (len xs))) ; 7 -3 12 0 5 -3 5
|
|
;; And of a two-element one, the smallest case that can actually move.
|
|
(reverse (slice xs 0 2))
|
|
(show (slice xs 0 (len xs))) ; -3 7 12 0 5 -3 5
|
|
|
|
(load-xs)
|
|
(sort (slice xs 0 (len xs)))
|
|
(show (slice xs 0 (len xs))) ; -3 -3 0 5 5 7 12
|
|
|
|
;; Reverse-sorted: the case a comparison that never fires would pass.
|
|
(set (at ys 0) 5) (set (at ys 1) 4) (set (at ys 2) 3)
|
|
(set (at ys 3) 2) (set (at ys 4) 1)
|
|
(sort (slice ys 0 (len ys)))
|
|
(show (slice ys 0 (len ys))) ; 1 2 3 4 5
|
|
|
|
;; A subslice, with the elements on both sides left alone.
|
|
(set (at zs 0) 100) (set (at zs 1) 9) (set (at zs 2) -1)
|
|
(set (at zs 3) 9) (set (at zs 4) 4) (set (at zs 5) 0)
|
|
(set (at zs 6) 200) (set (at zs 7) 300)
|
|
(sort (slice zs 1 6))
|
|
(show (slice zs 0 (len zs))) ; 100 -1 0 4 9 9 200 300
|
|
|
|
;; Degenerate lengths must do nothing rather than run off an end.
|
|
(sort (slice zs 0 0))
|
|
(reverse (slice zs 0 0))
|
|
(sort (slice zs 2 3))
|
|
(reverse (slice zs 2 3))
|
|
(show (slice zs 0 (len zs))) ; 100 -1 0 4 9 9 200 300
|
|
|
|
;; ── The short arities ──────────────────────────────────────────────
|
|
;; (slice a) and (slice a n) are (slice a 0 (len a)) and (slice a n (len a))
|
|
;; written out in the checker, so each line here is read against the
|
|
;; spelling above it that it stands for. A fixed array does not decay to a
|
|
;; slice at a call, so passing one to a function over [$t] is what these
|
|
;; exist for.
|
|
(load-xs)
|
|
(show (slice xs)) ; 5 -3 5 0 12 -3 7
|
|
(show (slice xs 4)) ; 12 -3 7
|
|
;; Of a slice rather than of an array: the length is the slice's own, so
|
|
;; these index from where the previous one started, not from the array's 0.
|
|
(show (slice (slice xs))) ; 5 -3 5 0 12 -3 7
|
|
(show (slice (slice xs 2) 1)) ; 0 12 -3 7
|
|
|
|
;; A literal array is nobody's named place, and it still slices: the two
|
|
;; lines the author hit are these, one sorting through the prelude's
|
|
;; generic [$t] sort and one over character literals.
|
|
(sort (slice [6 2 4 9 1 9 4 5]))
|
|
(let [a [6 2 4 9 1 9 4 5]]
|
|
(sort (slice a))
|
|
(show (slice a))) ; 1 2 4 4 5 6 9 9
|
|
(let [cs [\I \N \S \E \R \T \I \O \N \S \O \R \T]]
|
|
(sort (slice cs))
|
|
(println (string (slice cs)))) ; EIINNOORRSSTT
|
|
|
|
;; ── Strings ────────────────────────────────────────────────────────
|
|
;; A string is ptr+len over bytes, so it indexes to a byte and slices to a
|
|
;; string viewing the same bytes. No copy, and no route through [u8].
|
|
(let [s "insertion"]
|
|
(print (at s 0)) (println "") ; 105
|
|
(println (slice s)) ; insertion
|
|
(println (slice s 6)) ; ion
|
|
(println (slice s 0 6))) ; insert
|
|
(println (slice "sorted" 2 4)) ; rt
|
|
0)
|