flan/test/programs/string-eq.flan
Joseph Ferano 9ce51ba94e One slice over everything with elements, and the warning at the push
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.
2026-09-21 09:51:35 +07:00

48 lines
2.3 KiB
Plaintext

;;;; M2 queue item 5: typed = and != grow strings. Bytewise, with a
;;;; length-mismatch fast path and a same-pointer fast path ahead of the byte
;;;; loop (runtime/flan_rt.c, flan_str_eq). Ordering stays refused on a
;;;; string -- that half is tested in test_flan.ml, because a program that
;;;; wrote (< "a" "b") would not compile and so cannot be a row here.
(defn main [] i32
;; Same pointer: one local read twice is the same two words, ptr and len
;; both, and the fast path answers before a single byte is looked at.
(let [s "same"]
(println (= s s)) ; true
(println (!= s s))) ; false
;; Differing lengths: the length check alone settles it, and never reaches
;; the byte loop -- a common prefix would be no evidence otherwise.
(println (= "abc" "ab")) ; false
(println (!= "abc" "ab")) ; true
;; Equal contents, distinct pointers. "abc" the literal lives in the
;; read-only data section; to-lower of "ABC" is a fresh heap allocation,
;; so this pair shares no address and the same-pointer fast path cannot
;; fire -- what answers here is the byte loop, or the length check first
;; ruling nothing out since both are three bytes.
(let [heap (to-lower (bytes-view "ABC"))]
(let [h (string (slice heap))]
(println (= "abc" h)) ; true
(println (!= "abc" h)))
(free heap))
;; A one-byte difference at the end, so the length check cannot rule it
;; out and the byte loop has to run to the last byte before it can answer.
(println (= "abd" "abc")) ; false
(println (!= "abd" "abc")) ; true
;; Empty strings: the length check's zero case, which the runtime helper
;; also uses to skip a memcmp that would otherwise read through a null
;; pointer -- two empty string literals, and empty against non-empty.
(println (= "" "")) ; true
(println (= "" "a")) ; false
(println (= "a" "")) ; false
;; A slice and the prefix it was cut from: same base pointer, different
;; lengths -- the one pair the same-pointer fast path would answer wrong on
;; if it ran before the length check instead of after.
(let [s "abcd"]
(println (= s (string (slice (bytes-view s) 0 2))))) ; false
0)