flan/test/programs/dyn-cast.flan
Joseph Ferano 57fe91f303 Five records become one, and every citation lands somewhere
FIX.org, NEXT.md, DISCUSS.org, docs/DISCUSS.md and the session handoff at the
root are one TODO.org now: 293 entries under seven subsystem headings, each
carrying an org keyword that says where it stands. A DONE entry is a few lines
saying what was decided and what that rules out; the reasoning that would not
compress — the embedding spike and the four reports the hand-written x86
backend was built from — moved into docs/BUILT.md instead, and its entries
point there in one line.

Every entry was checked against the tree before it got a keyword, and the
prose was wrong in both directions. Things the deleted files called open were
built: the first-evaluation stall, main being redefinable, macro parameter
lists, the type-limit constants, the array constructors, the byte fills,
inc/dec, the discard's fontification, the Emacs buffers, rt_die's _exit, the
backtrace surface, and the acceptance failure that could print and still exit
zero. Things they called done were not: the backend reports' no-plan buckets
had gone stale in the other direction, the value-dependent defvar was
superseded rather than built, and macro-expansion source locations are on an
unmerged lane, so that entry is NEXT and names the branch.

Every comment that cited one of the five by name now cites a heading that
exists, in TODO.org or in docs/BUILT.md. The session reports under
docs/handoffs/ keep naming the files they worked on, because rewriting them
would falsify what those sessions did; each carries a note saying where the
content went.
2026-09-21 21:05:48 +07:00

73 lines
3.3 KiB
Plaintext

;;;; A numeric cast opens a dyn box — TODO.org, "A numeric cast opens a dyn box".
;;;;
;;;; Before this, a typed parameter was the only thing in the language that
;;;; could open a box, 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", so it is the spelling
;;;; that should have worked; here it does.
;;;;
;;;; Three behaviours, in the order main runs them:
;;;;
;;;; 1. Same kind. The box holds what the cast asks for, so the cast is the
;;;; unbox and nothing else is written or warned.
;;;; 2. Cross kind. The box holds the other number. The cast still converts —
;;;; exactly as the same cast converts a typed operand of that type — and
;;;; one line goes to stderr naming the site. The two cross-kind casts here
;;;; sit inside a dotimes that runs them eight times each, and stderr gets
;;;; two lines, because the warning is per *site*: these casts live in frame
;;;; loops and a per-value line would be a flood rather than a diagnostic.
;;;; 3. A box holding something that is not a number. That still traps, the way
;;;; a typed parameter traps on it today, and the runtime owns the sentence.
;;;; It is the last thing main does, so everything above it has printed.
;; The box. Unannotated parameters are dyn, so this is the boxing site and the
;; call below is where a typed literal crosses in.
(defn as-dyn [x] dyn x)
(defn main [] ()
(let [i (as-dyn 7)
f (as-dyn 2.5)]
;; ── 1. Same kind: the cast is the unbox ──────────────────────────
;; An int box to every integer width, and a float box to both floats.
;; None of these warns: the box holds what was asked for.
(print (i64 i))
(print "\n")
(print (i64 (i32 i)))
(print "\n")
(print (i64 (u32 i)))
(print "\n")
(print (i64 (u8 i)))
(print "\n")
(print (f64 f))
(print "\n")
(print (f64 (f32 f)))
(print "\n")
;; ── 2. Cross kind: it converts, and warns once for the site ──────
;; (f64 int-box) is 7 -> 7.0, the same widening (f64 7) does on a typed
;; operand. (i64 float-box) is 2.5 -> 2, the same truncation toward zero
;; (i64 2.5) does — range-checked by the emitter's own check_cast, because
;; the arm really is that cast with an unbox in front of it.
;;
;; Eight turns of the loop, two sites, two lines on stderr.
(dotimes [n 8]
(print (f64 i))
(print "\n")
(print (i64 f))
(print "\n"))
;; A third site, and the third warning. An int box straight to f32 is one
;; sitofp from i64, which is what (f32 x) on a typed i64 is. Written down
;; because the tempting "fix" — go through f64 and then narrow — rounds
;; twice and is a different number for values an i64 can hold and an f64
;; cannot round to an f32 the same way.
(print (f64 (f32 i)))
(print "\n"))
;; ── 3. And the box that holds no number ──────────────────────────────
;; A bool, which is what the typed parameter boundary already refuses: a dyn
;; holding true does not satisfy an i64 there and does not satisfy (i64 x)
;; here. The runtime owns the message.
(print (i64 (as-dyn true)))
(print "\n"))