Checked narrow casts; fix runtime require in self-contained-built binaries

byte/short/int/long/char silently wrapped or passed out-of-range values
through; the JVM range-checks (RT.byteCast family). One checked-cast
helper now carries the ranges: a double range-checks ITSELF before
truncating ((byte 1.1) is 1, (byte 127.000001) throws), NaN casts to 0,
ratios and bigdecs truncate, a non-number is CCE, and the throw carries
the JVM message. float range-checks against Float/MAX_VALUE. The
unchecked-* casts now genuinely wrap and sign-fold ((unchecked-byte 200)
is -56 — the old bit-and lost the sign) with doubles saturating like
Java's conversions; unchecked-long/int are host natives. double/float of
a bigdec convert instead of crashing. The no-single-float residue stays
accepted (SPEC.md).

Also fixes #290: a binary built by the SELF-CONTAINED joltc died with
'variable var-deref is not bound' when a namespace loaded at runtime.
The in-process build compiled flat.ss against a clean copy-environment,
which orphans every top-level define in locations the binary's runtime
eval can't see. It now compiles against the default interaction
environment (defines land in the real symbol cells, same as the legacy
fresh-Chez path) and a generated prologue pre-binds each kernel name the
runtime redefines to its kernel value, so the earliest boot reads match
the legacy path's primitive references. requiring-resolve is implemented
(the issue's dynamic-require pattern), and the release workflow smokes a
runtime require in a built binary.

Cast namespaces byte/short/int/long/char now fully clean; cts baseline
5805 -> 5857 pass, 67 baselined namespaces. 7 JVM-certified corpus rows.
This commit is contained in:
Yogthos 2026-07-02 09:42:06 -04:00
parent f80f9aab4b
commit d0e1a11934
10 changed files with 237 additions and 77 deletions

View file

@ -132,6 +132,19 @@ arithmetic fast path (both operands Chez numbers → the raw Chez op) or force
every `+`/`-`/`*` through an unwrapping dispatcher, de-optimizing all
arithmetic. Same shape as the accepted BigInt-vs-Long unification.
The cast RANGE contract is full parity (corpus `casts / *`): `byte`/`short`/
`int`/`long`/`char` range-check like `RT.byteCast` — an out-of-range value is
IllegalArgumentException "Value out of range for byte: 128". A double operand
range-checks ITSELF before truncating (`(byte 1.1)` is `1`, `(byte 127.000001)`
throws), NaN casts to 0, ratios and bigdecs truncate, a non-number is
ClassCastException. `float` range-checks against Float/MAX_VALUE. The
`unchecked-*` casts wrap and sign-fold like the JVM primitive conversions
(`(unchecked-byte 200)` is `-56`; a double saturates instead of wrapping).
What jolt does NOT model is a distinct single-float type: `(float x)` keeps
the double VALUE, so a double below Float/MIN_VALUE stays nonzero and float
rounding does not occur (the accepted no-single-float residue, baselined with
`:integer-box-model`'s class residue).
## Number operations
Binary arithmetic and comparisons follow the JVM's `Numbers.ops(x, y)` category