feat(strictness): merge rejects atomic/wrong-shape args into a map

merge now throws when a non-first arg is a scalar, a set, a list, or a
wrong-length vector (a length-2 vector or a map still merge). Other map-like
tables (records/sorted-maps/host tables, e.g. SCI's namespaces) keep the lenient
conj path so the SCI bootstrap still loads.

merge 29/11/2 -> 35/3/4. clojure-test-suite pass 3874->3880.
spec: 5 merge strictness cases. jpm test green.
This commit is contained in:
Yogthos 2026-06-05 13:57:05 -04:00
parent 2ca3fa4348
commit f644b4b719
3 changed files with 14 additions and 2 deletions

View file

@ -632,6 +632,13 @@
(and (or (pvec? m) (tuple? m) (array? m)) (and (or (pvec? m) (tuple? m) (array? m))
(= 2 (if (pvec? m) (pv-count m) (length m)))) (= 2 (if (pvec? m) (pv-count m) (length m))))
(set result (core-assoc result (vnth m 0) (vnth m 1))) (set result (core-assoc result (vnth m 0) (vnth m 1)))
# scalars, sets, and wrong-length sequentials can't merge into a map
# (a length-2 vector was handled above; anything else here is bad)
(or (number? m) (string? m) (buffer? m) (keyword? m) (boolean? m)
(set? m) (plist? m) (pvec? m) (tuple? m) (array? m)
(and (struct? m) (get m :jolt/type)))
(error (string "Can't merge " (type m) " into a map"))
# other map-like tables (records, sorted-maps, host tables): lenient conj
(set result (core-conj result m)))) (set result (core-conj result m))))
(++ i)) (++ i))
result))) result)))

View file

@ -18,7 +18,7 @@
# Baseline: assertions Jolt currently passes across the suite. Raise as Jolt # Baseline: assertions Jolt currently passes across the suite. Raise as Jolt
# improves so a regression (previously-passing assertion breaking) is caught. # improves so a regression (previously-passing assertion breaking) is caught.
(def baseline-pass 3870) (def baseline-pass 3875)
# A file is "clean" when it ran with zero failures AND zero errors. # A file is "clean" when it ran with zero failures AND zero errors.
(def baseline-clean-files 45) (def baseline-clean-files 45)
# Per-file wall-clock budget (seconds). Normal files finish in well under 1s; # Per-file wall-clock budget (seconds). Normal files finish in well under 1s;

View file

@ -90,4 +90,9 @@
["subvec out of range" :throws "(subvec [0 1 2 3] 1 5)"] ["subvec out of range" :throws "(subvec [0 1 2 3] 1 5)"]
["subvec start>end" :throws "(subvec [0 1 2 3] 3 2)"] ["subvec start>end" :throws "(subvec [0 1 2 3] 3 2)"]
["subvec ok" "[1 2]" "(subvec [0 1 2 3] 1 3)"] ["subvec ok" "[1 2]" "(subvec [0 1 2 3] 1 3)"]
["min-key empty" :throws "(apply min-key identity [])"]) ["min-key empty" :throws "(apply min-key identity [])"]
["merge empty vector" :throws "(merge {} [])"]
["merge 1-elem vector" :throws "(merge {} [:foo])"]
["merge atomic arg" :throws "(merge {} :foo)"]
["merge [k v] ok" "{:foo 1}" "(merge {} [:foo 1])"]
["merge maps ok" "{:a 1, :b 2}" "(merge {:a 1} {:b 2})"])