Skip to content

Commit f23b375

Browse files
committed
poc of type spreads of regular variants in patterns
1 parent 41ec1b0 commit f23b375

File tree

9 files changed

+197
-3
lines changed

9 files changed

+197
-3
lines changed

jscomp/ml/typecore.ml

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

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

596652
let rec expand_path env p =
@@ -1111,6 +1167,18 @@ and type_pat_aux ~constrs ~labels ~no_existentials ~mode ~explode ~env
11111167
}
11121168
| _ -> assert false
11131169
end
1170+
| Ppat_alias({ppat_desc=Ppat_type lid; ppat_attributes}, name) when Variant_coercion.has_res_pat_variant_spread_attribute ppat_attributes ->
1171+
let (_, p, ty) = build_or_pat_for_variant_spread !env loc lid expected_ty in
1172+
Ctype.subtype !env ty expected_ty ();
1173+
assert (constrs = None);
1174+
1175+
let id = enter_variable ~is_as_variable:true loc name ty in
1176+
rp k {
1177+
pat_desc = Tpat_alias(p, id, name);
1178+
pat_loc = loc; pat_extra=[];
1179+
pat_type = expected_ty;
1180+
pat_attributes = sp.ppat_attributes;
1181+
pat_env = !env }
11141182
| Ppat_alias(sq, name) ->
11151183
assert (constrs = None);
11161184
type_pat sq expected_ty (fun q ->
@@ -1435,6 +1503,12 @@ and type_pat_aux ~constrs ~labels ~no_existentials ~mode ~explode ~env
14351503
| _ -> {p with pat_type = ty;
14361504
pat_extra = extra :: p.pat_extra}
14371505
in k p)
1506+
| Ppat_type lid when Variant_coercion.has_res_pat_variant_spread_attribute sp.ppat_attributes ->
1507+
let (path, p, ty) = build_or_pat_for_variant_spread !env loc lid expected_ty in
1508+
Ctype.subtype !env ty expected_ty ();
1509+
assert (constrs = None);
1510+
k { p with pat_extra =
1511+
(Tpat_type (path, lid), loc, sp.ppat_attributes) :: p.pat_extra }
14381512
| Ppat_type lid ->
14391513
let (path, p,ty) = build_or_pat !env loc lid in
14401514
unify_pat_types loc !env ty expected_ty;

jscomp/ml/variant_coercion.ml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,3 +151,11 @@ let variant_configuration_can_be_coerced_raises ~is_spread_context ~left_loc
151151
right_loc;
152152
error = TagName {left_tag; right_tag};
153153
}))
154+
155+
let has_res_pat_variant_spread_attribute attrs =
156+
attrs
157+
|> List.find_opt (fun (({txt}, _) : Parsetree.attribute) ->
158+
txt = "res.patVariantSpread")
159+
|> Option.is_some
160+
161+

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 suppressFragileMatchWarningAttr =
175175
] )
176176
let makeBracesAttr loc = (Location.mkloc "res.braces" loc, Parsetree.PStr [])
177177
let templateLiteralAttr = (Location.mknoloc "res.template", Parsetree.PStr [])
178+
let makePatVariantSpreadAttr =
179+
(Location.mknoloc "res.patVariantSpread", Parsetree.PStr [])
178180

179181
let taggedTemplateLiteralAttr =
180182
(Location.mknoloc "res.taggedTemplate", Parsetree.PStr [])
@@ -1077,6 +1079,11 @@ let rec parsePattern ?(alias = true) ?(or_ = true) p =
10771079
match p.Parser.token with
10781080
| Lparen -> parseConstructorPatternArgs p constr startPos attrs
10791081
| _ -> Ast_helper.Pat.construct ~loc:constr.loc ~attrs constr None)
1082+
| DotDotDot ->
1083+
Parser.next p;
1084+
let ident = parseValuePath p in
1085+
let loc = mkLoc startPos 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: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,13 @@ let hasAwaitAttribute attrs =
104104
| _ -> false)
105105
attrs
106106

107+
let hasResPatVariantSpreadAttribute attrs =
108+
List.exists
109+
(function
110+
| {Location.txt = "res.patVariantSpread"}, _ -> true
111+
| _ -> false)
112+
attrs
113+
107114
let collectArrayExpressions expr =
108115
match expr.pexp_desc with
109116
| Pexp_array exprs -> (exprs, None)
@@ -225,7 +232,7 @@ let filterParsingAttrs attrs =
225232
( "bs" | "res.uapp" | "res.arity" | "res.braces" | "ns.braces"
226233
| "res.iflet" | "res.namedArgLoc" | "res.optional" | "res.ternary"
227234
| "res.async" | "res.await" | "res.template"
228-
| "res.taggedTemplate" );
235+
| "res.taggedTemplate" | "res.patVariantSpread" );
229236
},
230237
_ ) ->
231238
false

jscomp/syntax/src/res_parsetree_viewer.mli

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ type functionAttributesInfo = {
3333
val processFunctionAttributes : Parsetree.attributes -> functionAttributesInfo
3434

3535
val hasAwaitAttribute : Parsetree.attributes -> bool
36+
val hasResPatVariantSpreadAttribute : Parsetree.attributes -> bool
3637

3738
type ifConditionKind =
3839
| If of Parsetree.expression

jscomp/syntax/src/res_printer.ml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2371,7 +2371,12 @@ and printPattern ~state (p : Parsetree.pattern) cmtTbl =
23712371
in
23722372
Doc.group (Doc.concat [variantName; argsDoc])
23732373
| Ppat_type ident ->
2374-
Doc.concat [Doc.text "#..."; printIdentPath ident cmtTbl]
2374+
let prefix =
2375+
if ParsetreeViewer.hasResPatVariantSpreadAttribute p.ppat_attributes
2376+
then ""
2377+
else "#"
2378+
in
2379+
Doc.concat [Doc.text (prefix ^ "..."); printIdentPath ident cmtTbl]
23752380
| Ppat_record (rows, openFlag) ->
23762381
Doc.group
23772382
(Doc.concat

jscomp/test/VariantPatternMatchingSpreads.js

Lines changed: 59 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)