flan/test/programs/dyn-cast.flan

73 lines
3.3 KiB
Plaintext

;;;; A numeric cast opens a dyn box — FIX.org 2026-09-20.
;;;;
;;;; 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"))