(char n) on a u64 past 2^63 traps naming the u64 it was given.

This commit is contained in:
Joseph Ferano 2026-09-26 14:30:17 +07:00
parent 92fbeb4fc9
commit 2ace3bfe2b
5 changed files with 27 additions and 6 deletions

View File

@ -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 ->

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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. *)