flan/test/programs/string-eq.flan
Joseph Ferano 2e203f64b8 bytes copies, bytes-view aliases, and a dev-session segfault parks
The INSERTIONSORT crash, all three rulings (FIX.org 2026-09-20):

- (bytes s) allocates a writable copy through the allocator surface —
  context or (bytes s a), StorageExhausted with retry, a registry note in
  dev builds (flan_bytes_dup, lowered like vec-new). (bytes-view s) is the
  old zero-cost reinterpret, renamed, read-only by convention; every
  in-repo reader swept over to it. (string b) unchanged.
- String constants were already read-only on both backends at -O0; now
  pinned — bytes-copy.flan rows on LLVM/-O0/--x86, and dies_segv rows
  asserting the write-through-view trap on both backends.
- A dev build installs a SIGSEGV/SIGBUS handler by the same dev-only
  constructor slot that arms the registry: one line naming the address and
  the innermost frame, then the trap-hook park — stopped, not dead, the
  daemon serving. No agent: message and re-raise. Release builds untouched.
  Pinned by trap_park over dev-segv.flan.
2026-09-20 23:12:42 +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 (as-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)