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.
48 lines
2.3 KiB
Plaintext
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)
|