Skip to content

Commit 1998c99

Browse files
committed
poc of type spreads of regular variants in patterns
1 parent c15df0c commit 1998c99

9 files changed

+197
-4
lines changed

jscomp/ml/typecore.ml

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -595,6 +595,62 @@ let build_or_pat env loc lid =
595595
pat pats in
596596
(path, rp { r with pat_loc = loc },ty)
597597

598+
let build_or_pat_for_variant_spread env loc lid expected_ty =
599+
let path, decl = Typetexp.find_type env lid.loc lid.txt in
600+
match decl with
601+
| {type_kind = Type_variant constructors} -> (
602+
(* TODO: Probably problematic that we don't account for type params here? *)
603+
let ty = newty (Tconstr (path, [], ref Mnil)) in
604+
let gloc = {loc with Location.loc_ghost = true} in
605+
let pats =
606+
constructors
607+
|> List.map
608+
(fun (c : Types.constructor_declaration) : Typedtree.pattern ->
609+
let lid = Longident.Lident (Ident.name c.cd_id) in
610+
{
611+
pat_desc =
612+
Tpat_construct
613+
( {loc = Location.none; txt = lid},
614+
Env.lookup_constructor ~loc:c.cd_loc lid env,
615+
match c.cd_args with
616+
| Cstr_tuple [] -> []
617+
| _ ->
618+
[
619+
{
620+
pat_desc = Tpat_any;
621+
pat_loc = Location.none;
622+
pat_env = env;
623+
pat_type = expected_ty;
624+
pat_extra = [];
625+
pat_attributes = [];
626+
};
627+
] );
628+
pat_loc = Location.none;
629+
pat_extra = [];
630+
pat_type = expected_ty;
631+
pat_env = env;
632+
pat_attributes = [];
633+
})
634+
in
635+
match pats with
636+
| [] -> raise (Error (lid.loc, env, Not_a_variant_type lid.txt))
637+
| pat :: pats ->
638+
let r =
639+
List.fold_left
640+
(fun pat pat0 ->
641+
{
642+
Typedtree.pat_desc = Tpat_or (pat0, pat, None);
643+
pat_extra = [];
644+
pat_loc = gloc;
645+
pat_env = env;
646+
pat_type = expected_ty;
647+
pat_attributes = [];
648+
})
649+
pat pats
650+
in
651+
(path, rp {r with pat_loc = loc}, ty))
652+
| _ -> raise (Error (lid.loc, env, Not_a_variant_type lid.txt))
653+
598654
(* Type paths *)
599655

600656
let rec expand_path env p =
@@ -1121,6 +1177,18 @@ and type_pat_aux ~constrs ~labels ~no_existentials ~mode ~explode ~env
11211177
}
11221178
| _ -> assert false
11231179
end
1180+
| Ppat_alias({ppat_desc=Ppat_type lid; ppat_attributes}, name) when Variant_coercion.has_res_pat_variant_spread_attribute ppat_attributes ->
1181+
let (_, p, ty) = build_or_pat_for_variant_spread !env loc lid expected_ty in
1182+
Ctype.subtype !env ty expected_ty ();
1183+
assert (constrs = None);
1184+
1185+
let id = enter_variable ~is_as_variable:true loc name ty in
1186+
rp k {
1187+
pat_desc = Tpat_alias(p, id, name);
1188+
pat_loc = loc; pat_extra=[];
1189+
pat_type = expected_ty;
1190+
pat_attributes = sp.ppat_attributes;
1191+
pat_env = !env }
11241192
| Ppat_alias(sq, name) ->
11251193
assert (constrs = None);
11261194
type_pat sq expected_ty (fun q ->
@@ -1445,6 +1513,12 @@ and type_pat_aux ~constrs ~labels ~no_existentials ~mode ~explode ~env
14451513
| _ -> {p with pat_type = ty;
14461514
pat_extra = extra :: p.pat_extra}
14471515
in k p)
1516+
| Ppat_type lid when Variant_coercion.has_res_pat_variant_spread_attribute sp.ppat_attributes ->
1517+
let (path, p, ty) = build_or_pat_for_variant_spread !env loc lid expected_ty in
1518+
Ctype.subtype !env ty expected_ty ();
1519+
assert (constrs = None);
1520+
k { p with pat_extra =
1521+
(Tpat_type (path, lid), loc, sp.ppat_attributes) :: p.pat_extra }
14481522
| Ppat_type lid ->
14491523
let (path, p,ty) = build_or_pat !env loc lid in
14501524
unify_pat_types loc !env ty expected_ty;

jscomp/ml/variant_coercion.ml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,4 +202,11 @@ let can_coerce_polyvariant_to_variant ~row_fields ~variant_constructors ~type_at
202202
let type_is_variant (typ: (Path.t * Path.t * Types.type_declaration) option) =
203203
match typ with
204204
| Some (_, _, {type_kind = Type_variant _; _}) -> true
205-
| _ -> false
205+
| _ -> false
206+
let has_res_pat_variant_spread_attribute attrs =
207+
attrs
208+
|> List.find_opt (fun (({txt}, _) : Parsetree.attribute) ->
209+
txt = "res.patVariantSpread")
210+
|> Option.is_some
211+
212+

jscomp/syntax/src/res_core.ml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,8 @@ let suppress_fragile_match_warning_attr =
175175
] )
176176
let make_braces_attr loc = (Location.mkloc "res.braces" loc, Parsetree.PStr [])
177177
let template_literal_attr = (Location.mknoloc "res.template", Parsetree.PStr [])
178+
let makePatVariantSpreadAttr =
179+
(Location.mknoloc "res.patVariantSpread", Parsetree.PStr [])
178180

179181
let tagged_template_literal_attr =
180182
(Location.mknoloc "res.taggedTemplate", Parsetree.PStr [])
@@ -1077,6 +1079,11 @@ let rec parse_pattern ?(alias = true) ?(or_ = true) p =
10771079
match p.Parser.token with
10781080
| Lparen -> parse_constructor_pattern_args p constr start_pos attrs
10791081
| _ -> Ast_helper.Pat.construct ~loc:constr.loc ~attrs constr None)
1082+
| DotDotDot ->
1083+
Parser.next p;
1084+
let ident = parse_value_path p in
1085+
let loc = mk_loc start_pos ident.loc.loc_end in
1086+
Ast_helper.Pat.type_ ~loc ~attrs:(makePatVariantSpreadAttr :: attrs) ident
10801087
| Hash -> (
10811088
Parser.next p;
10821089
if p.Parser.token == DotDotDot then (

jscomp/syntax/src/res_parsetree_viewer.ml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,13 @@ let has_await_attribute attrs =
9797
| _ -> false)
9898
attrs
9999

100+
let hasResPatVariantSpreadAttribute attrs =
101+
List.exists
102+
(function
103+
| {Location.txt = "res.patVariantSpread"}, _ -> true
104+
| _ -> false)
105+
attrs
106+
100107
let collect_array_expressions expr =
101108
match expr.pexp_desc with
102109
| Pexp_array exprs -> (exprs, None)
@@ -219,7 +226,8 @@ let filter_parsing_attrs attrs =
219226
Location.txt =
220227
( "res.arity" | "res.braces" | "ns.braces" | "res.iflet"
221228
| "res.namedArgLoc" | "res.optional" | "res.ternary" | "res.async"
222-
| "res.await" | "res.template" | "res.taggedTemplate" );
229+
| "res.await" | "res.template" | "res.taggedTemplate"
230+
| "res.patVariantSpread" );
223231
},
224232
_ ) ->
225233
false

jscomp/syntax/src/res_parsetree_viewer.mli

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ val process_function_attributes :
2626
Parsetree.attributes -> function_attributes_info
2727

2828
val has_await_attribute : Parsetree.attributes -> bool
29+
val hasResPatVariantSpreadAttribute : Parsetree.attributes -> bool
2930

3031
type if_condition_kind =
3132
| If of Parsetree.expression

jscomp/syntax/src/res_printer.ml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2405,7 +2405,12 @@ and print_pattern ~state (p : Parsetree.pattern) cmt_tbl =
24052405
in
24062406
Doc.group (Doc.concat [variant_name; args_doc])
24072407
| Ppat_type ident ->
2408-
Doc.concat [Doc.text "#..."; print_ident_path ident cmt_tbl]
2408+
let prefix =
2409+
if ParsetreeViewer.hasResPatVariantSpreadAttribute p.ppat_attributes
2410+
then ""
2411+
else "#"
2412+
in
2413+
Doc.concat [Doc.text (prefix ^ "..."); print_ident_path ident cmt_tbl]
24092414
| Ppat_record (rows, open_flag) ->
24102415
Doc.group
24112416
(Doc.concat

jscomp/test/VariantPatternMatchingSpreads.js

Lines changed: 58 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
type a = One | Two | Three
2+
type a1 = One
3+
type b = | ...a | Four | Five
4+
5+
let doWithA = (a: a) => {
6+
switch a {
7+
| One => Js.log("aaa")
8+
| Two => Js.log("twwwoooo")
9+
| Three => Js.log("threeeee")
10+
}
11+
}
12+
13+
let doWithB = (b: b) => {
14+
switch b {
15+
| One => Js.log("aaa")
16+
| _ => Js.log("twwwoooo")
17+
}
18+
}
19+
20+
let lookup = (b: b) =>
21+
switch b {
22+
| ...a as a => doWithA(a)
23+
| Four => Js.log("four")
24+
| Five => Js.log("five")
25+
}
26+
27+
let lookup2 = (b: b) =>
28+
switch b {
29+
| ...a => Js.log("spread")
30+
| Four => Js.log("four")
31+
| Five => Js.log("five")
32+
}

jscomp/test/build.ninja

Lines changed: 2 additions & 1 deletion
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)