The cast that opens a box, written down
This commit is contained in:
parent
a45e3806af
commit
34790e78af
92
FIX.org
92
FIX.org
@ -1033,3 +1033,95 @@ inverting the condition to move the last operand into the else arm costs a
|
||||
[not] per operand and worse locs than it buys. What would fix it is check_if
|
||||
preferring the arm that is not a compiler temp when it decides which one to
|
||||
blame — a change in check.ml, which this lane did not own.
|
||||
|
||||
* A numeric cast opens a dyn box, decided 2026-09-20
|
||||
Every numeric cast — =(f64 x)=, =(i64 x)=, =(u32 x)=, =(f32 x)=, all of
|
||||
them — takes a dyn operand now. Until this, the cast arm refused it with "f64
|
||||
converts a number, found dyn", and the only thing in the language that opened
|
||||
a box was a typed parameter, so a program wanting a number out of a dyn wrote
|
||||
a one-line function whose parameter slot did the unboxing and called *that*.
|
||||
A cast is the operator for "convert this to that"; it is the spelling that
|
||||
should have worked.
|
||||
|
||||
Three cases, and the middle one is the author's call.
|
||||
|
||||
1. Same kind. The box holds what the cast asks for, so the cast is the unbox
|
||||
and nothing else. A dyn box only ever holds an i64, an f64 or a bool among
|
||||
the numbers, so =(f32 d)= on a float box unboxes to f64 and narrows, and
|
||||
=(u32 d)= on an int box unboxes to i64 and narrows — each by the rule the
|
||||
same cast already follows on a typed operand.
|
||||
|
||||
2. Cross kind — COERCE, with a warning. The author's words were "just coerce
|
||||
it with a warning". =(f64 int-box)= is 7 -> 7.0 and =(i64 float-box)= is
|
||||
2.5 -> 2, the truncation toward zero =(i64 2.5)= already does, range-check
|
||||
and ArithError included. This overrides the tempting rule of matching the
|
||||
parameter boundary, which traps on a kind mismatch: a cast is already a
|
||||
conversion operator — =(f64 5)= converts a typed integer — so converting
|
||||
across the box is the cast doing its job. The warning exists because the
|
||||
box's kind was not what the program apparently expected, not because the
|
||||
conversion is in doubt.
|
||||
|
||||
3. A box holding a non-number traps: text, nil, keyword, vec, map — and
|
||||
*bool*, which is not a special case but the parameter boundary's existing
|
||||
answer mirrored. flan_dyn_need_i64 refuses a dyn holding true at a typed
|
||||
i64 parameter today, and =(i64 d)= refuses it for the same reason and in
|
||||
the same voice.
|
||||
|
||||
** The warning is once per SITE
|
||||
These casts sit in per-cell-per-frame loops — sand.flan runs at 120fps — so a
|
||||
per-occurrence line is a flood and not a diagnostic. check.ml threads the
|
||||
site's loc text into the runtime call and flan_dyn.c keeps a small table of
|
||||
sites it has already spoken about, keyed on the loc's *bytes* rather than its
|
||||
address: the two backends emit their own constants for it and neither promises
|
||||
that two mentions of one site share a pointer. Sixty-four sites, and past that
|
||||
it stops deduplicating rather than stops warning — the noisy failure, not the
|
||||
silent one. The line is:
|
||||
|
||||
flan FILE:LINE:COL: (f64 x) found a dyn holding an int, and converted it to f64 — warned once for this site
|
||||
|
||||
Both builds warn, dev and release. No precedent was found making a diagnostic
|
||||
of this kind dev-only: the allocation registry's notes are the one runtime
|
||||
family a release build drops, and those are a *feature* being disabled, not a
|
||||
warning being hushed. After the first hit this costs a tag compare and a
|
||||
linear scan of a handful of entries, which is nothing.
|
||||
|
||||
** How it lowers, and why that shape
|
||||
check.ml's [cast_dyn] builds a branch, not a call that converts:
|
||||
|
||||
(let ([s d])
|
||||
(if (= (flan_dyn_cast_kind s "file:1:2" "u32" 0) 1)
|
||||
(u32 (flan_dyn_need_f64 s))
|
||||
(u32 (flan_dyn_need_i64 s))))
|
||||
|
||||
flan_dyn_cast_kind answers 1 for a float box and 0 for an int box, traps for
|
||||
everything else, and warns when the answer disagrees with the target. Each arm
|
||||
is then an ordinary [Cast] over an ordinary need — *the same node* a typed
|
||||
operand of that type would have produced.
|
||||
|
||||
The alternative was a coercing runtime entry point answering the finished
|
||||
number, and it was rejected because =(i64 2.5)= is not a bare fptosi in this
|
||||
compiler: Emit.check_cast range-checks it and signals ArithError when the
|
||||
value will not fit, and lib/x86.ml does the same. A C function returning an
|
||||
int64_t would have had to grow its own second opinion about range and NaN, in
|
||||
a second place, for two backends — a fork of exactly the kind "Arithmetic
|
||||
semantics do not fork across the two spaces" forbids. With the branch there is
|
||||
nothing to keep in step, and "x86 tracks LLVM -O0" holds by construction:
|
||||
programs/dyn-cast.flan prints byte-identical output on both backends,
|
||||
warnings and trap included.
|
||||
|
||||
The generic cast arm — =(t x)= inside a body with ={:where (numeric? $t)}= —
|
||||
did NOT grow a dyn case and did not need one: the operand's type there is what
|
||||
the bound admits, and numeric? does not admit dyn, so a dyn cannot reach that
|
||||
arm. Pinned in test_flan.ml.
|
||||
|
||||
** What this repeals
|
||||
test_flan.ml's row "a keyword with no expectation converts as dyn" pinned
|
||||
=(i64 :space)= as a *check* error. It is a well-typed program now and a
|
||||
run-time trap instead; the row became an [accepts] saying so. That is the
|
||||
whole of the behaviour change outside the new feature.
|
||||
|
||||
** For the author: the shims in sand.flan can go
|
||||
sand.flan defines =dyn->f64= and =dyn->u32=, one-line functions whose only
|
||||
job is that their parameter slot unboxes. Every call site can now write the
|
||||
cast directly — =(f64 d)=, =(u32 d)= — and the two defns deleted. Not done
|
||||
here: sand.flan is the author's WIP and this lane did not touch it.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user