Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions analysis/src/type_utils.ml
Original file line number Diff line number Diff line change
Expand Up @@ -1330,7 +1330,9 @@ let rec find_root_type_id ~full ~env ~state (t : Types.type_expr) =
then
Some
(if debug then Printf.printf "[find_root_type_id] returning builtin\n";
Path.name path)
(* [char] is an alias of [int] *)
if Path.same path Predef.path_char then Path.name Predef.path_int
else Path.name path)
else None)
| _ -> None

Expand Down Expand Up @@ -1396,7 +1398,7 @@ let completion_path_from_maybe_builtin path =
| Some ("list", _) -> Some ["Stdlib"; "List"]
| Some ("result", _) -> Some ["Stdlib"; "Result"]
| Some ("dict", _) -> Some ["Stdlib"; "Dict"]
| Some ("char", _) -> Some ["Stdlib"; "Char"]
| Some ("char", _) -> Some ["Stdlib"; "Int"]
| _ -> (
match path |> Utils.expand_path |> List.rev with
| [main_module; "t"] when String.starts_with ~prefix:"Stdlib_" main_module
Expand Down
1 change: 1 addition & 0 deletions changes/char-int-alias.feature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- `char` is now an alias of `int`, so char literals and ints mix freely, including in `switch` patterns.
39 changes: 17 additions & 22 deletions compiler/ml/matching.ml
Original file line number Diff line number Diff line change
Expand Up @@ -1147,28 +1147,30 @@ let add make_matching_fun division eq_key key patl_action args =
cell.pm.cases <- [patl_action];
(key, cell) :: division

(* Cells whose key is [hashable] are also found by hashing, which must agree
with [eq_key]. This keeps a column of many distinct keys linear. *)
let divide ?(hashable = fun _ -> false) make eq_key get_key get_args ctx pm =
(* Cells whose key has a [hash_key] are also found by hashing on it, which
must agree with [eq_key]. This keeps a column of many distinct keys
linear. *)
let divide ?(hash_key = fun _ -> None) make eq_key get_key get_args ctx pm =
let cells = lazy (Hashtbl.create 16) in
let rec divide_rec = function
| (p :: patl, action) :: rem -> (
let this_match = divide_rec rem in
let key = get_key p in
let patl_action = (get_args p patl, action) in
if not (hashable key) then
match hash_key key with
| None ->
add (make p pm.default ctx) this_match eq_key key patl_action pm.args
else
| Some hkey -> (
let cells = Lazy.force cells in
match Hashtbl.find_opt cells key with
match Hashtbl.find_opt cells hkey with
| Some cell ->
cell.pm.cases <- patl_action :: cell.pm.cases;
this_match
| None ->
let cell = make p pm.default ctx pm.args in
cell.pm.cases <- [patl_action];
Hashtbl.add cells key cell;
(key, cell) :: this_match)
Hashtbl.add cells hkey cell;
(key, cell) :: this_match))
| _ -> []
in

Expand Down Expand Up @@ -1226,9 +1228,10 @@ let make_constant_matching p def ctx = function

let divide_constant ctx m =
divide
~hashable:(function
| Asttypes.Const_int _ | Const_char _ | Const_string _ -> true
| Const_float _ | Const_bigint _ -> false)
~hash_key:(function
| (Asttypes.Const_int _ | Const_char _ | Const_string _) as c ->
Some (Parmatch.const_key c)
| Const_float _ | Const_bigint _ -> None)
make_constant_matching
(fun c d -> const_compare c d = 0)
(get_key_constant "divide")
Expand Down Expand Up @@ -1999,27 +2002,19 @@ let combine_constant loc arg cst partial ctx def
let fail, local_jumps = mk_failaction_neg partial ctx def in
let lambda1 =
match (cst : Asttypes.constant) with
| Const_int _ ->
| Const_int _ | Const_char _ ->
(* [char] is an alias of [int], so a column can mix both. *)
let int_lambda_list =
List.map
(function
| Asttypes.Const_int n, l -> (n, l)
| (Asttypes.Const_int n | Const_char n), l -> (n, l)
| _ -> assert false)
const_lambda_list
in
(* Match intervals must use ReScript's int32 bounds, not the host's.
Otherwise the upper fail interval starts at 2147483648, which wraps
when converted to a Lambda integer constant. *)
call_switcher fail arg int32_low int32_high int_lambda_list
| Const_char _ ->
let int_lambda_list =
List.map
(function
| Asttypes.Const_char c, l -> (c, l)
| _ -> assert false)
const_lambda_list
in
call_switcher fail arg 0 max_int int_lambda_list
| Const_string _ ->
(* Note as the bytecode compiler may resort to dichotomic search,
the clauses of stringswitch are sorted with duplicates removed.
Expand Down
36 changes: 20 additions & 16 deletions compiler/ml/parmatch.ml
Original file line number Diff line number Diff line change
Expand Up @@ -155,8 +155,7 @@ let all_coherent column =
| _ -> false)
| Tpat_constant c1, Tpat_constant c2 -> (
match (c1, c2) with
| Const_char _, Const_char _
| Const_int _, Const_int _
| (Const_char _ | Const_int _), (Const_char _ | Const_int _)
| Const_bigint _, Const_bigint _
| Const_float _, Const_float _
| Const_string _, Const_string _ ->
Expand Down Expand Up @@ -270,8 +269,16 @@ let const_compare x y =
| Const_bigint (s1, b1), Const_bigint (s2, b2) ->
Bigint_utils.compare (s1, b1) (s2, b2)
| Const_string s1, Const_string s2 -> String.compare s1 s2
| (Const_char i1 | Const_int i1), (Const_char i2 | Const_int i2) ->
compare i1 i2
| _, _ -> compare x y

(* [char] is an alias of [int], so a char constant and an int constant with the
same value match the same values. Hash tables of constants use this key. *)
let const_key = function
| Const_char i -> Const_int i
| c -> c

let records_args l1 l2 =
(* Invariant: fields are already sorted by Typecore.type_record_elem_list *)
let rec combine r1 r2 l1 l2 =
Expand Down Expand Up @@ -765,7 +772,7 @@ let filter_all pat0 pss =
let insert q qs =
match q.pat_desc with
| Tpat_constant ((Const_int _ | Const_char _ | Const_string _) as c) ->
find_or_add const_groups c q qs
find_or_add const_groups (const_key c) q qs
| Tpat_variant (l, _, _) -> find_or_add variant_groups l q qs
| _ -> (
match List.find_opt (fun (q0, _) -> simple_match q0 q) !groups with
Expand Down Expand Up @@ -1058,21 +1065,18 @@ let build_other ext env : Typedtree.pattern =
(fun p_res pat ->
make_pat (Tpat_or (pat, p_res, None)) p.pat_type p.pat_env)
pat other_pats)
| (({pat_desc = Tpat_constant (Const_int _)} as p), _) :: _ ->
build_other_constant
(function
| Tpat_constant (Const_int i) -> i
| _ -> assert false)
(function
| i -> Tpat_constant (Const_int i))
0 succ p env
| (({pat_desc = Tpat_constant (Const_char _)} as p), _) :: _ ->
| (({pat_desc = Tpat_constant ((Const_int _ | Const_char _) as c)} as p), _)
:: _ ->
(* Int and char constants can share a column; the example takes the kind
of the first one. *)
build_other_constant
(function
| Tpat_constant (Const_char i) -> i
| Tpat_constant (Const_int i | Const_char i) -> i
| _ -> assert false)
(function
| i -> Tpat_constant (Const_char i))
(fun i ->
match c with
| Const_char _ -> Tpat_constant (Const_char i)
| _ -> Tpat_constant (Const_int i))
0 succ p env
| (({pat_desc = Tpat_constant (Const_bigint _)} as p), _) :: _ ->
build_other_constant
Expand Down Expand Up @@ -1713,7 +1717,7 @@ let get_mins_pats pss =
Tpat_constant ((Const_int _ | Const_char _ | Const_string _) as c);
}
:: _ ->
Some c
Some (const_key c)
| _ -> None
in
(* [select_rec [] pss] of [get_mins le_pats]: drops each row covered by a
Expand Down
3 changes: 3 additions & 0 deletions compiler/ml/parmatch.mli
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ val all_record_args :
(Longident.t loc * label_description * pattern * bool) list
val const_compare : constant -> constant -> int

(* A hash key agreeing with [const_compare]: char constants map to ints. *)
val const_key : constant -> constant

val le_pat : pattern -> pattern -> bool
val le_pats : pattern list -> pattern list -> bool

Expand Down
5 changes: 2 additions & 3 deletions compiler/ml/predef.ml
Original file line number Diff line number Diff line change
Expand Up @@ -434,9 +434,8 @@ let common_initial_env add_type add_extension empty_env =

let build_initial_env add_type add_exception empty_env =
let common = common_initial_env add_type add_exception empty_env in
let decl_type_char =
{decl_abstr with type_manifest = Some (type_int ()); type_private = Private}
in
(* [char] is an alias of [int]: a char literal is its code point. *)
let decl_type_char = {decl_abstr with type_manifest = Some (type_int ())} in
add_type ident_char decl_type_char common

let () =
Expand Down
2 changes: 1 addition & 1 deletion packages/@rescript/runtime/Primitive_string_extern.res
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@

external length: string => int = "%string_length"

@send external getChar: (string, int) => option<char> = "codePointAt"
@send external getChar: (string, int) => option<int> = "codePointAt"
4 changes: 4 additions & 0 deletions tests/analysis_tests/tests/src/CompletionChar.res
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// `char` is an alias of `int`, so a char completes `Int` functions.
let c = 'a'
// let _ = c->toS
// ^com
34 changes: 34 additions & 0 deletions tests/analysis_tests/tests/src/expected/CompletionChar.res.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
Complete src/CompletionChar.res 2:17
pos_cursor:[2:17] pos_no_white:[2:16] Found expr:[2:11->2:17]
Completable: Cpath Value[c]->toS
Package opens Stdlib.place holder Pervasives.JsxModules.place holder
Resolved opens 1 Stdlib
ContextPath Value[c]->toS
ContextPath Value[c]
Path c
Path Stdlib.Int.toS
Path toS
[
{
"deprecated": true,
"detail": "(int, ~radix: int) => string",
"documentation": {
"kind": "markdown",
"value": "Deprecated: Use `toString` instead\n\n\n`toStringWithRadix(n, ~radix)` return a `string` representing the given value.\n`~radix` specifies the radix base to use for the formatted number.\nSee [`Number.toString`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toString)\non MDN.\n\n## Examples\n\n```rescript\nInt.toStringWithRadix(6, ~radix=2) // \"110\"\nInt.toStringWithRadix(373592855, ~radix=16) // \"16449317\"\nInt.toStringWithRadix(123456, ~radix=36) // \"2n9c\"\n```\n\n## Exceptions\n\n`RangeError`: if `radix` is less than 2 or greater than 36.\n"
},
"kind": 12,
"label": "Int.toStringWithRadix",
"tags": [ 1 ]
},
{
"detail": "(int, ~radix: int=?) => string",
"documentation": {
"kind": "markdown",
"value": "\n`toString(n, ~radix=?)` return a `string` representing the given value.\n`~radix` specifies the radix base to use for the formatted number.\nSee [`Number.toString`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toString)\non MDN.\n\n## Examples\n\n```rescript\nInt.toString(1000) // \"1000\"\nInt.toString(-1000) // \"-1000\"\nInt.toString(6, ~radix=2) // \"110\"\nInt.toString(373592855, ~radix=16) // \"16449317\"\nInt.toString(123456, ~radix=36) // \"2n9c\"\n```\n\n## Exceptions\n\n`RangeError`: if `radix` is less than 2 or greater than 36.\n"
},
"kind": 12,
"label": "Int.toString",
"tags": []
}
]

Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@

Warning number 11
/.../fixtures/warning_11_equivalent_char_int_patterns.res:5:5-6

3 ┆ switch x {
4 ┆ | 'a' => 1
5 ┆ | 97 => 2
6 ┆ | _ => 3
7 ┆ }

this match case is unused.
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@

Warning number 8
/.../fixtures/warning_8_char_int_patterns.res:3:3-6:3

1 │ // Char and int patterns share one column when checking exhaustiveness.
2 │ let f = x =>
3 │ switch x {
4 │  | 'a' => 1
5 │  | 98 => 2
6 │  }
7 │

You forgot to handle a possible case here, for example:
| '\x00'
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// `char` is an alias of `int`, so `97` repeats `'a'`.
let f = x =>
switch x {
| 'a' => 1
| 97 => 2
| _ => 3
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Char and int patterns share one column when checking exhaustiveness.
let f = x =>
switch x {
| 'a' => 1
| 98 => 2
}
5 changes: 2 additions & 3 deletions tests/tests/src/adt_optimize_test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,15 @@ function f_0(x) {
}

function f2(x) {
if (x >= 3) {
return "T003";
}
switch (x) {
case 0 :
return "T000";
case 1 :
return "T001";
case 2 :
return "T002";
default:
return "T003";
}
}

Expand Down
Loading
Loading