From 2ace3bfe2b59bfba4bfd4d97063aa135028fcd7a Mon Sep 17 00:00:00 2001 From: Joseph Ferano Date: Sat, 26 Sep 2026 14:30:17 +0700 Subject: [PATCH] (char n) on a u64 past 2^63 traps naming the u64 it was given. --- lib/check.ml | 5 +++++ lib/emit.ml | 1 + runtime/flan_rt.c | 8 ++++++++ test/programs/char.flan | 11 ++++++++--- test/test_acceptance.ml | 8 +++++--- 5 files changed, 27 insertions(+), 6 deletions(-) diff --git a/lib/check.ml b/lib/check.ml index bc37a738..8a9e3db1 100644 --- a/lib/check.ml +++ b/lib/check.ml @@ -14752,6 +14752,11 @@ and named_call ?(qualified = false) ctx ~want loc name args = in (match a.Tast.ty with | Types.Char -> expect ctx loc ~want a + (* A u64 goes as itself, so one past 2^63 is named as the number + it is and not as the negative i64 with its bits. *) + | Types.Int Types.U64 -> + expect ctx loc ~want + (rt loc Types.Char "flan_char_of_u64" [ a; here loc ]) | Types.Int _ -> expect ctx loc ~want (checked (widen loc dyn_i64 a)) (* Explicit, so a dyn int converts as a typed one does. *) | Types.Dyn -> diff --git a/lib/emit.ml b/lib/emit.ml index c0583628..9c271658 100644 --- a/lib/emit.ml +++ b/lib/emit.ml @@ -5146,6 +5146,7 @@ declare i64 @flan_dyn_need_int(i64, i32, ptr, i64) declare i64 @flan_dyn_int_of(i64) declare i32 @flan_dyn_need_char(i64, ptr, i64) declare i32 @flan_char_of(i64, ptr, i64) +declare i32 @flan_char_of_u64(i64, ptr, i64) ; A numeric cast written on a dyn answers which numeric tag the box holds; ; check.ml's [cast_dyn] branches on it and each arm is an ordinary need plus ; the ordinary cast. The two slices are the site's location and the target's diff --git a/runtime/flan_rt.c b/runtime/flan_rt.c index f4b91a4f..98a86cb3 100644 --- a/runtime/flan_rt.c +++ b/runtime/flan_rt.c @@ -2981,6 +2981,14 @@ uint32_t flan_char_of(int64_t n, const uint8_t *loc, int64_t loclen) { rt_trap((const uint8_t *)"InvalidChar", 11); } +/* The same for a u64, which past 2^63 has no i64 to arrive as. */ +uint32_t flan_char_of_u64(uint64_t n, const uint8_t *loc, int64_t loclen) { + if (n <= 0x10ffff && !(n >= 0xd800 && n <= 0xdfff)) return (uint32_t)n; + flan_say(loc, loclen, "%llu is not a Unicode scalar value, so it is not a char", + (unsigned long long)n); + rt_trap((const uint8_t *)"InvalidChar", 11); +} + /* [n] elements from [src] onto the end of a Vec, growing it once. [src] may * point into the Vec's own block — (append s (str s)) — so where it lies is * found before the grow and read again after it: the grow frees the old diff --git a/test/programs/char.flan b/test/programs/char.flan index 5d58768e..17070755 100644 --- a/test/programs/char.flan +++ b/test/programs/char.flan @@ -1,7 +1,8 @@ ;;;; A typed char (decision 127): a char literal is a char unless typed code ;;;; wants a number, and a char crossing into dyn stays a char. It compares, ;;;; orders and hashes; (i32 c) and (char n) convert. With "dyn-int" a dyn -;;;; int at a char parameter traps, and with "surrogate" (char n) does. +;;;; int at a char parameter traps, with "surrogate" (char n) does, and with +;;;; "u64" (char n) on a u64 past 2^63 does, naming the u64. (defn show [x] () (println x)) (defn take-char [c char] char c) @@ -46,8 +47,12 @@ (show cs) (show (at (a-dyn cs) 1))) (when (> (length args) 1) - (if (= (at args 1) "dyn-int") - (println (take-char (a-dyn 97))) + (cond + (= (at args 1) "dyn-int") (println (take-char (a-dyn 97))) + (= (at args 1) "u64") + (let [n (+ (u64 9223372036854775807) (u64 (length args)))] + (println (char n))) + :else (let [n (+ 0xD800 (length args))] (println (char n))))) 0) diff --git a/test/test_acceptance.ml b/test/test_acceptance.ml index d39974b0..d91b3175 100644 --- a/test/test_acceptance.ml +++ b/test/test_acceptance.ml @@ -5551,11 +5551,13 @@ level "1" wanted: %S (exit 134)\n" arg (if x86 then ", --x86" else "") text code want end) - [ ("dyn-int", "programs/char.flan:50:27: dyn: a char is wanted \ + [ ("dyn-int", "programs/char.flan:51:53: dyn: a char is wanted \ here, and this is an int, 97. Convert a code point \ with (char n)"); - ("surrogate", "programs/char.flan:52:18: 55298 is not a Unicode \ - scalar value, so it is not a char") ]) + ("surrogate", "programs/char.flan:57:18: 55298 is not a Unicode \ + scalar value, so it is not a char"); + ("u64", "programs/char.flan:54:18: 9223372036854775809 is not a \ + Unicode scalar value, so it is not a char") ]) [ false; true ]; (* A String, and a str made from one, cross into dyn as text measured like any other: characters counted, ASCII or not. *)