Commit graph

2 commits

Author SHA1 Message Date
Yogthos
6abfd660ce Direct-link + whole-program by default for program runs; open for the REPL
Running a program is a closed world — every namespace is required, then it runs
to completion — so make it direct-link by default (inlining, record shapes, the
inference's specialization), and for a -m/-M entry auto-enable the whole-program
cross-namespace inference pass. A decomposed multi-namespace program was ~3.7x
slower than the same code in one namespace purely because per-namespace
inference can't see a caller in a not-yet-loaded namespace; this closes that for
the common case with no flags and no hints.

Interactive modes (repl, -e, nrepl-server) stay indirect/open — they have to let
you redefine vars, which direct-linking seals against. Opt-outs:
JOLT_NO_DIRECT_LINK forces the open path even for a program run (hot-reload,
runtime redefinition); JOLT_NO_WHOLE_PROGRAM keeps direct-linking but per-ns;
JOLT_DIRECT_LINK / JOLT_WHOLE_PROGRAM still force-on. Namespaces required inside
-main (after the batch pass) fall back to per-ns inference.

The success checker (RFC 0006) rides on the inference for free, but a casual
program run shouldn't spam type warnings just because it now direct-links, so its
default-on is suppressed when direct-linking was auto-enabled (:direct-link-auto?);
an explicit JOLT_DIRECT_LINK or JOLT_TYPE_CHECK still turns it on. whole-program-
test and devirt-test opt their per-ns baseline out of the new auto-default.

Docs: RFC 0005 gains 'Compilation modes and defaults' + 'Cross-namespace
inference'; RFC 0004 documents cross-ns/param hints; self-hosting-compiler and
--help updated. Full gate green.
2026-06-14 15:44:01 -04:00
Yogthos
5f59c02b69 feat: expand type-hint lookup specialization (^Record, get-form, checked mode, docs)
Builds on the ^:struct keyword-lookup hint:

- ^TypeName for records. A tag naming a defrecord/deftype now resolves to the
  struct fast path: record instances are tables tagged :jolt/deftype (not
  :jolt/type), so a raw keyword get is correct for them. A new host contract fn
  record-type? detects a record by its ->Name constructor; a non-record tag
  (^String, ^long, ...) is ignored, as before.

- (get m :k) and (get m :k default) now get the same inlined keyword lookup as
  (:k m): the representation guard fast path when unhinted, and the bare get
  when the subject is ^:struct/^Record. A variable/number/string key still
  falls through to core-get. The two call shapes share one emitter
  (emit-kw-lookup).

- JOLT_CHECK_HINTS=1 turns a violated hint into a clear runtime error (naming
  the local and key) by keeping the guard and throwing on the tagged arm. It is
  off by default with zero cost to normal builds (a hinted lookup still emits a
  bare get), and is part of the image-cache fingerprint. This is the answer to
  "a lying hint is silent": opt into checking during development.

- Docs: RFC 0004 records the design, soundness contract, and measurements; the
  reader spec gains S12b (hints are semantically transparent; jolt recognizes
  ^:struct and ^Record as lookup-optimization assertions).

There is no Clojure keyword equivalent for "plain map / fast keyword access"
(Clojure hints are class names), so ^:struct stays a jolt-specific flag,
analogous to ^:dynamic.

Verified: conformance 335/335 in all three modes and the full jpm test pass; a
seeded ray-tracer render is byte-identical hinted vs unhinted; the struct-hint
test covers record hints, the get-form, inline propagation, and the checked-mode
error. Full render with hints holds at 13.3s -> 10.9s (1.22x).
2026-06-12 20:20:25 -04:00