Skip to content

Commit 3d98dcd

Browse files
authored
OCaml 5.4 syntax support (#2720)
* Basic OCaml 5.4 support - update vendored parsers to mirror upstream at 5.4: * introduce locations for Longident.t components * distinguish (module M:S) and ((module M):(module S)) for expressions - support for new syntaxes: * bivariance * labelled tuples * Add test for labeled tuples * review: lax rule for variance annotation * review: test comments inside tuple types * review: track labels locations in tuple types * Locations for labels * Concrete node for tuple element punning
1 parent 5d9d21e commit 3d98dcd

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+4320
-347
lines changed

CHANGES.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,12 @@ profile. This started with version 0.26.0.
66

77
## unreleased
88

9-
### Added
9+
### Added
1010

11-
- Support for OCaml 5.4 (#2717, @Julow)
11+
- Support for OCaml 5.4 (#2717, #2720, @Julow, @Octachron)
12+
OCamlformat now supports OCaml 5.4 syntax.
13+
Module packing of the form `((module M) : (module S))` are no longer
14+
rewritten to `(module M : S)` because these are now two different syntaxes.
1215

1316
- Added option `module-indent` option (#2711, @HPRIOR) to control the indentation
1417
of items within modules. This affects modules and signatures. For example,

lib/Ast.ml

Lines changed: 56 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,8 @@ let longident_is_simple c x =
6161
let rec length x =
6262
match x with
6363
| Longident.Lident x -> String.length x
64-
| Ldot (x, y) -> length x + 1 + String.length y
65-
| Lapply (x, y) -> length x + length y + 3
64+
| Ldot (x, y) -> length x.txt + 1 + String.length y.txt
65+
| Lapply (x, y) -> length x.txt + length y.txt + 3
6666
in
6767
longident_fit_margin c (length x)
6868

@@ -977,14 +977,16 @@ end = struct
977977
| Ptyp_alias (t1, _) | Ptyp_poly (_, t1) -> assert (typ == t1)
978978
| Ptyp_arrow (t, t2) ->
979979
assert (List.exists t ~f:(fun x -> typ == x.pap_type) || typ == t2)
980-
| Ptyp_tuple t1N | Ptyp_constr (_, t1N) -> assert (List.exists t1N ~f)
980+
| Ptyp_tuple t1N ->
981+
assert (List.exists t1N ~f:(fun x -> x.lte_elt == typ))
982+
| Ptyp_constr (_, t1N) -> assert (List.exists t1N ~f)
981983
| Ptyp_variant (r1N, _, _) ->
982984
assert (
983985
List.exists r1N ~f:(function
984986
| {prf_desc= Rtag (_, _, t1N); _} -> List.exists t1N ~f
985987
| {prf_desc= Rinherit t1; _} -> typ == t1 ) )
986988
| Ptyp_open (_, t1) -> assert (t1 == typ)
987-
| Ptyp_package (_, it1N, _) -> assert (List.exists it1N ~f:snd_f)
989+
| Ptyp_package ptyp -> assert (List.exists ptyp.ppt_cstrs ~f:snd_f)
988990
| Ptyp_object (fields, _) ->
989991
assert (
990992
List.exists fields ~f:(function
@@ -1017,15 +1019,20 @@ end = struct
10171019
match ctx.ppat_desc with
10181020
| Ppat_constraint (_, t1) -> assert (typ == t1)
10191021
| Ppat_extension (_, PTyp t) -> assert (typ == t)
1020-
| Ppat_unpack (_, Some (_, l, _)) ->
1021-
assert (List.exists l ~f:(fun (_, t) -> typ == t))
1022+
| Ppat_unpack (_, Some ptyp) ->
1023+
assert (List.exists ptyp.ppt_cstrs ~f:(fun (_, t) -> typ == t))
10221024
| Ppat_record (l, _) ->
10231025
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 ) )
10241031
| _ -> assert false )
10251032
| Exp ctx -> (
10261033
match ctx.pexp_desc with
1027-
| Pexp_pack (_, Some (_, it1N, _), _) ->
1028-
assert (List.exists it1N ~f:snd_f)
1034+
| Pexp_pack (_, Some ptyp, _) ->
1035+
assert (List.exists ptyp.ppt_cstrs ~f:snd_f)
10291036
| Pexp_constraint (_, t1)
10301037
|Pexp_coerce (_, None, t1)
10311038
|Pexp_extension (_, PTyp t1) ->
@@ -1039,6 +1046,16 @@ end = struct
10391046
Option.exists c ~f:check_type_constraint ) )
10401047
| Pexp_let (lbs, _, _) -> assert (check_let_bindings lbs)
10411048
| 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 ) )
10421059
| _ -> assert false )
10431060
| Fpe _ | Fpc _ -> assert false
10441061
| Vc c -> assert (check_value_constraint c)
@@ -1063,7 +1080,7 @@ end = struct
10631080
| Mod ctx -> (
10641081
match ctx.pmod_desc with
10651082
| Pmod_unpack (_, ty1, ty2) ->
1066-
let f (_, cstrs, _) = List.exists cstrs ~f:(fun (_, x) -> f x) in
1083+
let f ptyp = List.exists ptyp.ppt_cstrs ~f:snd_f in
10671084
assert (Option.exists ty1 ~f || Option.exists ty2 ~f)
10681085
| _ -> assert false )
10691086
| Sig ctx -> (
@@ -1255,7 +1272,12 @@ end = struct
12551272
| Pat ctx -> (
12561273
let f pI = pI == pat in
12571274
match ctx.ppat_desc with
1258-
| Ppat_array p1N | Ppat_list p1N | Ppat_tuple p1N | Ppat_cons p1N ->
1275+
| Ppat_tuple (p1N, _) ->
1276+
assert (
1277+
List.exists p1N ~f:(function
1278+
| Lte_simple s -> f s.lte_elt
1279+
| _ -> false ) )
1280+
| Ppat_array p1N | Ppat_list p1N | Ppat_cons p1N ->
12591281
assert (List.exists p1N ~f)
12601282
| Ppat_record (p1N, _) ->
12611283
assert (List.exists p1N ~f:(fun (_, _, x) -> Option.exists x ~f))
@@ -1423,7 +1445,12 @@ end = struct
14231445
| Pexp_apply (e0, e1N) ->
14241446
(* FAIL *)
14251447
assert (e0 == exp || List.exists e1N ~f:snd_f)
1426-
| Pexp_tuple e1N | Pexp_array e1N | Pexp_list e1N | Pexp_cons e1N ->
1448+
| Pexp_tuple e1N ->
1449+
assert (
1450+
List.exists e1N ~f:(function
1451+
| Lte_simple te -> te.lte_elt == exp
1452+
| _ -> false ) )
1453+
| Pexp_array e1N | Pexp_list e1N | Pexp_cons e1N ->
14271454
assert (List.exists e1N ~f)
14281455
| Pexp_construct (_, e) | Pexp_variant (_, e) ->
14291456
assert (Option.exists e ~f)
@@ -1529,7 +1556,13 @@ end = struct
15291556
&& fit_margin c (width xexp)
15301557
| Pexp_construct (_, Some e0) | Pexp_variant (_, Some e0) ->
15311558
Exp.is_trivial e0
1532-
| Pexp_array e1N | Pexp_list e1N | Pexp_tuple e1N ->
1559+
| Pexp_tuple e1N ->
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 )
1564+
&& fit_margin c (width xexp)
1565+
| Pexp_array e1N | Pexp_list e1N ->
15331566
List.for_all e1N ~f:Exp.is_trivial && fit_margin c (width xexp)
15341567
| Pexp_record (e1N, e0) ->
15351568
Option.for_all e0 ~f:Exp.is_trivial
@@ -1631,8 +1664,9 @@ end = struct
16311664
| {ast= Typ _; _} -> None
16321665
| {ctx= Exp {pexp_desc; _}; ast= Exp exp} -> (
16331666
match pexp_desc with
1634-
| Pexp_tuple (e0 :: _) ->
1667+
| Pexp_tuple (Lte_simple {lte_elt= e0; _} :: _) ->
16351668
Some (Comma, if exp == e0 then Left else Right)
1669+
| Pexp_tuple (_ :: _) -> Some (Comma, Right)
16361670
| Pexp_cons l ->
16371671
Some (ColonColon, if exp == List.last_exn l then Right else Left)
16381672
| Pexp_construct
@@ -1848,6 +1882,9 @@ end = struct
18481882
( Str {pstr_desc= Pstr_exception _; _}
18491883
| Sig {psig_desc= Psig_exception _; _} ) } ->
18501884
true
1885+
| { ast= {ptyp_desc= Ptyp_tuple ({lte_label= Some _; _} :: _); _}
1886+
; ctx= Typ {ptyp_desc= Ptyp_arrow _; _} } ->
1887+
true
18511888
| _ -> (
18521889
match ambig_prec (sub_ast ~ctx (Typ typ)) with
18531890
| `Ambiguous -> true
@@ -2039,6 +2076,9 @@ end = struct
20392076
register_reset (fun () -> Hashtbl.clear memo) ;
20402077
memo
20412078

2079+
let last_tuple_and_simple f l =
2080+
match List.last_exn l with Lte_simple l -> f l.lte_elt | _ -> false
2081+
20422082
(** [exposed cls exp] holds if there is a right-most subexpression of [exp]
20432083
which satisfies [Exp.mem_cls cls] and is not parenthesized. *)
20442084
let rec exposed_right_exp =
@@ -2087,7 +2127,7 @@ end = struct
20872127
|Pexp_try (_, cases, _) ->
20882128
continue (List.last_exn cases).pc_rhs
20892129
| Pexp_apply (_, args) -> continue (snd (List.last_exn args))
2090-
| Pexp_tuple es -> continue (List.last_exn es)
2130+
| Pexp_tuple es -> last_tuple_and_simple continue es
20912131
| Pexp_array _ | Pexp_list _ | Pexp_coerce _ | Pexp_constant _
20922132
|Pexp_constraint _
20932133
|Pexp_construct (_, None)
@@ -2168,7 +2208,7 @@ end = struct
21682208
| Pexp_indexop_access {pia_rhs= rhs; _} -> (
21692209
match rhs with Some e -> continue e | None -> false )
21702210
| Pexp_apply (_, args) -> continue (snd (List.last_exn args))
2171-
| Pexp_tuple es -> continue (List.last_exn es)
2211+
| Pexp_tuple es -> last_tuple_and_simple continue es
21722212
| Pexp_array _ | Pexp_list _ | Pexp_coerce _ | Pexp_constant _
21732213
|Pexp_constraint _
21742214
|Pexp_construct (_, None)
@@ -2220,7 +2260,7 @@ end = struct
22202260
&& Option.value_map ~default:false (prec_ast ctx) ~f:(fun p ->
22212261
Prec.compare p Apply < 0 ) ->
22222262
true
2223-
| Pexp_tuple e1N -> List.last_exn e1N == xexp.ast
2263+
| Pexp_tuple e1N -> last_tuple_and_simple (( == ) xexp.ast) e1N
22242264
| _ -> false
22252265
in
22262266
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)
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)
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 & 20 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,26 +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)}
225-
(* [(module M) : (module T)] -> [(module M : T)] *)
226-
| { pexp_desc=
227-
Pexp_constraint
228-
( { pexp_desc=
229-
Pexp_pack (name, None, {infix_ext= None; infix_attrs= []})
230-
; pexp_attributes= []
231-
; pexp_loc
232-
; _ }
233-
, {ptyp_desc= Ptyp_package pt; ptyp_attributes= []; ptyp_loc; _}
234-
)
235-
; _ } as p
236-
when Migrate_ast.Location.compare_start ptyp_loc pexp_loc > 0 ->
237-
(* Match locations to differentiate between the two position for
238-
the constraint, we want to shorten the second: - [let _ :
239-
(module S) = (module M)] - [let _ = ((module M) : (module
240-
S))] *)
241-
{ p with
242-
pexp_desc=
243-
Pexp_pack (name, Some pt, {infix_ext= None; infix_attrs= []})
244-
}
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}
245306
| e -> Ast_mapper.default_mapper.expr m e
246307
in
247308
Ast_mapper.{default_mapper with expr; pat; binding_op}

0 commit comments

Comments
 (0)