feat: bounded union types in the RFC 0005 lattice (jolt-pz5)
The success checker (RFC 0006) used to lose differing if-branches to :any
and accept the use. (inc (if c "a" :k)) typed the if as :any — sound but
imprecise, since the value is provably {:str | :kw}, every member of which
is in inc's error domain.
Adds {:union #{T...}} to the lattice: join-t forms a scalar union of
differing branches instead of collapsing to :any, capped at 4 distinct
scalars (the member space is the five scalar tags, so the lattice stays
finite and the inter-procedural fixpoint still terminates). The checker's
not-number?/not-seqable? report a union only when EVERY member is in the
error domain — any valid member accepts the call, so still no false
positives. type-name renders "a string or a keyword".
Unions are scalar-only and carry no :struct/:vec/:set key, so every
structural predicate already treats them as opaque — specialization sees
them exactly as :any and codegen is unchanged. Gate green, suite 4718,
conformance 335/335, bench even.
This commit is contained in:
parent
1c0b3fe9bd
commit
9f076937af
2 changed files with 75 additions and 7 deletions
|
|
@ -40,6 +40,26 @@
|
|||
(assert (= 0 (nd "(inc (count [1 2 3]))")) "count of vector + inc of :num both fine")
|
||||
(assert (= 0 (nd "(inc (first [1 2 3]))")) "first of vector -> :num, inc fine")
|
||||
|
||||
# --- bounded unions (jolt-pz5): report only when EVERY member is in the error
|
||||
# domain; accept when any member is valid. Differing branches used to collapse
|
||||
# to :any (accepted); now they form {:union #{...}} and are checked per-member.
|
||||
(assert (= 1 (nd "(fn [c] (inc (if c \"a\" :k)))"))
|
||||
"inc of {:str | :kw} — every member non-number — reported")
|
||||
(assert (= 0 (nd "(fn [c] (inc (if c 1 \"x\")))"))
|
||||
"inc of {:num | :str} — :num is fine — still accepted")
|
||||
(assert (= 1 (nd "(fn [c] (count (if c :k 5)))"))
|
||||
"count of {:kw | :num} — both non-seqable — reported")
|
||||
(assert (= 0 (nd "(fn [c] (count (if c :k \"ab\")))"))
|
||||
"count of {:kw | :str} — :str is seqable — accepted")
|
||||
(assert (= 1 (nd "(fn [c] (inc (if c \"a\" (if c :k :j))))"))
|
||||
"inc of nested all-non-number union reported")
|
||||
(assert (= 0 (nd "(fn [c] (inc (if c \"a\" (if c :k 1))))"))
|
||||
"inc of union with a buried :num member accepted")
|
||||
# a union is opaque to structural specialization — it keeps the dynamic guard,
|
||||
# exactly like :any, so a keyword lookup over it is never mis-specialized.
|
||||
(assert (= 0 (nd "(fn [c] (:r (if c {:r 1} {:g 2})))"))
|
||||
"keyword lookup over a struct union is accepted (no false positive)")
|
||||
|
||||
# --- the diagnostic carries op + type + a message ----------------------------
|
||||
(def one (in (diags "(inc \"x\")") 0))
|
||||
(assert (= "inc" (get one :op)) "diagnostic names the op")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue