destructuring :or, fn :pre/:post, deftype field access + map-like dispatch

General fixes shaken out running clojure.core.cache (66 -> 198 of its assertions):

- Map destructuring applied an :or default only for :keys/:strs/:syms, not a
  direct {x :x} binding — so {x :x :or {x 9}} (and the & {…} kwargs form) ignored
  the default. Apply it for the direct binding too.
- fn didn't implement :pre/:post: a leading conditions map was evaluated as a body
  literal (so % was unbound and (.q %) blew up). Recognize it and assert pre
  before the body, bind % to the result, assert post, return %.
- (.q inst) on a deftype field with no matching method reads the field, like the
  JVM (was "No method q").
- A deftype implementing the clojure.lang collection interfaces now dispatches
  dissoc (without), contains? (containsKey), peek/pop (IPersistentStack), and
  keys/vals (via its Seqable seq) through its methods — they were field-only, so
  core.cache's caches and data.priority-map didn't behave as maps.

JVM-certified corpus rows for each. make test + shakesmoke green.
This commit is contained in:
Yogthos 2026-06-25 10:06:33 -04:00
parent 93b4d101a1
commit b21b99b275
5 changed files with 342 additions and 273 deletions

View file

@ -1,4 +1,11 @@
[
{:suite "destructure / :or" :label "map default when key absent" :expected "9" :actual "(let [{x :x :or {x 9}} {}] x)"}
{:suite "destructure / :or" :label "kwargs default when key absent" :expected "9" :actual "((fn [& {x :x :or {x 9}}] x))"}
{:suite "destructure / :or" :label "default not used when key present" :expected "5" :actual "(let [{x :x :or {x 9}} {:x 5}] x)"}
{:suite "deftype / field access" :label ".field reads a deftype field" :expected "7" :actual "(do (deftype FldT [q]) (.q (->FldT 7)))"}
{:suite "fn / pre-post" :label ":pre + :post pass" :expected "5" :actual "(do (defn ppf [x] {:pre [(pos? x)] :post [(= % x)]} x) (ppf 5))"}
{:suite "fn / pre-post" :label ":pre failure throws" :expected ":blocked" :actual "(do (defn ppg [x] {:pre [(pos? x)]} x) (try (ppg -1) (catch Throwable _ :blocked)))"}
{:suite "deftype / map-like dispatch" :label "dissoc + keys via IPersistentMap/Seqable methods" :expected "[:b]" :actual "(do (deftype MapT [m] clojure.lang.Seqable (seq [_] (seq m)) clojure.lang.IPersistentMap (without [_ k] (->MapT (dissoc m k))) (assoc [_ k v] (->MapT (assoc m k v)))) (vec (keys (dissoc (->MapT {:a 1 :b 2}) :a))))"}
{:suite "interop / Class.forName" :label "unknown class throws ClassNotFoundException" :expected ":nf" :actual "(try (Class/forName \"no.such.Klass\") (catch ClassNotFoundException _ :nf))"}
{:suite "interop / Class.forName" :label "known class resolves" :expected "\"java.lang.String\"" :actual "(.getName (Class/forName \"java.lang.String\"))"}
{:suite "clojure.string / replace" :label "char match and replacement" :expected "\"a-b-c\"" :actual "(clojure.string/replace \"a/b/c\" \\/ \\-)"}