Skip to content

Commit d634e1e

Browse files
committed
address comments (parsetree0 PPX roundtrips, nested rest, etc)
1 parent 2245765 commit d634e1e

16 files changed

Lines changed: 174 additions & 58 deletions

File tree

compiler/ml/ast_iterator.ml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -398,12 +398,13 @@ module P = struct
398398
iter_loc sub l;
399399
iter_opt (sub.pat sub) p
400400
| Ppat_variant (_l, p) -> iter_opt (sub.pat sub) p
401-
| Ppat_record (lpl, _cf, _rest) ->
401+
| Ppat_record (lpl, _cf, rest) ->
402402
List.iter
403403
(fun {lid; x = pat} ->
404404
iter_loc sub lid;
405405
sub.pat sub pat)
406-
lpl
406+
lpl;
407+
iter_opt (sub.pat sub) rest
407408
| Ppat_array pl -> List.iter (sub.pat sub) pl
408409
| Ppat_or (p1, p2) ->
409410
sub.pat sub p1;

compiler/ml/ast_mapper_from0.ml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -576,7 +576,8 @@ module P = struct
576576
construct ~loc ~attrs (map_loc sub l) (map_opt (sub.pat sub) p)
577577
| Ppat_variant (l, p) -> variant ~loc ~attrs l (map_opt (sub.pat sub) p)
578578
| Ppat_record (lpl, cf) ->
579-
record ~loc ~attrs
579+
let rest, attrs = Parsetree0.get_record_rest_attr attrs in
580+
record ~loc ~attrs ?rest
580581
(Ext_list.map lpl (fun (lid, p) ->
581582
let lid1 = map_loc sub lid in
582583
let p1 = sub.pat sub p in

compiler/ml/ast_mapper_to0.ml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -559,7 +559,13 @@ module P = struct
559559
| Ppat_construct (l, p) ->
560560
construct ~loc ~attrs (map_loc sub l) (map_opt (sub.pat sub) p)
561561
| Ppat_variant (l, p) -> variant ~loc ~attrs l (map_opt (sub.pat sub) p)
562-
| Ppat_record (lpl, cf, _rest) ->
562+
| Ppat_record (lpl, cf, rest) ->
563+
let attrs =
564+
match rest with
565+
| None -> attrs
566+
| Some rest_pat ->
567+
Parsetree0.add_record_rest_attr ~rest:(sub.pat sub rest_pat) attrs
568+
in
563569
record ~loc ~attrs
564570
(Ext_list.map lpl (fun {lid; x = p; opt = optional} ->
565571
let lid1 = map_loc sub lid in

compiler/ml/depend.ml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,8 @@ let rec add_pattern bv pat =
188188
(fun {lid = lbl; x = p} ->
189189
add bv lbl;
190190
add_pattern bv p)
191-
pl
191+
pl;
192+
add_opt add_pattern bv _rest
192193
| Ppat_array pl -> List.iter (add_pattern bv) pl
193194
| Ppat_or (p1, p2) ->
194195
add_pattern bv p1;

compiler/ml/matching.ml

Lines changed: 19 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -543,6 +543,14 @@ let simplify_or p =
543543
in
544544
try simpl_rec p with Var p -> p
545545

546+
let bind_record_rest loc arg rest action =
547+
Llet
548+
( Strict,
549+
Pgenval,
550+
rest.rest_ident,
551+
Lprim (Precord_spread_new rest.excluded_labels, [arg], loc),
552+
action )
553+
546554
let simplify_cases args cls =
547555
match args with
548556
| [] -> assert false
@@ -560,7 +568,12 @@ let simplify_cases args cls =
560568
| Tpat_record (lbls, closed, rest) ->
561569
let all_lbls = all_record_args lbls in
562570
let full_pat =
563-
{pat with pat_desc = Tpat_record (all_lbls, closed, rest)}
571+
{pat with pat_desc = Tpat_record (all_lbls, closed, None)}
572+
in
573+
let action =
574+
match rest with
575+
| None -> action
576+
| Some rest -> bind_record_rest pat.pat_loc arg rest action
564577
in
565578
(full_pat :: patl, action) :: simplify rem
566579
| Tpat_or _ -> (
@@ -617,8 +630,11 @@ let rec extract_vars r p =
617630
| Tpat_var (id, _) -> IdentSet.add id r
618631
| Tpat_alias (p, id, _) -> extract_vars (IdentSet.add id r) p
619632
| Tpat_tuple pats -> List.fold_left extract_vars r pats
620-
| Tpat_record (lpats, _, _rest) ->
621-
List.fold_left (fun r (_, _, p, _) -> extract_vars r p) r lpats
633+
| Tpat_record (lpats, _, rest) -> (
634+
let r = List.fold_left (fun r (_, _, p, _) -> extract_vars r p) r lpats in
635+
match rest with
636+
| None -> r
637+
| Some rest -> IdentSet.add rest.rest_ident r)
622638
| Tpat_construct (_, _, pats) -> List.fold_left extract_vars r pats
623639
| Tpat_array pats -> List.fold_left extract_vars r pats
624640
| Tpat_variant (_, Some p, _) -> extract_vars r p
@@ -2742,32 +2758,7 @@ let partial_function loc () =
27422758
],
27432759
loc )
27442760

2745-
(* For record patterns with rest, inject the rest binding into the action body *)
2746-
let inject_record_rest_binding param (pat, action) =
2747-
match pat.pat_desc with
2748-
| Tpat_record (_, _, Some rest) ->
2749-
let action_with_rest =
2750-
Llet
2751-
( Strict,
2752-
Pgenval,
2753-
rest.rest_ident,
2754-
Lprim (Precord_spread_new rest.excluded_labels, [param], pat.pat_loc),
2755-
action )
2756-
in
2757-
let pat_without_rest =
2758-
{
2759-
pat with
2760-
pat_desc =
2761-
(match pat.pat_desc with
2762-
| Tpat_record (fields, closed, _) -> Tpat_record (fields, closed, None)
2763-
| _ -> pat.pat_desc);
2764-
}
2765-
in
2766-
(pat_without_rest, action_with_rest)
2767-
| _ -> (pat, action)
2768-
27692761
let for_function loc repr param pat_act_list partial =
2770-
let pat_act_list = List.map (inject_record_rest_binding param) pat_act_list in
27712762
compile_matching repr (partial_function loc) param pat_act_list partial
27722763

27732764
(* In the following two cases, exhaustiveness info is not available! *)
@@ -2836,28 +2827,6 @@ let for_let loc param pat body =
28362827
| Tpat_var (id, _) ->
28372828
(* fast path, and keep track of simple bindings to unboxable numbers *)
28382829
Llet (Strict, Pgenval, id, param, body)
2839-
| Tpat_record (_, _, Some rest) ->
2840-
(* Record pattern with rest: compile the explicit field bindings normally,
2841-
then add a binding for the rest ident using Precord_spread_new *)
2842-
let body_with_rest =
2843-
Llet
2844-
( Strict,
2845-
Pgenval,
2846-
rest.rest_ident,
2847-
Lprim (Precord_spread_new rest.excluded_labels, [param], loc),
2848-
body )
2849-
in
2850-
(* Compile the explicit fields pattern (without rest) into the body *)
2851-
let pat_without_rest =
2852-
{
2853-
pat with
2854-
pat_desc =
2855-
(match pat.pat_desc with
2856-
| Tpat_record (fields, closed, _) -> Tpat_record (fields, closed, None)
2857-
| _ -> pat.pat_desc);
2858-
}
2859-
in
2860-
simple_for_let loc param pat_without_rest body_with_rest
28612830
| _ -> simple_for_let loc param pat body
28622831

28632832
(* Handling of tupled functions and matchings *)

compiler/ml/parsetree0.ml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -597,6 +597,7 @@ and module_binding = {
597597

598598
let optional_attr = (Location.mknoloc "res.optional", Parsetree.PStr [])
599599
let optional_attr0 = (Location.mknoloc "res.optional", PStr [])
600+
let record_rest_attr_name = "res.record_rest"
600601

601602
let add_optional_attr ~optional attrs =
602603
if optional then optional_attr0 :: attrs else attrs
@@ -608,3 +609,16 @@ let get_optional_attr attrs_ =
608609
let attrs = remove_optional_attr attrs_ in
609610
let optional = List.length attrs <> List.length attrs_ in
610611
(optional, attrs)
612+
613+
let add_record_rest_attr ~rest attrs =
614+
(Location.mknoloc record_rest_attr_name, PPat (rest, None)) :: attrs
615+
616+
let get_record_rest_attr attrs_ =
617+
let rec remove_record_rest_attr acc = function
618+
| ({Location.txt = attr_name; _}, Parsetree.PPat (rest, None)) :: attrs
619+
when attr_name = record_rest_attr_name ->
620+
(Some rest, List.rev_append acc attrs)
621+
| attr :: attrs -> remove_record_rest_attr (attr :: acc) attrs
622+
| [] -> (None, List.rev acc)
623+
in
624+
remove_record_rest_attr [] attrs_

compiler/ml/typecore.ml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2237,7 +2237,9 @@ let iter_ppat f p =
22372237
| Ppat_open (_, p)
22382238
| Ppat_constraint (p, _) ->
22392239
f p
2240-
| Ppat_record (args, _flag, _rest) -> List.iter (fun {x = p} -> f p) args
2240+
| Ppat_record (args, _flag, rest) ->
2241+
List.iter (fun {x = p} -> f p) args;
2242+
may f rest
22412243
22422244
let contains_polymorphic_variant p =
22432245
let rec loop p =

compiler/ml/typedtree.ml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,16 @@ let rec alpha_pat env p =
500500
let new_p = alpha_pat env p1 in
501501
try {p with pat_desc = Tpat_alias (new_p, alpha_var env id, s)}
502502
with Not_found -> new_p)
503+
| Tpat_record (lpats, closed, Some rest) ->
504+
let rest_ident =
505+
try alpha_var env rest.rest_ident with Not_found -> rest.rest_ident
506+
in
507+
let lpats =
508+
List.map
509+
(fun (lid, lbl, pat, opt) -> (lid, lbl, alpha_pat env pat, opt))
510+
lpats
511+
in
512+
{p with pat_desc = Tpat_record (lpats, closed, Some {rest with rest_ident})}
503513
| d -> {p with pat_desc = map_pattern_desc (alpha_pat env) d}
504514

505515
let mkloc = Location.mkloc

compiler/syntax/src/res_core.ml

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,11 @@ module ErrorMessages = struct
7676
"Record rest patterns require a type annotation and a binding name.\n\
7777
Correct syntax: `...typeName as bindingName`\n\
7878
Example: `let {name, ...Config.t as rest} = myRecord`"
79+
80+
let record_pattern_multiple_rest =
81+
"Record patterns can only have one `...` rest clause.\n\
82+
Use a single `...typeName as bindingName` clause to capture the remaining \
83+
fields."
7984
(* let recordPatternUnderscore = "Record patterns only support one `_`, at the end." *)
8085
[@@live]
8186

@@ -1549,7 +1554,13 @@ and parse_record_pattern ~attrs p =
15491554
Parser.err ~start_pos:pattern.Parsetree.ppat_loc.loc_start p
15501555
(Diagnostics.message ErrorMessages.record_pattern_spread));
15511556
(field :: fields, flag, rest)
1552-
| PatRest rest_pat -> (fields, flag, Some rest_pat)
1557+
| PatRest rest_pat -> (
1558+
match rest with
1559+
| None -> (fields, flag, Some rest_pat)
1560+
| Some _ ->
1561+
Parser.err ~start_pos:rest_pat.Parsetree.ppat_loc.loc_start p
1562+
(Diagnostics.message ErrorMessages.record_pattern_multiple_rest);
1563+
(fields, flag, rest))
15531564
| PatUnderscore -> (fields, flag, rest))
15541565
([], flag, None) raw_fields
15551566
in
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
2+
Syntax error!
3+
syntax_tests/data/parsing/errors/other/record_rest_duplicate.res:1:9-51
4+
5+
1 │ let {...Config.t as first, ...Config.t as second} = myRecord
6+
2 │
7+
8+
Record patterns can only have one `...` rest clause.
9+
Use a single `...typeName as bindingName` clause to capture the remaining fields.
10+
11+
let { } = myRecord

0 commit comments

Comments
 (0)