flan/test/programs/string-eq.flan
Joseph Ferano daed039331 Typed = and != grow strings — M2 queue item 5
Types.is_equatable splits from is_comparable: a string answers equal?
now, bytewise, but still answers no to ordered? — there is no collation
the language has picked, so < and friends keep the refusal they had.

The comparison itself is one new runtime entry point, flan_str_eq
(runtime/flan_rt.c), length-mismatch and same-pointer fast paths ahead
of the memcmp, called identically from both backends: emit.ml pulls a
string's ptr and length out of the %slice SSA value and calls it
directly in the Eq/Ne arm; x86.ml adds an arm ahead of the generic
scalar comparison that reaches it through call_native, flipping the
answer for != the same way Not already flips a bool.

test_flan.ml covers the checker side directly and through a generic
instantiated at string, including the two different ways ordered? and
equal? fail at that type. test/programs/string-eq.flan is the survey
program — same pointer, differing lengths, equal content at distinct
addresses (a literal against a fresh heap string), a difference in the
last byte, and the empty-string cases — with acceptance rows for LLVM,
-O0 and --x86 in test_acceptance.ml.
2026-09-19 19:56:36 +07:00

41 lines
1.9 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 "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
;; 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
0)