as-slice was a warning, not an operation. The input type already decides which of the two things happens — a Vec can only be borrowed, an array or a string can only be viewed, and no call site picks between them — so the second name expressed no choice a reader could make. And it warned at the moment the view is taken, which is the one moment nothing is wrong; the danger arrives later, at the push. slice now takes a Vec at all three arities and as-slice is gone. (slice v lo) was free, and is the arity the Vec never had: the runtime already reads a hi of -1 as "to the end", so the tail form passes the caller's lo and the same -1 — no slot, no length read, no second evaluation. The merge is entirely in the checker; the Vec path builds the flan_vec_as_slice call it always built and neither backend has a line about any of it. A Vec a call returned is refused at every arity, and not for the array's reason. (slice (mk)) over an array dangles. (slice (make-vec)) does not — the storage outlives the expression — but the header is a temporary, so nothing can ever free the block. The refusal says that and names the let. The name's own refusal sits in ordinary_call after every table, so a program that defines an as-slice still reaches its own. It reads for somebody who has never heard of the old name and writes the call back out, spelling each argument that is a name or a number. The warning moved to where it bites: BUILT.md gains a section beside the Vec table and the push row points at it, spec-memory.md's Borrowing says the same. Investigated and deliberately not built — a diagnostic for a live view at the push. (reserve v 100) then a slice, a push and a read is correct code under the contract the spec chose, so any flag on it is a false positive by the language's own semantics rather than by an approximation. FIX.org has the finding and the syntactic sketch that does not work.
121 lines
5.5 KiB
Plaintext
121 lines
5.5 KiB
Plaintext
;;;; The slice family at its second and third element types.
|
|
;;;;
|
|
;;;; sort was the only sort in the language. These are the other two, and
|
|
;;;; they are copies rather than an abstraction: map, filter, reduce and a sort
|
|
;;;; taking a comparator all need a *function value*, which check.ml refuses
|
|
;;;; with "a function type is not implemented yet -- milestone 5". So the
|
|
;;;; honest form is the concrete one, and the claim this file makes is only
|
|
;;;; that the concrete ones are right.
|
|
|
|
(defn show-f32 [s [f32]] ()
|
|
(dotimes [i (len s)]
|
|
(print (at s i))
|
|
(print " "))
|
|
(println ""))
|
|
|
|
(defn show-fields [s [[u8]]] ()
|
|
(dotimes [i (len s)]
|
|
(print (string (at s i)))
|
|
(print " "))
|
|
(println ""))
|
|
|
|
(defn main [] i32
|
|
;; Every float literal is cast. A literal defaults to f64 and an array
|
|
;; literal has no context to say otherwise -- a let has no type annotation --
|
|
;; so [3.5 -1.0] is an [f64] and (sort) refuses it by type. The cast is
|
|
;; the only spelling available today.
|
|
;;
|
|
;; sort: duplicates, negatives, a zero and an odd length, which is the
|
|
;; input shape the i32 sort is tested on for the same reasons.
|
|
(let [xs [(f32 3.5) (f32 -1.0) (f32 0.0) (f32 3.5) (f32 -2.25) (f32 10.0) (f32 0.5)]]
|
|
(sort (slice xs 0 7))
|
|
(show-f32 (slice xs 0 7))) ; -2.25 -1 0 0.5 3.5 3.5 10
|
|
|
|
;; In place and ptr+len: sorting a subslice leaves its neighbours alone. That
|
|
;; is the whole content of the in-place claim, and a version that copied
|
|
;; would pass every test above and fail this one.
|
|
(let [xs [(f32 9.0) (f32 4.0) (f32 3.0) (f32 2.0) (f32 1.0) (f32 9.0)]]
|
|
(sort (slice xs 1 5))
|
|
(show-f32 (slice xs 0 6))) ; 9 1 2 3 4 9
|
|
|
|
;; Already sorted, reverse sorted, and a single element -- the three inputs
|
|
;; where an insertion loop with the comparison the wrong way round still
|
|
;; looks plausible.
|
|
(let [xs [(f32 1.0) (f32 2.0) (f32 3.0)]]
|
|
(sort (slice xs 0 3))
|
|
(show-f32 (slice xs 0 3))) ; 1 2 3
|
|
(let [xs [(f32 3.0) (f32 2.0) (f32 1.0)]]
|
|
(sort (slice xs 0 3))
|
|
(show-f32 (slice xs 0 3))) ; 1 2 3
|
|
(let [xs [(f32 7.0)]]
|
|
(sort (slice xs 0 1))
|
|
(show-f32 (slice xs 0 1))) ; 7
|
|
;; The empty slice must not read (at s -1).
|
|
(let [xs [(f32 7.0)]]
|
|
(sort (slice xs 0 0))
|
|
(show-f32 (slice xs 0 0))) ;
|
|
|
|
(let [xs [(f32 1.0) (f32 2.0) (f32 3.0) (f32 4.0)]]
|
|
(reverse (slice xs 0 4))
|
|
(show-f32 (slice xs 0 4))) ; 4 3 2 1
|
|
|
|
;; min, max and sum. The empty slice is None for the first two -- there is no
|
|
;; least f32 that is also an honest answer -- and the sum accumulates in f64,
|
|
;; which is why 16777216 + 1 does not absorb here the way it would in f32.
|
|
(let [xs [(f32 3.5) (f32 -1.0) (f32 10.0)]]
|
|
(match (min-of (slice xs 0 3)) (Some m) (print m) None (print "none"))
|
|
(print " ")
|
|
(match (max-of (slice xs 0 3)) (Some m) (print m) None (print "none"))
|
|
(print " ")
|
|
(print (sum-f32 (slice xs 0 3)))
|
|
(println "")) ; -1 10 12.5
|
|
(let [xs [(f32 1.0)]]
|
|
(match (min-of (slice xs 0 0)) (Some m) (print m) None (print "none"))
|
|
(print " ")
|
|
(print (sum-f32 (slice xs 0 0)))
|
|
(println "")) ; none 0
|
|
;; The accumulator's width, made visible. 2^24 is where an f32 stops having
|
|
;; a bit for 1, so an f32 running total absorbs both of these and the
|
|
;; difference below is 0. In f64 they both land, and it is 2.
|
|
(let [xs [(f32 16777216.0) (f32 1.0) (f32 1.0)]]
|
|
(print (- (sum-f32 (slice xs 0 3)) 16777216.0))
|
|
(println "")) ; 2
|
|
|
|
;; bytes<? is bytewise and unsigned, and explicitly not alphabetical: "Zebra"
|
|
;; comes before "apple" because 'Z' is 90. A prefix comes before what extends
|
|
;; it, which is the case a loop running only to (len a) reads off the end
|
|
;; for, and 0x80 above 0x00 is the case a signed byte gets backwards.
|
|
(print (bytes<? (bytes-view "a") (bytes-view "b"))) (print " ") ; true
|
|
(print (bytes<? (bytes-view "b") (bytes-view "a"))) (print " ") ; false
|
|
(print (bytes<? (bytes-view "a") (bytes-view "a"))) (print " ") ; false
|
|
(print (bytes<? (bytes-view "ab") (bytes-view "abc"))) (print " ") ; true
|
|
(print (bytes<? (bytes-view "abc") (bytes-view "ab"))) (print " ") ; false
|
|
(print (bytes<? (bytes-view "") (bytes-view "a"))) (print " ") ; true
|
|
(print (bytes<? (bytes-view "Zebra") (bytes-view "apple"))) ; true
|
|
(println "")
|
|
;; 0x00 below 0x80, which is the pair a comparison over a *signed* byte gets
|
|
;; backwards -- and there is no \x escape in the reader, so these are built
|
|
;; as u8 arrays rather than written as string literals.
|
|
(let [lo [(u8 0)]
|
|
hi [(u8 128)]]
|
|
(print (bytes<? (slice lo 0 1) (slice hi 0 1))) (print " ") ; true
|
|
(print (bytes<? (slice hi 0 1) (slice lo 0 1))) ; false
|
|
(println ""))
|
|
|
|
;; sort-bytes over the fields split out of one buffer. The slices move and
|
|
;; the bytes never do, so this sorts a borrowed view of a string literal --
|
|
;; which an in-place byte sort could not, since a literal lives in .rodata.
|
|
(let [f (split (bytes-view "pear,apple,Fig,apple,banana") \,)]
|
|
(sort-bytes (slice f))
|
|
(show-fields (slice f)) ; Fig apple apple banana pear
|
|
(free f))
|
|
|
|
;; And the round trip the whole second tier is for: split, sort, join.
|
|
(let [f (split (bytes-view "delta,alpha,charlie,bravo") \,)]
|
|
(sort-bytes (slice f))
|
|
(let [j (join (slice f) (bytes-view " < "))]
|
|
(println (string (slice j))) ; alpha < bravo < charlie < delta
|
|
(free j))
|
|
(free f))
|
|
0)
|