Nilable record types + flow-sensitive nil narrowing (#235)
A record-or-nil (a protocol method whose impls return a record in one branch and nil in another, or an `if` over a ctor and nil) now types as a NILABLE record instead of widening to :any. A nilable record still bare-indexes its field reads (jrec-field-at falls back to jolt-get on nil), but some?/nil? do NOT fold on it, so a runtime guard is preserved — and inside (if (some? x) ..) / (if x ..) the then- branch narrows x to the non-nil record, so its reads bare-index AND unbox there. This is what lets the bounced ray type without a hint: scatter returns ScatterResult-or-nil (Metal absorbs some rays), and the consumer reads (:ray scattered) only under (if (some? scattered) ..). The narrowing proves scattered non-nil there. lattice: :nil type; :nil ∨ struct -> nilable struct, ∨ anything else -> :any; nilability is contagious through a struct join, which also now preserves :type when both sides agree (needed so a record ∨ its nilable self stays that record). truthy-type?/field-type/pred-on treat a nilable struct as maybe-nil. types: nil literal -> :nil; an `if` whose test is (some? x)/(nil? x)/x narrows the nilable local x in the proven branch. Ray tracer with NO hints: 38.4s -> 23.9s (~1.6x) — hit-sphere now types fully (0 jolt-get, 57 jrec-field-at, 38 fl-ops), identical to the hand-hinted build. run-narrow.ss gate, incl. the load-bearing check that the nil case still takes the else branch (the guard is not folded away). make test / shakesmoke green, selfhost holds, 0 new divergences. Co-authored-by: Yogthos <yogthos@gmail.com>
This commit is contained in:
parent
f124701393
commit
f920ff6ea2
5 changed files with 420 additions and 280 deletions
10
Makefile
10
Makefile
|
|
@ -4,7 +4,7 @@
|
|||
# build step. `make test` is the full gate. `make remint` rebuilds the seed after a
|
||||
# source change.
|
||||
|
||||
.PHONY: test ci values corpus unit smoke buildsmoke selfhost sci certify ffi transient infer wp devirt fieldread numwp fieldnum protoret directlink numeric inline shakesmoke remint
|
||||
.PHONY: test ci values corpus unit smoke buildsmoke selfhost sci certify ffi transient infer wp devirt fieldread numwp fieldnum protoret narrow directlink numeric inline shakesmoke remint
|
||||
|
||||
# Full gate (dev machine). Includes the self-host byte-fixpoint, which only holds
|
||||
# on the same Chez that minted the seed.
|
||||
|
|
@ -15,7 +15,7 @@ test: selfhost ci
|
|||
# lockfile) — it RUNS correctly on any Chez, but `selfhost` rebuilds it and a
|
||||
# different Chez version may emit byte-different (gensym/order) output, so the
|
||||
# byte-fixpoint is a dev-machine check, not a CI one (jolt-8479).
|
||||
ci: values corpus unit smoke buildsmoke sci ffi transient infer wp devirt fieldread numwp fieldnum protoret directlink numeric inline certify
|
||||
ci: values corpus unit smoke buildsmoke sci ffi transient infer wp devirt fieldread numwp fieldnum protoret narrow directlink numeric inline certify
|
||||
@echo "OK: CI gates passed"
|
||||
|
||||
# Self-host fixpoint: bootstrap.ss rebuild == checked-in seed.
|
||||
|
|
@ -98,6 +98,12 @@ fieldnum:
|
|||
protoret:
|
||||
@chez --script host/chez/run-protoret.ss
|
||||
|
||||
# Nilable record types + flow-sensitive narrowing: a record-or-nil types as a nilable
|
||||
# record (some?/nil? don't fold, so a runtime guard stays); inside (if (some? x) ..)
|
||||
# the then-branch narrows x to non-nil, so its field reads bare-index and unbox.
|
||||
narrow:
|
||||
@chez --script host/chez/run-narrow.ss
|
||||
|
||||
# Direct-linking emission: a closed-world build binds top-level app defs to jv$
|
||||
# Scheme bindings and routes app->app calls/refs to them, skipping var-deref +
|
||||
# jolt-invoke; ^:dynamic/^:redef and nested defs opt out.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue