Skip to content

Commit 7cc3534

Browse files
committed
Concrete node for tuple element punning
1 parent c8325a9 commit 7cc3534

File tree

13 files changed

+343
-99
lines changed

13 files changed

+343
-99
lines changed

lib/Ast.ml

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -978,7 +978,7 @@ end = struct
978978
| Ptyp_arrow (t, t2) ->
979979
assert (List.exists t ~f:(fun x -> typ == x.pap_type) || typ == t2)
980980
| Ptyp_tuple t1N ->
981-
assert (List.exists t1N ~f:(fun x -> x.te_elt == typ))
981+
assert (List.exists t1N ~f:(fun x -> x.lte_elt == typ))
982982
| Ptyp_constr (_, t1N) -> assert (List.exists t1N ~f)
983983
| Ptyp_variant (r1N, _, _) ->
984984
assert (
@@ -1023,6 +1023,11 @@ end = struct
10231023
assert (List.exists ptyp.ppt_cstrs ~f:(fun (_, t) -> typ == t))
10241024
| Ppat_record (l, _) ->
10251025
assert (List.exists l ~f:(fun (_, t, _) -> Option.exists t ~f))
1026+
| Ppat_tuple (l, _) ->
1027+
assert (
1028+
List.exists l ~f:(function
1029+
| Lte_constrained_pun x -> x.type_constraint == typ
1030+
| _ -> false ) )
10261031
| _ -> assert false )
10271032
| Exp ctx -> (
10281033
match ctx.pexp_desc with
@@ -1041,6 +1046,16 @@ end = struct
10411046
Option.exists c ~f:check_type_constraint ) )
10421047
| Pexp_let (lbs, _, _) -> assert (check_let_bindings lbs)
10431048
| Pexp_function (_, Some t1, _, _) -> assert (check_type_constraint t1)
1049+
| Pexp_tuple l ->
1050+
assert (
1051+
List.exists l ~f:(function
1052+
| Lte_constrained_pun
1053+
{type_constraint= Pconstraint t | Pcoerce (None, t); _} ->
1054+
t == typ
1055+
| Lte_constrained_pun
1056+
{type_constraint= Pcoerce (Some bty, ty); _} ->
1057+
typ == bty || typ == ty
1058+
| _ -> false ) )
10441059
| _ -> assert false )
10451060
| Fpe _ | Fpc _ -> assert false
10461061
| Vc c -> assert (check_value_constraint c)
@@ -1258,7 +1273,10 @@ end = struct
12581273
let f pI = pI == pat in
12591274
match ctx.ppat_desc with
12601275
| Ppat_tuple (p1N, _) ->
1261-
assert (List.exists p1N ~f:(fun x -> f x.te_elt))
1276+
assert (
1277+
List.exists p1N ~f:(function
1278+
| Lte_simple s -> f s.lte_elt
1279+
| _ -> false ) )
12621280
| Ppat_array p1N | Ppat_list p1N | Ppat_cons p1N ->
12631281
assert (List.exists p1N ~f)
12641282
| Ppat_record (p1N, _) ->
@@ -1428,7 +1446,10 @@ end = struct
14281446
(* FAIL *)
14291447
assert (e0 == exp || List.exists e1N ~f:snd_f)
14301448
| Pexp_tuple e1N ->
1431-
assert (List.exists e1N ~f:(fun te -> te.te_elt == exp))
1449+
assert (
1450+
List.exists e1N ~f:(function
1451+
| Lte_simple te -> te.lte_elt == exp
1452+
| _ -> false ) )
14321453
| Pexp_array e1N | Pexp_list e1N | Pexp_cons e1N ->
14331454
assert (List.exists e1N ~f)
14341455
| Pexp_construct (_, e) | Pexp_variant (_, e) ->
@@ -1536,7 +1557,10 @@ end = struct
15361557
| Pexp_construct (_, Some e0) | Pexp_variant (_, Some e0) ->
15371558
Exp.is_trivial e0
15381559
| Pexp_tuple e1N ->
1539-
List.for_all e1N ~f:(fun te -> Exp.is_trivial te.te_elt)
1560+
List.for_all e1N ~f:(function
1561+
| Lte_pun _ -> true
1562+
| Lte_constrained_pun _ -> false
1563+
| Lte_simple lte -> Exp.is_trivial lte.lte_elt )
15401564
&& fit_margin c (width xexp)
15411565
| Pexp_array e1N | Pexp_list e1N ->
15421566
List.for_all e1N ~f:Exp.is_trivial && fit_margin c (width xexp)
@@ -1640,8 +1664,9 @@ end = struct
16401664
| {ast= Typ _; _} -> None
16411665
| {ctx= Exp {pexp_desc; _}; ast= Exp exp} -> (
16421666
match pexp_desc with
1643-
| Pexp_tuple ({te_elt= e0; _} :: _) ->
1667+
| Pexp_tuple (Lte_simple {lte_elt= e0; _} :: _) ->
16441668
Some (Comma, if exp == e0 then Left else Right)
1669+
| Pexp_tuple (_ :: _) -> Some (Comma, Right)
16451670
| Pexp_cons l ->
16461671
Some (ColonColon, if exp == List.last_exn l then Right else Left)
16471672
| Pexp_construct
@@ -1857,7 +1882,7 @@ end = struct
18571882
( Str {pstr_desc= Pstr_exception _; _}
18581883
| Sig {psig_desc= Psig_exception _; _} ) } ->
18591884
true
1860-
| { ast= {ptyp_desc= Ptyp_tuple ({te_label= Some _; _} :: _); _}
1885+
| { ast= {ptyp_desc= Ptyp_tuple ({lte_label= Some _; _} :: _); _}
18611886
; ctx= Typ {ptyp_desc= Ptyp_arrow _; _} } ->
18621887
true
18631888
| _ -> (
@@ -2051,6 +2076,9 @@ end = struct
20512076
register_reset (fun () -> Hashtbl.clear memo) ;
20522077
memo
20532078

2079+
let last_tuple_and_simple f l =
2080+
match List.last_exn l with Lte_simple l -> f l.lte_elt | _ -> false
2081+
20542082
(** [exposed cls exp] holds if there is a right-most subexpression of [exp]
20552083
which satisfies [Exp.mem_cls cls] and is not parenthesized. *)
20562084
let rec exposed_right_exp =
@@ -2099,7 +2127,7 @@ end = struct
20992127
|Pexp_try (_, cases, _) ->
21002128
continue (List.last_exn cases).pc_rhs
21012129
| Pexp_apply (_, args) -> continue (snd (List.last_exn args))
2102-
| Pexp_tuple es -> continue (List.last_exn es).te_elt
2130+
| Pexp_tuple es -> last_tuple_and_simple continue es
21032131
| Pexp_array _ | Pexp_list _ | Pexp_coerce _ | Pexp_constant _
21042132
|Pexp_constraint _
21052133
|Pexp_construct (_, None)
@@ -2180,7 +2208,7 @@ end = struct
21802208
| Pexp_indexop_access {pia_rhs= rhs; _} -> (
21812209
match rhs with Some e -> continue e | None -> false )
21822210
| Pexp_apply (_, args) -> continue (snd (List.last_exn args))
2183-
| Pexp_tuple es -> continue (List.last_exn es).te_elt
2211+
| Pexp_tuple es -> last_tuple_and_simple continue es
21842212
| Pexp_array _ | Pexp_list _ | Pexp_coerce _ | Pexp_constant _
21852213
|Pexp_constraint _
21862214
|Pexp_construct (_, None)
@@ -2232,7 +2260,7 @@ end = struct
22322260
&& Option.value_map ~default:false (prec_ast ctx) ~f:(fun p ->
22332261
Prec.compare p Apply < 0 ) ->
22342262
true
2235-
| Pexp_tuple e1N -> (List.last_exn e1N).te_elt == xexp.ast
2263+
| Pexp_tuple e1N -> last_tuple_and_simple (( == ) xexp.ast) e1N
22362264
| _ -> false
22372265
in
22382266
match ambig_prec (sub_ast ~ctx (Exp exp)) with

lib/Exposed.ml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ module Left = struct
1515
let rec core_type typ =
1616
match typ.ptyp_desc with
1717
| Ptyp_arrow (t :: _, _) -> core_type t.pap_type
18-
| Ptyp_tuple l -> core_type (List.hd_exn l).te_elt
18+
| Ptyp_tuple l -> core_type (List.hd_exn l).lte_elt
1919
| Ptyp_object _ -> true
2020
| Ptyp_alias (typ, _) -> core_type typ
2121
| _ -> false
@@ -29,7 +29,7 @@ module Right = struct
2929
| {ptyp_desc; _} -> (
3030
match ptyp_desc with
3131
| Ptyp_arrow (_, t) -> core_type t
32-
| Ptyp_tuple l -> core_type (List.last_exn l).te_elt
32+
| Ptyp_tuple l -> core_type (List.last_exn l).lte_elt
3333
| Ptyp_object _ -> true
3434
| _ -> false )
3535

lib/Extended_ast.ml

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,81 @@ module Parse = struct
154154
(f, Some t, None)
155155
| _ -> (f, t, Option.map ~f:(m.pat m) v)
156156
in
157+
let map_labeled_tuple_element m f = function
158+
| Lte_simple lte -> f m lte
159+
| (Lte_constrained_pun _ | Lte_pun _) as x -> x
160+
in
161+
let pat_tuple_elt m te =
162+
match (te.lte_label, te.lte_elt) with
163+
(* [ ~x:x ] -> [ ~x ] *)
164+
| ( Some lbl
165+
, {ppat_desc= Ppat_var {txt= v_txt; _}; ppat_attributes= []; _} )
166+
when String.equal lbl.txt v_txt ->
167+
Lte_pun lbl
168+
(* [~x:(x : t)] -> [ ~(x : t)] *)
169+
| ( Some lbl
170+
, { ppat_desc=
171+
Ppat_constraint
172+
( { ppat_desc= Ppat_var {txt= v_txt; _}
173+
; ppat_attributes= []
174+
; _ }
175+
, t )
176+
; ppat_attributes= []
177+
; ppat_loc
178+
; _ } )
179+
when String.equal lbl.txt v_txt ->
180+
Lte_constrained_pun
181+
{ loc= {lbl.loc with loc_end= ppat_loc.loc_end}
182+
; label= lbl
183+
; type_constraint= t }
184+
| lte_label, pat -> Lte_simple {lte_label; lte_elt= m.pat m pat}
185+
in
186+
let pat_tuple_elt m lte =
187+
map_labeled_tuple_element m pat_tuple_elt lte
188+
in
189+
let exp_tuple_elt m te =
190+
match (te.lte_label, te.lte_elt) with
191+
(* [ ~x:x ] -> [ ~x ] *)
192+
| ( Some lbl
193+
, { pexp_desc= Pexp_ident {txt= Lident v_txt; _}
194+
; pexp_attributes= []
195+
; _ } )
196+
when String.equal lbl.txt v_txt ->
197+
Lte_pun lbl
198+
(* [~x:(x : t)] -> [ ~(x : t)] *)
199+
| ( Some lbl
200+
, { pexp_desc=
201+
Pexp_constraint
202+
( { pexp_desc= Pexp_ident {txt= Lident v_txt; _}
203+
; pexp_attributes= []
204+
; _ }
205+
, t )
206+
; pexp_attributes= []
207+
; pexp_loc
208+
; _ } )
209+
when String.equal lbl.txt v_txt ->
210+
Lte_constrained_pun
211+
{ loc= {lbl.loc with loc_end= pexp_loc.loc_end}
212+
; label= lbl
213+
; type_constraint= Pconstraint t }
214+
(* [~x:(x : t1 :> t2)] -> [ ~(x : t1 :> t2)] *)
215+
| ( Some lbl
216+
, { pexp_desc=
217+
Pexp_coerce
218+
({pexp_desc= Pexp_ident {txt= Lident v_txt; _}; _}, bty, tty)
219+
; pexp_attributes= []
220+
; pexp_loc
221+
; _ } )
222+
when String.equal lbl.txt v_txt ->
223+
Lte_constrained_pun
224+
{ loc= {lbl.loc with loc_end= pexp_loc.loc_end}
225+
; label= lbl
226+
; type_constraint= Pcoerce (bty, tty) }
227+
| lte_label, exp -> Lte_simple {lte_label; lte_elt= m.expr m exp}
228+
in
229+
let exp_tuple_elt m lte =
230+
map_labeled_tuple_element m exp_tuple_elt lte
231+
in
157232
let binding_op (m : Ast_mapper.mapper) b =
158233
let b' =
159234
let loc_start = b.pbop_op.loc.loc_start in
@@ -184,6 +259,9 @@ module Parse = struct
184259
, {ptyp_desc= Ptyp_package pt; ptyp_attributes= []; _} )
185260
; _ } as p ->
186261
{p with ppat_desc= Ppat_unpack (name, Some pt)}
262+
| {ppat_desc= Ppat_tuple (l, oc); _} as p ->
263+
let l = List.map ~f:(pat_tuple_elt m) l in
264+
{p with ppat_desc= Ppat_tuple (l, oc)}
187265
| p -> Ast_mapper.default_mapper.pat m p
188266
in
189267
let expr (m : Ast_mapper.mapper) = function
@@ -222,6 +300,9 @@ module Parse = struct
222300
&& not (Std_longident.is_monadic_binding longident) ->
223301
let label_loc = {txt= op; loc= loc_op} in
224302
{e with pexp_desc= Pexp_infix (label_loc, m.expr m l, m.expr m r)}
303+
| {pexp_desc= Pexp_tuple l; _} as p ->
304+
let l = List.map ~f:(exp_tuple_elt m) l in
305+
{p with pexp_desc= Pexp_tuple l}
225306
| e -> Ast_mapper.default_mapper.expr m e
226307
in
227308
Ast_mapper.{default_mapper with expr; pat; binding_op}

lib/Fmt_ast.ml

Lines changed: 27 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -966,7 +966,7 @@ and fmt_core_type c ?(box = true) ?pro ?(pro_space = true) ?constraint_ctx
966966
$ fmt_core_type c ?box:box_core_type ~pro_space:false
967967
(sub_typ ~ctx t) )
968968
| Ptyp_tuple typs ->
969-
let with_label {te_label= lbl; te_elt= typ} =
969+
let with_label {lte_label= lbl; lte_elt= typ} =
970970
let label = fmt_tuple_label c lbl (str ":") in
971971
label $ fmt_core_type c (sub_typ ~ctx typ)
972972
in
@@ -1164,20 +1164,20 @@ and fmt_pattern ?ext c ?pro ?parens ?(box = false)
11641164
let parens =
11651165
parens || Poly.(c.conf.fmt_opts.parens_tuple_patterns.v = `Always)
11661166
in
1167-
let with_label {te_label= lbl; te_elt= pat} =
1168-
match (lbl, pat) with
1169-
| ( Some {txt= label; loc}
1170-
, { ppat_desc=
1171-
( Ppat_var var
1172-
| Ppat_constraint ({ppat_desc= Ppat_var var; _}, _) )
1173-
; ppat_attributes= []
1174-
; _ } )
1175-
when String.(var.txt = label) ->
1176-
Cmts.fmt c loc @@ str "~" $ fmt_pattern c (sub_pat ~ctx pat)
1177-
| (Some _ as lbl), {ppat_desc= Ppat_construct _; _} ->
1167+
let with_label = function
1168+
| Lte_pun l -> fmt_str_loc c ~pre:"~" l
1169+
| Lte_constrained_pun {loc; label; type_constraint} ->
1170+
Cmts.fmt c loc
1171+
( str "~(" $ fmt_str_loc c label $ space_break $ str ":"
1172+
$ space_break
1173+
$ fmt_core_type c (sub_typ ~ctx type_constraint)
1174+
$ str ")" )
1175+
| Lte_simple
1176+
{ lte_label= Some _ as lbl
1177+
; lte_elt= {ppat_desc= Ppat_construct _; _} as pat } ->
11781178
let label = fmt_tuple_label c ~pre:"~" lbl (str ":") in
11791179
label $ fmt_pattern ~parens:true c (sub_pat ~ctx pat)
1180-
| lbl, _ ->
1180+
| Lte_simple {lte_label= lbl; lte_elt= pat} ->
11811181
let label = fmt_tuple_label c ~pre:"~" lbl (str ":") in
11821182
label $ fmt_pattern c (sub_pat ~ctx pat)
11831183
in
@@ -2863,23 +2863,22 @@ and fmt_expression c ?(box = true) ?(pro = noop) ?eol ?parens
28632863
in
28642864
let outer_wrap = has_attr && parens in
28652865
let inner_wrap = has_attr || parens in
2866-
let with_label {te_label= lbl; te_elt= exp} =
2867-
match (lbl, exp) with
2868-
| ( Some {txt; loc}
2869-
, { pexp_desc=
2870-
( Pexp_ident {txt= Lident i; _}
2871-
| Pexp_constraint
2872-
({pexp_desc= Pexp_ident {txt= Lident i; _}; _}, _) )
2873-
; pexp_attributes= []
2874-
; _ } )
2875-
when String.equal i txt ->
2876-
Cmts.fmt c loc @@ str "~" $ fmt_expression c (sub_exp ~ctx exp)
2877-
| (Some _ as lbl), {pexp_desc= Pexp_apply _ | Pexp_function _; _} ->
2866+
let with_label = function
2867+
| Lte_pun l -> fmt_str_loc c ~pre:"~" l
2868+
| Lte_constrained_pun {loc; label; type_constraint} ->
2869+
Cmts.fmt c loc
2870+
( str "~(" $ fmt_str_loc c label
2871+
$ fmt_type_constraint c ctx type_constraint
2872+
$ str ")" )
2873+
| Lte_simple
2874+
{ lte_label= Some _ as lbl
2875+
; lte_elt= {pexp_desc= Pexp_apply _ | Pexp_function _; _} as exp
2876+
} ->
28782877
fmt_tuple_label c ~pre:"~" lbl (str ":")
28792878
$ fmt_expression ~parens:true c (sub_exp ~ctx exp)
2880-
| lbl, _ ->
2881-
fmt_tuple_label c ~pre:"~" lbl (str ":")
2882-
$ fmt_expression c (sub_exp ~ctx exp)
2879+
| Lte_simple {lte_label= lbl; lte_elt= pat} ->
2880+
let label = fmt_tuple_label c ~pre:"~" lbl (str ":") in
2881+
label $ fmt_expression c (sub_exp ~ctx pat)
28832882
in
28842883
pro
28852884
$ hvbox_if outer_wrap 0

test/passing/refs.ahrefs/labeled_tuples.ml.ref

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -593,6 +593,12 @@ let ~(x : int), _ = ~x:0, 1
593593

594594
let _ = ~(x : int), ~(y : int)
595595

596+
let _ = ~(x : int :> float), ~y, ~z
597+
598+
(** Normalization *)
599+
600+
let ~x:{ x }, ~y, ~(z : t), .. = ~x:{ x }, ~y, ~(z : t), ~(w : t :> t)
601+
596602
(** Comment tests *)
597603
type t =
598604
(*before*)
@@ -601,3 +607,25 @@ type t =
601607
y:(*after y, before the type*) int (* after the type, before * *)
602608
* (* before the z label *)
603609
z:(*after z label *) float (*end*)
610+
611+
let
612+
(*before*)
613+
( ~(x
614+
(*l-ty*)
615+
:
616+
int (*after-ty*))
617+
(*after x*),
618+
(*before y*)
619+
~y
620+
(* after y *),
621+
(* bz *)
622+
~z:(*zv*) 0 (*end*) ) =
623+
(*before*)
624+
( ~(x
625+
: (*l-ty*) int (*after-ty*))
626+
(*after x*),
627+
(*before y*)
628+
~y
629+
(* after y *),
630+
(* bz *)
631+
~z:(*zv*) 0 (*end*) )

0 commit comments

Comments
 (0)