feat: negative/never types — calling a non-function, wrong-arity (jolt-wwy)

Two provably-wrong cases the inference already has the facts for, closing the
last RFC 0006 open question:

- Calling a non-function. At an :invoke whose callee is provably :num or :str
  (the only non-callable types — keywords/maps/vectors/sets are IFn), report
  "cannot call a number as a function". Default level (no closed-world: the
  callee type is inferred at the call site). Covers (5 1), ("hi" 0),
  ((+ 1 2) :k), a let-bound number, and a var holding a number (via vtype-box
  in direct-link). A union is non-callable only when every member is, so
  ((if c 1 :k) x) is accepted (:kw is callable). Verified zero false positives
  on the ray tracer, which calls maps/keywords/vectors as fns throughout.

- Wrong arity to a user fn. The registered single-fixed-arity sig (jolt-zo1)
  makes a mismatched arg count provably throw; reported under the
  JOLT_TYPE_CHECK_USER opt-in (same closed-world boundary; ^:redef/variadic
  skipped). Caught at compile time before the runtime arity error.

Both fold into the existing infer walk, carry :pos for file:line:col, and keep
no-false-positives. Gate green, suite 4718, conformance 335/335, runtime bench
even (compile-time only).
This commit is contained in:
Yogthos 2026-06-13 14:48:14 -04:00
parent e071d09170
commit 328f88636e
3 changed files with 100 additions and 23 deletions

View file

@ -142,6 +142,26 @@
(os/setenv "JOLT_TYPE_CHECK" nil)
r)
(fn [s] (nil? (string/find "type error" s))))
# negative/never types (jolt-wwy): calling a non-function is reported by default
# in direct-link; wrong-arity to a user fn under the JOLT_TYPE_CHECK_USER opt-in
(def tcn (string (or (os/getenv "TMPDIR") "/tmp") "/jolt-tcneg-" (os/time) ".clj"))
(spit tcn "(ns tcn)\n\n(defn nope []\n (let [n 5] (n 1)))\n")
(check "direct-link reports calling a number as a function"
(do (os/setenv "JOLT_DIRECT_LINK" "1")
(def r (run-err tcn))
(os/setenv "JOLT_DIRECT_LINK" nil)
r)
(has "cannot call a number as a function"))
(def tca (string (or (os/getenv "TMPDIR") "/tmp") "/jolt-tcarity-" (os/time) ".clj"))
(spit tca "(ns tca)\n\n(defn f [x y] (+ x y))\n(defn g [] (f 1))\n")
(check "JOLT_TYPE_CHECK_USER reports wrong arity to a user fn"
(do (os/setenv "JOLT_DIRECT_LINK" "1")
(os/setenv "JOLT_TYPE_CHECK_USER" "1")
(def r (run-err tca))
(os/setenv "JOLT_DIRECT_LINK" nil)
(os/setenv "JOLT_TYPE_CHECK_USER" nil)
r)
(has "wrong number of args (1) passed to `f` (expected 2)"))
(if (> fails 0)
(error (string "cli-test: " fails " failing check(s)"))