A u64 constant above 2^63 can be written in decimal, and a cast's integer literal too wide for i32 is read at the cast's type
This commit is contained in:
parent
aa629da2e5
commit
f142cfaadf
16
lib/check.ml
16
lib/check.ml
@ -3744,7 +3744,8 @@ and in_range loc k n =
|
|||||||
else if bits = 64 then
|
else if bits = 64 then
|
||||||
(* A u64 literal is its 64-bit pattern, so anything at or above 2^63
|
(* A u64 literal is its 64-bit pattern, so anything at or above 2^63
|
||||||
arrives here as a negative [int64] and is still in range —
|
arrives here as a negative [int64] and is still in range —
|
||||||
0xcbf29ce484222325 is a real u64 and not an error. The cost is that a
|
0xcbf29ce484222325 is a real u64 and not an error, and so is its
|
||||||
|
decimal, which the reader reads as the same pattern. The cost is that a
|
||||||
negative *decimal* literal is accepted as a u64 too, because the
|
negative *decimal* literal is accepted as a u64 too, because the
|
||||||
reader records only the value and not how it was written. Narrower
|
reader records only the value and not how it was written. Narrower
|
||||||
unsigned types keep the strict check, which is where a typo like 300
|
unsigned types keep the strict check, which is where a typo like 300
|
||||||
@ -8796,7 +8797,18 @@ and named_call ?(qualified = false) ctx ~want loc name args =
|
|||||||
prim (Tast.Cast target) target [ a ]
|
prim (Tast.Cast target) target [ a ]
|
||||||
| _ when is_cast name && List.length args = 1 ->
|
| _ when is_cast name && List.length args = 1 ->
|
||||||
let target = resolve_name ctx.env ~seen:[] loc name in
|
let target = resolve_name ctx.env ~seen:[] loc name in
|
||||||
let a = check ctx (List.hd args) in
|
(* An integer literal too wide for the i32 it would default to is checked
|
||||||
|
at the target instead, so (u64 2935910691) and (i64 5000000000) are the
|
||||||
|
constants they say. One that fits i32 keeps the default and the cast,
|
||||||
|
which is what (u32 -1) has always meant. *)
|
||||||
|
let want =
|
||||||
|
match (List.hd args).Ast.e, target with
|
||||||
|
| Ast.Int n, (Types.Int _ | Types.Float _)
|
||||||
|
when Int64.compare n (-2147483648L) < 0
|
||||||
|
|| Int64.compare n 2147483647L > 0 -> Some target
|
||||||
|
| _ -> None
|
||||||
|
in
|
||||||
|
let a = check ctx ?want (List.hd args) in
|
||||||
(match a.Tast.ty with
|
(match a.Tast.ty with
|
||||||
| Types.Enum _ -> ()
|
| Types.Enum _ -> ()
|
||||||
(* A dyn opens here — [cast_dyn], TODO.org, "A numeric cast opens a dyn
|
(* A dyn opens here — [cast_dyn], TODO.org, "A numeric cast opens a dyn
|
||||||
|
|||||||
@ -153,8 +153,20 @@ let read_number st =
|
|||||||
| None -> Loc.failk "reader/malformed-number" (Loc.upto loc (here st))
|
| None -> Loc.failk "reader/malformed-number" (Loc.upto loc (here st))
|
||||||
"malformed float literal %s" text
|
"malformed float literal %s" text
|
||||||
else
|
else
|
||||||
|
(* A decimal above the largest i64 and below 2^64 is read as its 64-bit
|
||||||
|
pattern, as a hex literal is, so a u64 constant can be written in
|
||||||
|
decimal. It therefore arrives negative, and [Check.in_range] makes the
|
||||||
|
same allowance for it that it makes for hex. *)
|
||||||
|
let unsigned () =
|
||||||
|
if String.for_all (fun c -> c >= '0' && c <= '9') text then
|
||||||
|
Int64.of_string_opt ("0u" ^ text)
|
||||||
|
else None
|
||||||
|
in
|
||||||
match Int64.of_string_opt text with
|
match Int64.of_string_opt text with
|
||||||
| Some i -> spanned st loc (Form.Int i)
|
| Some i -> spanned st loc (Form.Int i)
|
||||||
|
| None ->
|
||||||
|
match unsigned () with
|
||||||
|
| Some i -> spanned st loc (Form.Int i)
|
||||||
| None -> Loc.failk "reader/malformed-number" (Loc.upto loc (here st))
|
| None -> Loc.failk "reader/malformed-number" (Loc.upto loc (here st))
|
||||||
"malformed integer literal %s" text
|
"malformed integer literal %s" text
|
||||||
|
|
||||||
|
|||||||
16
test/programs/u64-decimal.flan
Normal file
16
test/programs/u64-decimal.flan
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
;;;; A u64 constant above 2^63 written in decimal, and an integer literal too
|
||||||
|
;;;; wide for i32 as a cast's argument.
|
||||||
|
|
||||||
|
(defconst top u64 18446744073709551615)
|
||||||
|
(defonce fnv u64 14695981039346656037)
|
||||||
|
|
||||||
|
(defn main [] i32
|
||||||
|
(println top)
|
||||||
|
(println fnv)
|
||||||
|
(println (u64 2935910691))
|
||||||
|
(println (u64 18446744073709551615))
|
||||||
|
(println (i64 -5000000000))
|
||||||
|
(println (f64 4000000000))
|
||||||
|
;; A literal that fits i32 keeps its default and is cast, as before.
|
||||||
|
(println (u32 -1))
|
||||||
|
0)
|
||||||
@ -508,6 +508,14 @@ let () =
|
|||||||
"2 3 4 7 9 3\n";
|
"2 3 4 7 9 3\n";
|
||||||
outputs ~x86:true "vec-new takes a type expression, x86"
|
outputs ~x86:true "vec-new takes a type expression, x86"
|
||||||
"programs/vec-new-type.flan" "2 3 4 7 9 3\n";
|
"programs/vec-new-type.flan" "2 3 4 7 9 3\n";
|
||||||
|
(* A u64 above 2^63 in decimal, and a cast of a literal too wide for i32. *)
|
||||||
|
let u64_out =
|
||||||
|
"18446744073709551615\n14695981039346656037\n2935910691\n\
|
||||||
|
18446744073709551615\n-5000000000\n4e+09\n4294967295\n"
|
||||||
|
in
|
||||||
|
outputs "a u64 constant in decimal" "programs/u64-decimal.flan" u64_out;
|
||||||
|
outputs ~x86:true "a u64 constant in decimal, x86"
|
||||||
|
"programs/u64-decimal.flan" u64_out;
|
||||||
(* into. The count of pulls is the assertion a unit test cannot make: one
|
(* into. The count of pulls is the assertion a unit test cannot make: one
|
||||||
pass, one call per element per stage it reaches, and no intermediate
|
pass, one call per element per stage it reaches, and no intermediate
|
||||||
collection anywhere. The two show lines either side of it are the same
|
collection anywhere. The two show lines either side of it are the same
|
||||||
|
|||||||
@ -92,6 +92,13 @@ let () =
|
|||||||
reads "bare plus" "+" "+";
|
reads "bare plus" "+" "+";
|
||||||
reads "float" "0.05" "0.05";
|
reads "float" "0.05" "0.05";
|
||||||
reads "hex" "0xE6B800FF" "3870818559";
|
reads "hex" "0xE6B800FF" "3870818559";
|
||||||
|
(* A decimal between 2^63 and 2^64 is its bit pattern, as hex is; one past
|
||||||
|
2^64, or a negative one past the smallest i64, is still malformed. *)
|
||||||
|
reads "u64 decimal" "18446744073709551615" "-1";
|
||||||
|
rejects "decimal past 2^64" "18446744073709551616"
|
||||||
|
~needle:"malformed integer literal";
|
||||||
|
rejects "negative decimal past i64" "-9223372036854775809"
|
||||||
|
~needle:"malformed integer literal";
|
||||||
reads "string" "\"SAND\"" "\"SAND\"";
|
reads "string" "\"SAND\"" "\"SAND\"";
|
||||||
reads "symbol" "empty-at?" "empty-at?";
|
reads "symbol" "empty-at?" "empty-at?";
|
||||||
reads "qualified" "rl/draw-fps" "rl/draw-fps";
|
reads "qualified" "rl/draw-fps" "rl/draw-fps";
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user