From 10610fb76b57203ecaee8dbb96019db2b28547e9 Mon Sep 17 00:00:00 2001 From: Joseph Ferano Date: Sun, 20 Sep 2026 13:54:07 +0700 Subject: [PATCH] The corpus program for a cast that opens a box --- runtime/flan_dyn.c | 4 +-- runtime/flan_dyn_stub.c | 2 +- test/programs/dyn-cast.flan | 72 +++++++++++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+), 3 deletions(-) create mode 100644 test/programs/dyn-cast.flan diff --git a/runtime/flan_dyn.c b/runtime/flan_dyn.c index 8e13dd4..3a4436f 100644 --- a/runtime/flan_dyn.c +++ b/runtime/flan_dyn.c @@ -1219,8 +1219,8 @@ int32_t flan_dyn_cast_kind(flan_dyn v, const uint8_t *loc, int64_t loc_len, "flan %.*s: (%.*s x) found a dyn holding %s, and converted it to " "%.*s — warned once for this site\n", (int)loc_len, (const char *)loc, (int)target_len, - (const char *)target, tag_of(v), (int)target_len, - (const char *)target); + (const char *)target, is_float ? "a float" : "an int", + (int)target_len, (const char *)target); } return is_float; } diff --git a/runtime/flan_dyn_stub.c b/runtime/flan_dyn_stub.c index b11cff6..ce64ee7 100644 --- a/runtime/flan_dyn_stub.c +++ b/runtime/flan_dyn_stub.c @@ -334,7 +334,7 @@ int32_t flan_dyn_cast_kind(flan_dyn v, const uint8_t *loc, int64_t loc_len, "flan %.*s: (%.*s x) found a dyn holding %s, and converted it " "to %.*s — warned once for this site\n", (int)loc_len, (const char *)loc, (int)target_len, - (const char *)target, is_float ? "float" : "int", + (const char *)target, is_float ? "a float" : "an int", (int)target_len, (const char *)target); } } diff --git a/test/programs/dyn-cast.flan b/test/programs/dyn-cast.flan new file mode 100644 index 0000000..ac52711 --- /dev/null +++ b/test/programs/dyn-cast.flan @@ -0,0 +1,72 @@ +;;;; 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"))