Skip to content

Commit 4b75d31

Browse files
committed
make typechecker account for res.dictPattern attribute to infer record pattern as dict pattern match when the type is not already known
1 parent f91973f commit 4b75d31

File tree

6 files changed

+54
-2
lines changed

6 files changed

+54
-2
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
2+
We've found a bug for you!
3+
/.../fixtures/dict_pattern_inference.res:3:36-42
4+
5+
1 │ let foo = dict =>
6+
2 │ switch dict {
7+
3 │ | @res.dictPattern {one: 1, two: "hello"} => Js.log("one")
8+
4 │ | _ => Js.log("not one")
9+
5 │ }
10+
11+
This pattern matches values of type string
12+
but a pattern was expected which matches values of type int
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
let foo = dict =>
2+
switch dict {
3+
| @res.dictPattern {one: 1, two: "hello"} => Js.log("one")
4+
| _ => Js.log("not one")
5+
}

jscomp/ml/dicts.ml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
let has_dict_pattern_attribute attrs =
2+
attrs
3+
|> List.find_opt (fun (({txt}, _) : Parsetree.attribute) ->
4+
txt = "res.dictPattern")
5+
|> Option.is_some

jscomp/ml/typecore.ml

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1370,12 +1370,20 @@ and type_pat_aux ~constrs ~labels ~no_existentials ~mode ~explode ~env
13701370
| _ -> k None
13711371
end
13721372
| Ppat_record(lid_sp_list, closed) ->
1373-
let opath, record_ty =
1373+
let has_dict_pattern_attr = Dicts.has_dict_pattern_attribute sp.ppat_attributes in
1374+
let opath, record_ty = (
1375+
match (has_dict_pattern_attr, expected_ty.desc) with
1376+
| (true, Tvar _) ->
1377+
(* When this is a dict pattern and we don't have an actual expected type yet,
1378+
infer the type as a dict with a new type variable. This let us hook into the
1379+
existing inference mechanism for records in dict pattern matching too. *)
1380+
(Some (Predef.path_dict, Predef.path_dict), newgenty (Tconstr (Predef.path_dict, [newvar ()], ref Mnil)))
1381+
| _ ->
13741382
try
13751383
let (p0, p, _, _) = extract_concrete_record !env expected_ty in
13761384
Some (p0, p), expected_ty
13771385
with Not_found -> None, newvar ()
1378-
in
1386+
) in
13791387
let get_jsx_component_error_info = get_jsx_component_error_info ~extract_concrete_typedecl opath !env record_ty in
13801388
let process_optional_label (ld, pat) =
13811389
let exp_optional_attr = check_optional_attr !env ld pat.ppat_attributes pat.ppat_loc in

jscomp/test/DictTests.js

Lines changed: 14 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

jscomp/test/DictTests.res

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,11 @@ let intDict = dict{
1414
"two": 2,
1515
"three": three,
1616
}
17+
18+
module PatternMatching = {
19+
let foo = dict =>
20+
switch dict {
21+
| @res.dictPattern {one: 1} => Js.log("one")
22+
| _ => Js.log("not one")
23+
}
24+
}

0 commit comments

Comments
 (0)