A char minus a char takes the integer width its site wants, a chain of + or - folds left step by step, and a char plus a u64 is checked unsigned.
This commit is contained in:
parent
33203bd3c5
commit
7361830938
150
lib/check.ml
150
lib/check.ml
@ -11233,45 +11233,84 @@ and char_arith loc name =
|
|||||||
|
|
||||||
(* One step of char arithmetic (decision 131, Kotlin's rules): a char plus or
|
(* One step of char arithmetic (decision 131, Kotlin's rules): a char plus or
|
||||||
minus an integer is a char, checked to be a scalar value — at compile time
|
minus an integer is a char, checked to be a scalar value — at compile time
|
||||||
when both sides are constants, by [flan_char_of] at run time otherwise —
|
when both sides are constants, at run time otherwise — and a char minus a
|
||||||
and a char minus a char is the distance between them, an i32. Anything
|
char is the distance between them, an integer at the width the site wants
|
||||||
else with a char in it is refused. *)
|
(i32 when it wants none), as any integer expression is. A step with no char
|
||||||
and char_step _ctx loc name (a : Tast.expr) (b : Tast.expr) : Tast.expr =
|
in it is the ordinary one. Anything else with a char in it is refused. *)
|
||||||
|
and char_step ~want loc name (a : Tast.expr) (b : Tast.expr) : Tast.expr =
|
||||||
let i64 e = widen loc dyn_i64 e in
|
let i64 e = widen loc dyn_i64 e in
|
||||||
let const (e : Tast.expr) =
|
let const (e : Tast.expr) =
|
||||||
match e.Tast.e with Tast.Int (n, _) -> Some n | _ -> None
|
match e.Tast.e with Tast.Int (n, _) -> Some n | _ -> None
|
||||||
in
|
in
|
||||||
let op = if String.equal name "+" then Tast.Add else Tast.Sub in
|
let op = if String.equal name "+" then Tast.Add else Tast.Sub in
|
||||||
let fold f = match const a, const b with
|
let scalar n =
|
||||||
| Some x, Some y -> Some (f x y) | _ -> None
|
Int64.compare n 0L >= 0 && Int64.compare n 0x10ffffL <= 0
|
||||||
in
|
|
||||||
let apply x y = if String.equal name "+" then Int64.add x y else Int64.sub x y in
|
|
||||||
match a.Tast.ty, b.Tast.ty with
|
|
||||||
| Types.Char, Types.Char when String.equal name "-" ->
|
|
||||||
(match fold Int64.sub with
|
|
||||||
| Some n -> mk loc (Types.Int Types.I32) (Tast.Int (n, Types.I32))
|
|
||||||
| None ->
|
|
||||||
let i32 e = widen loc (Types.Int Types.I32) e in
|
|
||||||
mk loc (Types.Int Types.I32) (Tast.Prim (Tast.Sub, [ i32 a; i32 b ])))
|
|
||||||
| Types.Char, Types.Int _ | Types.Int _, Types.Char
|
|
||||||
when not (String.equal name "-" && b.Tast.ty = Types.Char) ->
|
|
||||||
(match fold apply with
|
|
||||||
| Some n ->
|
|
||||||
if Int64.compare n 0L >= 0 && Int64.compare n 0x10ffffL <= 0
|
|
||||||
&& not (Int64.compare n 0xd800L >= 0 && Int64.compare n 0xdfffL <= 0)
|
&& not (Int64.compare n 0xd800L >= 0 && Int64.compare n 0xdfffL <= 0)
|
||||||
then mk loc Types.Char (Tast.Int (n, Types.U32))
|
in
|
||||||
else
|
let not_scalar n =
|
||||||
Loc.failk "check/char-range" loc
|
Loc.failk "check/char-range" loc
|
||||||
"this is %Ld, which is not a Unicode scalar value, so it is not a \
|
"this is %s, which is not a Unicode scalar value, so it is not a \
|
||||||
char. A char is a code point from 0 to 0x10FFFF, outside 0xD800 \
|
char. A char is a code point from 0 to 0x10FFFF, outside 0xD800 \
|
||||||
to 0xDFFF" n
|
to 0xDFFF" n
|
||||||
| None ->
|
in
|
||||||
|
let kind = match want with Some (Types.Int k) -> k | _ -> Types.I32 in
|
||||||
|
match a.Tast.ty, b.Tast.ty with
|
||||||
|
| Types.Char, Types.Char when String.equal name "-" ->
|
||||||
|
let t = Types.Int kind in
|
||||||
|
(match const a, const b with
|
||||||
|
| Some x, Some y -> int_literal loc ~want:(Some t) (Int64.sub x y)
|
||||||
|
| _ -> mk loc t (Tast.Prim (Tast.Sub, [ widen loc t a; widen loc t b ])))
|
||||||
|
| Types.Char, Types.Int k | Types.Int k, Types.Char
|
||||||
|
when not (String.equal name "-" && b.Tast.ty = Types.Char) ->
|
||||||
|
let c, n = if a.Tast.ty = Types.Char then a, b else b, a in
|
||||||
|
(match const c, const n with
|
||||||
|
(* A u64 past the largest i64 is held as a negative i64: no char. *)
|
||||||
|
| Some _, Some v when k = Types.U64 && Int64.compare v 0L < 0 ->
|
||||||
|
not_scalar (Printf.sprintf "past 0x10FFFF")
|
||||||
|
| Some x, Some y ->
|
||||||
|
let r = if op = Tast.Add then Int64.add x y else Int64.sub x y in
|
||||||
|
if scalar r then mk loc Types.Char (Tast.Int (r, Types.U32))
|
||||||
|
else not_scalar (Int64.to_string r)
|
||||||
|
(* A u64 is taken as itself, so no large one wraps round to a char. *)
|
||||||
|
| _ when k = Types.U64 ->
|
||||||
|
rt loc Types.Char "flan_char_step_u64"
|
||||||
|
[ widen loc (Types.Int Types.I32) c; n;
|
||||||
|
mk loc (Types.Int Types.I32)
|
||||||
|
(Tast.Int ((if op = Tast.Sub then 1L else 0L), Types.I32));
|
||||||
|
here loc ]
|
||||||
|
| _ ->
|
||||||
let sum = mk loc dyn_i64 (Tast.Prim (op, [ i64 a; i64 b ])) in
|
let sum = mk loc dyn_i64 (Tast.Prim (op, [ i64 a; i64 b ])) in
|
||||||
rt loc Types.Char "flan_char_of" [ sum; here loc ])
|
rt loc Types.Char "flan_char_of" [ sum; here loc ])
|
||||||
| _ ->
|
| _ ->
|
||||||
char_arith (if a.Tast.ty = Types.Char then a.Tast.loc else b.Tast.loc) name;
|
char_arith (if a.Tast.ty = Types.Char then a.Tast.loc else b.Tast.loc) name;
|
||||||
assert false
|
assert false
|
||||||
|
|
||||||
|
(* Whether [e] may be a char, read off its form without checking it: a name
|
||||||
|
bound to one, a (char n), a function returning one, or [+]/[-] over any
|
||||||
|
of those. What decides whether a [+] or [-] is checked as char
|
||||||
|
arithmetic before the want of its site reaches its operands. A char
|
||||||
|
literal is not on the list: beside a number it is that number. *)
|
||||||
|
and maybe_char ctx (e : Ast.expr) =
|
||||||
|
match e.Ast.e with
|
||||||
|
| Ast.Var n ->
|
||||||
|
(match lookup ctx n with
|
||||||
|
| Some b -> b.bty = Types.Char
|
||||||
|
| None ->
|
||||||
|
match peek_outer ctx n with
|
||||||
|
| Some b -> b.bty = Types.Char
|
||||||
|
| None ->
|
||||||
|
(match Hashtbl.find_opt ctx.env.globals n with
|
||||||
|
| Some (t, _) -> t = Types.Char
|
||||||
|
| None -> false))
|
||||||
|
| Ast.Call ({ Ast.e = Ast.Var "char"; _ }, [ _ ]) -> true
|
||||||
|
| Ast.Call ({ Ast.e = Ast.Var ("+" | "-"); _ }, args) ->
|
||||||
|
List.exists (maybe_char ctx) args
|
||||||
|
| Ast.Call ({ Ast.e = Ast.Var f; _ }, _) ->
|
||||||
|
(match Hashtbl.find_opt ctx.env.fns f with
|
||||||
|
| Some (_, r) -> r = Types.Char
|
||||||
|
| None -> false)
|
||||||
|
| _ -> false
|
||||||
|
|
||||||
(* ── A conversion whose operand is a type variable ─────────────────────
|
(* ── A conversion whose operand is a type variable ─────────────────────
|
||||||
[(i32 x)] where [x] is a [$t]. The concrete question — is this a number —
|
[(i32 x)] where [x] is a [$t]. The concrete question — is this a number —
|
||||||
has no answer during the abstract pass, and asking it anyway is what
|
has no answer during the abstract pass, and asking it anyway is what
|
||||||
@ -11347,30 +11386,61 @@ and fold_left_prim ctx ~want loc name p ~needs ok what args =
|
|||||||
match args with x :: y :: rest -> x, y, rest | _ -> assert false
|
match args with x :: y :: rest -> x, y, rest | _ -> assert false
|
||||||
in
|
in
|
||||||
let charish = String.equal name "+" || String.equal name "-" in
|
let charish = String.equal name "+" || String.equal name "-" in
|
||||||
|
let nwant = numeric_want want in
|
||||||
|
(* A char literal is a char here, not the number, when no number is
|
||||||
|
wanted; one that may be a char by its form keeps the want off the pair,
|
||||||
|
which a char minus a char answers at the want's width itself. *)
|
||||||
|
let int_lit (e : Ast.expr) = match e.Ast.e with Ast.Int _ -> true | _ -> false in
|
||||||
|
(* A char literal later in the chain is a char only while everything
|
||||||
|
before it is an integer literal too; beside a typed number it is that
|
||||||
|
number, as in (+ b c \0) over bytes. *)
|
||||||
|
let untyped = ref (int_lit x && int_lit y) in
|
||||||
|
let char_lit (e : Ast.expr) =
|
||||||
|
match e.Ast.e with Ast.Byte _ -> nwant = None && !untyped | _ -> false
|
||||||
|
in
|
||||||
let a, b =
|
let a, b =
|
||||||
try
|
try
|
||||||
(* An integer literal then a char literal, where no number is wanted:
|
(match x.Ast.e, y.Ast.e with
|
||||||
char arithmetic, which the join would read the other way round. *)
|
| Ast.Int _, Ast.Byte _ when charish && nwant = None ->
|
||||||
(match x.Ast.e, y.Ast.e, numeric_want want with
|
|
||||||
| Ast.Int _, Ast.Byte _, None when charish ->
|
|
||||||
raise_notrace (Char_pair (check ctx x, check ctx y))
|
raise_notrace (Char_pair (check ctx x, check ctx y))
|
||||||
| _ -> ());
|
| _ -> ());
|
||||||
|
let pwant = if charish && (maybe_char ctx x || maybe_char ctx y) then None else nwant in
|
||||||
char_operands ctx ~charish name [ x; y ] (fun () ->
|
char_operands ctx ~charish name [ x; y ] (fun () ->
|
||||||
binary ctx ~dyn_ok:true ~char_ok:charish name loc
|
binary ctx ~dyn_ok:true ~char_ok:charish name loc ~want:pwant [ x; y ])
|
||||||
~want:(numeric_want want) [ x; y ])
|
|
||||||
with Char_pair (a, b) -> a, b
|
with Char_pair (a, b) -> a, b
|
||||||
in
|
in
|
||||||
(* One dyn operand makes the whole fold dyn, whichever side it is on. The
|
(* One dyn operand makes the whole fold dyn, whichever side it is on. The
|
||||||
typed side is boxed by [dyn_fold]; a literal was already built at dyn by
|
typed side is boxed by [dyn_fold]; a literal was already built at dyn by
|
||||||
[binary], so [(+ x 1)] over a dyn x folds an i64 one. *)
|
[binary], so [(+ x 1)] over a dyn x folds an i64 one. *)
|
||||||
if charish && (a.Tast.ty = Types.Char || b.Tast.ty = Types.Char)
|
let is_char (e : Tast.expr) = e.Tast.ty = Types.Char in
|
||||||
&& a.Tast.ty <> Types.Dyn && b.Tast.ty <> Types.Dyn then
|
if charish && a.Tast.ty <> Types.Dyn && b.Tast.ty <> Types.Dyn
|
||||||
let acc =
|
&& (is_char a || is_char b
|
||||||
List.fold_left
|
|| List.exists (maybe_char ctx) rest
|
||||||
(fun acc arg -> char_step ctx loc name acc (check ctx arg))
|
|| (let rec any = function
|
||||||
(char_step ctx loc name a b) rest
|
| [] -> false
|
||||||
|
| r :: tl -> char_lit r || (untyped := !untyped && int_lit r; any tl)
|
||||||
in
|
in
|
||||||
expect ctx loc ~want acc
|
let was = !untyped in
|
||||||
|
let r = any rest in
|
||||||
|
untyped := was; r)) then
|
||||||
|
(* Left to right, each step char arithmetic when a char is in it and the
|
||||||
|
ordinary join when none is: (- \z \a 1) is 25 - 1, and (+ 1 2 \a) is
|
||||||
|
3 + \a. *)
|
||||||
|
let step acc (arg : Ast.expr) =
|
||||||
|
let lit = char_lit arg in
|
||||||
|
untyped := !untyped && int_lit arg;
|
||||||
|
if is_char acc || maybe_char ctx arg || lit then
|
||||||
|
let v = check ctx arg in
|
||||||
|
if is_char acc || is_char v then char_step ~want:nwant loc name acc v
|
||||||
|
else mk loc acc.Tast.ty (Tast.Prim (p, [ acc; expect ctx arg.Ast.loc ~want:(Some acc.Tast.ty) v ]))
|
||||||
|
else
|
||||||
|
mk loc acc.Tast.ty (Tast.Prim (p, [ acc; check ctx ~want:acc.Tast.ty arg ]))
|
||||||
|
in
|
||||||
|
let first =
|
||||||
|
if is_char a || is_char b then char_step ~want:nwant loc name a b
|
||||||
|
else mk loc a.Tast.ty (Tast.Prim (p, [ a; b ]))
|
||||||
|
in
|
||||||
|
expect ctx loc ~want (List.fold_left step first rest)
|
||||||
else if a.Tast.ty = Types.Dyn || b.Tast.ty = Types.Dyn then
|
else if a.Tast.ty = Types.Dyn || b.Tast.ty = Types.Dyn then
|
||||||
dyn_fold ctx ~want loc name [ a; b ] rest
|
dyn_fold ctx ~want loc name [ a; b ] rest
|
||||||
else begin
|
else begin
|
||||||
@ -12853,6 +12923,12 @@ and named_call ?(qualified = false) ctx ~want loc name args =
|
|||||||
| Ast.Float v, _ -> check ctx ?want { Ast.e = Ast.Float (-.v); loc }
|
| Ast.Float v, _ -> check ctx ?want { Ast.e = Ast.Float (-.v); loc }
|
||||||
| _ ->
|
| _ ->
|
||||||
let v = check ctx ?want:(numeric_want want) x in
|
let v = check ctx ?want:(numeric_want want) x in
|
||||||
|
if v.Tast.ty = Types.Char then
|
||||||
|
Loc.failk "check/char-arithmetic" x.Ast.loc
|
||||||
|
"- does not negate a char. Take its code point with %s, and make a \
|
||||||
|
char of one with %s"
|
||||||
|
(if fln_source loc then "i32(c)" else "(i32 c)")
|
||||||
|
(if fln_source loc then "char(n)" else "(char n)");
|
||||||
if v.Tast.ty = Types.Dyn then
|
if v.Tast.ty = Types.Dyn then
|
||||||
expect ctx loc ~want (rt loc Types.Dyn "flan_dyn_neg" [ v; here loc ])
|
expect ctx loc ~want (rt loc Types.Dyn "flan_dyn_neg" [ v; here loc ])
|
||||||
else begin
|
else begin
|
||||||
|
|||||||
@ -5148,6 +5148,7 @@ declare i64 @flan_dyn_int_of(i64)
|
|||||||
declare i32 @flan_dyn_need_char(i64, ptr, i64)
|
declare i32 @flan_dyn_need_char(i64, ptr, i64)
|
||||||
declare i32 @flan_char_of(i64, ptr, i64)
|
declare i32 @flan_char_of(i64, ptr, i64)
|
||||||
declare i32 @flan_char_of_u64(i64, ptr, i64)
|
declare i32 @flan_char_of_u64(i64, ptr, i64)
|
||||||
|
declare i32 @flan_char_step_u64(i32, i64, i32, ptr, i64)
|
||||||
; A numeric cast written on a dyn answers which numeric tag the box holds;
|
; 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
|
; 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
|
; the ordinary cast. The two slices are the site's location and the target's
|
||||||
|
|||||||
@ -2994,6 +2994,21 @@ uint32_t flan_char_of_u64(uint64_t n, const uint8_t *loc, int64_t loclen) {
|
|||||||
rt_trap((const uint8_t *)"InvalidChar", 11);
|
rt_trap((const uint8_t *)"InvalidChar", 11);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* A char plus or minus a u64 ([sub] 1 for minus), taken unsigned so no u64
|
||||||
|
* past the largest i64 wraps round to a char. */
|
||||||
|
uint32_t flan_char_step_u64(int32_t cp, uint64_t n, int32_t sub,
|
||||||
|
const uint8_t *loc, int64_t loclen) {
|
||||||
|
uint64_t r;
|
||||||
|
if (sub ? n > (uint64_t)cp : n > 0x10ffff) {
|
||||||
|
flan_say(loc, loclen, "%lld %s %llu is %s, so it is not a char",
|
||||||
|
(long long)cp, sub ? "minus" : "plus", (unsigned long long)n,
|
||||||
|
sub ? "below zero" : "past 0x10FFFF");
|
||||||
|
rt_trap((const uint8_t *)"InvalidChar", 11);
|
||||||
|
}
|
||||||
|
r = sub ? (uint64_t)cp - n : (uint64_t)cp + n;
|
||||||
|
return flan_char_of_u64(r, loc, loclen);
|
||||||
|
}
|
||||||
|
|
||||||
/* [n] elements from [src] onto the end of a Vec, growing it once. [src] may
|
/* [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
|
* 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
|
* found before the grow and read again after it: the grow frees the old
|
||||||
|
|||||||
@ -2,12 +2,21 @@
|
|||||||
;;;; integer is a char, an integer plus a char is too, and a char minus a char
|
;;;; integer is a char, an integer plus a char is too, and a char minus a char
|
||||||
;;;; is the distance, an i32. Dyn chars do the same. Byte code beside it is
|
;;;; is the distance, an i32. Dyn chars do the same. Byte code beside it is
|
||||||
;;;; unchanged. With "past" a char past U+10FFFF traps, with "surrogate" one
|
;;;; unchanged. With "past" a char past U+10FFFF traps, with "surrogate" one
|
||||||
;;;; landing on a surrogate does, and with "dyn" a dyn char below zero does.
|
;;;; landing on a surrogate does, with "dyn" a dyn char below zero does, and
|
||||||
|
;;;; with "u64" a char plus the largest u64 does rather than wrapping round.
|
||||||
|
|
||||||
(defn show [x] () (println x))
|
(defn show [x] () (println x))
|
||||||
(defn add [a b] dyn (+ a b))
|
(defn add [a b] dyn (+ a b))
|
||||||
(defn sub [a b] dyn (- a b))
|
(defn sub [a b] dyn (- a b))
|
||||||
(defn upper [c char] char (if (and (>= c \a) (<= c \z)) (- c 32) c))
|
(defn upper [c char] char (if (and (>= c \a) (<= c \z)) (- c 32) c))
|
||||||
|
;; A char minus a char where an integer is wanted is that integer.
|
||||||
|
(defn digit [c char] i32 (- c \0))
|
||||||
|
(defn gap [a char b char] u8 (- a b))
|
||||||
|
(defn gaps [a char b char] i32
|
||||||
|
(let [v (vec-new i32)]
|
||||||
|
(push v (- a b))
|
||||||
|
(+ (- a b) (at v 0) 1)))
|
||||||
|
(defn plus-u64 [c char n u64] char (+ c n))
|
||||||
|
|
||||||
(defn main [args [str]] i32
|
(defn main [args [str]] i32
|
||||||
;; The fork case with arithmetic: a let-bound char stays a char.
|
;; The fork case with arithmetic: a let-bound char stays a char.
|
||||||
@ -19,6 +28,9 @@
|
|||||||
n 3]
|
n 3]
|
||||||
(println (+ c n) (+ n c) (- c n) (- c \a) (- \a \A) (+ \a 1 1)))
|
(println (+ c n) (+ n c) (- c n) (- c \a) (- \a \A) (+ \a 1 1)))
|
||||||
(println (upper \q) (upper \Q) (upper \é))
|
(println (upper \q) (upper \Q) (upper \é))
|
||||||
|
(println (digit \7) (gap \c \a) (gaps \c \a) (plus-u64 \a (u64 2)))
|
||||||
|
;; Longer chains fold left, each step by its own operands.
|
||||||
|
(println (- \z \a 1) (+ 1 2 \a) (- \z 1 \a))
|
||||||
;; += and -= on a char local.
|
;; += and -= on a char local.
|
||||||
(let [c \a]
|
(let [c \a]
|
||||||
(set c (+ c 2))
|
(set c (+ c 2))
|
||||||
@ -38,6 +50,8 @@
|
|||||||
(println (+ (char 0x10FFFF) (- k 1)))
|
(println (+ (char 0x10FFFF) (- k 1)))
|
||||||
(= (at args 1) "surrogate")
|
(= (at args 1) "surrogate")
|
||||||
(println (+ (char 0xD7FF) (- k 1)))
|
(println (+ (char 0xD7FF) (- k 1)))
|
||||||
|
(= (at args 1) "u64")
|
||||||
|
(println (plus-u64 \a (- (u64 0) (u64 (- k 1)))))
|
||||||
:else
|
:else
|
||||||
(println (sub \a (* k 100))))))
|
(println (sub \a (* k 100))))))
|
||||||
0)
|
0)
|
||||||
|
|||||||
@ -5603,7 +5603,7 @@ level "1"
|
|||||||
with arithmetic, and a byte beside it; then a char past U+10FFFF, one
|
with arithmetic, and a byte beside it; then a char past U+10FFFF, one
|
||||||
on a surrogate, and a dyn one below zero, each trapping at its form. *)
|
on a surrogate, and a dyn one below zero, each trapping at its form. *)
|
||||||
let char_arith_out =
|
let char_arith_out =
|
||||||
"a\nb\ng g a 3 32 c\nQ Q é\nb\nb b y 32 z\n97 true\n"
|
"a\nb\ng g a 3 32 c\nQ Q é\n7 2 5 c\n24 d 24\nb\nb b y 32 z\n97 true\n"
|
||||||
in
|
in
|
||||||
outputs "char: arithmetic" "programs/char-arith.flan" char_arith_out;
|
outputs "char: arithmetic" "programs/char-arith.flan" char_arith_out;
|
||||||
outputs ~opt:"-O0" "char: arithmetic, -O0" "programs/char-arith.flan"
|
outputs ~opt:"-O0" "char: arithmetic, -O0" "programs/char-arith.flan"
|
||||||
@ -5623,11 +5623,14 @@ level "1"
|
|||||||
%d)\n wanted: %S (exit 134)\n"
|
%d)\n wanted: %S (exit 134)\n"
|
||||||
arg (if x86 then ", --x86" else "") text code want
|
arg (if x86 then ", --x86" else "") text code want
|
||||||
end)
|
end)
|
||||||
[ ("past", "programs/char-arith.flan:38:18: 1114112 is not a \
|
[ ("past", "programs/char-arith.flan:50:18: 1114112 is not a \
|
||||||
Unicode scalar value, so it is not a char");
|
Unicode scalar value, so it is not a char");
|
||||||
("surrogate", "programs/char-arith.flan:40:18: 55296 is not a \
|
("surrogate", "programs/char-arith.flan:52:18: 55296 is not a \
|
||||||
Unicode scalar value, so it is not a char");
|
Unicode scalar value, so it is not a char");
|
||||||
("dyn", "programs/char-arith.flan:9:21: dyn -: -103 is not a \
|
("u64", "programs/char-arith.flan:19:36: 97 plus \
|
||||||
|
18446744073709551615 is past 0x10FFFF, so it is not a \
|
||||||
|
char");
|
||||||
|
("dyn", "programs/char-arith.flan:10:21: dyn -: -103 is not a \
|
||||||
Unicode scalar value, so it is not a char") ])
|
Unicode scalar value, so it is not a char") ])
|
||||||
[ false; true ];
|
[ false; true ];
|
||||||
(* A String, and a str made from one, cross into dyn as text measured
|
(* A String, and a str made from one, cross into dyn as text measured
|
||||||
|
|||||||
@ -3251,7 +3251,13 @@ let () =
|
|||||||
"(defn f [] char (- \\a 200))"
|
"(defn f [] char (- \\a 200))"
|
||||||
~needle:"this is -103, which is not a Unicode scalar value";
|
~needle:"this is -103, which is not a Unicode scalar value";
|
||||||
rejects_check "nor negated"
|
rejects_check "nor negated"
|
||||||
"(defn f [c char] char (- c))" ~needle:"- takes an integer or a char";
|
"(defn f [c char] char (- c))" ~needle:"- does not negate a char";
|
||||||
|
accepts "a char minus a char where an i32 is wanted"
|
||||||
|
"(defn f [c char] i32 (- c \\0))";
|
||||||
|
accepts "a char minus a char where a u8 is wanted"
|
||||||
|
"(defn f [a char b char] u8 (- a b))";
|
||||||
|
accepts "a char distance in arithmetic"
|
||||||
|
"(defn f [a char b char] i32 (+ (- a b) 1))";
|
||||||
rejects_check "nor bitwise"
|
rejects_check "nor bitwise"
|
||||||
"(defn f [c char] char (bit-and c c))"
|
"(defn f [c char] char (bit-and c c))"
|
||||||
~needle:"bit-and does no arithmetic on a char";
|
~needle:"bit-and does no arithmetic on a char";
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user