Skip to content

Commit b25c048

Browse files
authored
Backport polymorphic parameters (#2772)
* parser-std: Backport polymorphic paramters From ocaml/ocaml#13806 * parser-ext: Backport polymorphic parameters * Fix Ast rules for Ptyp_poly It is treated with the same precedence as Ptyp_arrow.
1 parent 86f8112 commit b25c048

10 files changed

Lines changed: 1092 additions & 39 deletions

File tree

CHANGES.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ profile. This started with version 0.26.0.
88

99
### Highlight
1010

11+
- Support OCaml 5.5 syntax (#2772, @Julow)
12+
1113
- \* Update Odoc's parser to 3.0 (#2757, @Julow)
1214
The indentation of code-blocks containing OCaml code is reduced by 2 to avoid
1315
changing the generated documentation. The indentation within code-blocks is

lib/Ast.ml

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1605,27 +1605,35 @@ end = struct
16051605
| _ -> false
16061606
in
16071607
let constructor_cxt_prec_of_inner = function
1608-
| {ptyp_desc= Ptyp_arrow _; _} -> Some (Apply, Non)
1608+
| {ptyp_desc= Ptyp_arrow _ | Ptyp_poly _; _} -> Some (Apply, Non)
16091609
| {ptyp_desc= Ptyp_tuple _; _} -> Some (InfixOp3, Non)
16101610
| _ -> None
16111611
in
16121612
match ctx with
16131613
| { ctx= Td {ptype_kind= Ptype_variant v; _}
1614-
; ast= Typ ({ptyp_desc= Ptyp_arrow _ | Ptyp_tuple _; _} as typ) }
1614+
; ast=
1615+
Typ
1616+
({ptyp_desc= Ptyp_arrow _ | Ptyp_poly _ | Ptyp_tuple _; _} as typ)
1617+
}
16151618
when List.exists v ~f:(is_tuple_lvl1_in_constructor typ) ->
16161619
constructor_cxt_prec_of_inner typ
16171620
| { ctx=
16181621
( Str {pstr_desc= Pstr_typext {ptyext_constructors= l; _}; _}
16191622
| Sig {psig_desc= Psig_typext {ptyext_constructors= l; _}; _} )
1620-
; ast= Typ ({ptyp_desc= Ptyp_arrow _ | Ptyp_tuple _; _} as typ)
1623+
; ast=
1624+
Typ
1625+
({ptyp_desc= Ptyp_arrow _ | Ptyp_poly _ | Ptyp_tuple _; _} as typ)
16211626
; _ }
16221627
when List.exists l ~f:(is_tuple_lvl1_in_ext_constructor typ) ->
16231628
constructor_cxt_prec_of_inner typ
16241629
| { ctx=
16251630
( Str {pstr_desc= Pstr_exception {ptyexn_constructor= constr; _}; _}
16261631
| Sig {psig_desc= Psig_exception {ptyexn_constructor= constr; _}; _}
16271632
| Exp {pexp_desc= Pexp_letexception (constr, _, _); _} )
1628-
; ast= Typ ({ptyp_desc= Ptyp_tuple _ | Ptyp_arrow _; _} as typ) }
1633+
; ast=
1634+
Typ
1635+
({ptyp_desc= Ptyp_tuple _ | Ptyp_arrow _ | Ptyp_poly _; _} as typ)
1636+
}
16291637
when is_tuple_lvl1_in_ext_constructor typ constr ->
16301638
constructor_cxt_prec_of_inner typ
16311639
| {ctx= Str _; ast= Typ _; _} -> None
@@ -1637,13 +1645,13 @@ end = struct
16371645
else Right
16381646
in
16391647
Some (MinusGreater, assoc)
1648+
| Ptyp_poly _ -> Some (MinusGreater, Right)
16401649
| Ptyp_tuple _ -> Some (InfixOp3, Non)
16411650
| Ptyp_alias _ -> Some (As, Non)
16421651
| Ptyp_constr (_, _ :: _ :: _) -> Some (Comma, Non)
16431652
| Ptyp_constr _ -> Some (Apply, Non)
16441653
| Ptyp_any | Ptyp_var _ | Ptyp_object _ | Ptyp_class _
1645-
|Ptyp_variant _ | Ptyp_poly _ | Ptyp_package _ | Ptyp_extension _
1646-
|Ptyp_open _ ->
1654+
|Ptyp_variant _ | Ptyp_package _ | Ptyp_extension _ | Ptyp_open _ ->
16471655
None )
16481656
| {ctx= Cty {pcty_desc; _}; ast= Typ typ; _} -> (
16491657
match pcty_desc with
@@ -1772,12 +1780,11 @@ end = struct
17721780
| Typ {ptyp_desc; _} -> (
17731781
match ptyp_desc with
17741782
| Ptyp_package _ -> Some Low
1775-
| Ptyp_arrow _ -> Some MinusGreater
1783+
| Ptyp_arrow _ | Ptyp_poly _ -> Some MinusGreater
17761784
| Ptyp_tuple _ -> Some InfixOp3
17771785
| Ptyp_alias _ -> Some As
17781786
| Ptyp_any | Ptyp_var _ | Ptyp_constr _ | Ptyp_object _
1779-
|Ptyp_class _ | Ptyp_variant _ | Ptyp_poly _ | Ptyp_extension _
1780-
|Ptyp_open _ ->
1787+
|Ptyp_class _ | Ptyp_variant _ | Ptyp_extension _ | Ptyp_open _ ->
17811788
None )
17821789
| Td _ -> None
17831790
| Cty {pcty_desc; _} -> (
@@ -1872,7 +1879,7 @@ end = struct
18721879
match xtyp with
18731880
| {ast= {ptyp_desc= Ptyp_package _; _}; _} -> true
18741881
| {ast= {ptyp_desc= Ptyp_alias _; _}; ctx= Typ _} -> true
1875-
| { ast= {ptyp_desc= Ptyp_arrow _ | Ptyp_tuple _; _}
1882+
| { ast= {ptyp_desc= Ptyp_arrow _ | Ptyp_poly _ | Ptyp_tuple _; _}
18761883
; ctx= Typ {ptyp_desc= Ptyp_class _; _} } ->
18771884
true
18781885
| { ast= {ptyp_desc= Ptyp_alias _; _}
@@ -1887,13 +1894,16 @@ end = struct
18871894
| Pcstr_tuple l -> List.exists l ~f:(phys_equal typ)
18881895
| _ -> false ) ->
18891896
true
1890-
| { ast= {ptyp_desc= Ptyp_alias _ | Ptyp_arrow _ | Ptyp_tuple _; _}
1897+
| { ast=
1898+
{ ptyp_desc=
1899+
Ptyp_alias _ | Ptyp_arrow _ | Ptyp_poly _ | Ptyp_tuple _
1900+
; _ }
18911901
; ctx=
18921902
( Str {pstr_desc= Pstr_exception _; _}
18931903
| Sig {psig_desc= Psig_exception _; _} ) } ->
18941904
true
18951905
| { ast= {ptyp_desc= Ptyp_tuple ({lte_label= Some _; _} :: _); _}
1896-
; ctx= Typ {ptyp_desc= Ptyp_arrow _; _} } ->
1906+
; ctx= Typ {ptyp_desc= Ptyp_arrow _ | Ptyp_poly _; _} } ->
18971907
true
18981908
| _ -> (
18991909
match ambig_prec (sub_ast ~ctx (Typ typ)) with
@@ -1987,7 +1997,6 @@ end = struct
19871997
| Fpe {pparam_desc= Pparam_val (_, _, _); _}, Ppat_cons _ -> true
19881998
| Fpc {pparam_desc= _; _}, Ppat_cons _ -> true
19891999
| Pat {ppat_desc= Ppat_construct _; _}, Ppat_cons _ -> true
1990-
| _, Ppat_constraint (_, {ptyp_desc= Ptyp_poly _; _}) -> false
19912000
| ( Bo {pbop_typ= None; _}
19922001
, ( Ppat_construct (_, Some _)
19932002
| Ppat_cons _

test/passing/gen/dune.inc

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4591,6 +4591,24 @@
45914591
(package ocamlformat)
45924592
(action (diff parens_tuple_patterns.ml.err parens_tuple_patterns.ml.stderr)))
45934593

4594+
(rule
4595+
(deps .ocamlformat)
4596+
(package ocamlformat)
4597+
(action
4598+
(with-stdout-to poly_params.ml.stdout
4599+
(with-stderr-to poly_params.ml.stderr
4600+
(run %{bin:ocamlformat} --name poly_params.ml --margin-check %{dep:../tests/poly_params.ml})))))
4601+
4602+
(rule
4603+
(alias runtest)
4604+
(package ocamlformat)
4605+
(action (diff poly_params.ml.ref poly_params.ml.stdout)))
4606+
4607+
(rule
4608+
(alias runtest)
4609+
(package ocamlformat)
4610+
(action (diff poly_params.ml.err poly_params.ml.stderr)))
4611+
45944612
(rule
45954613
(deps .ocamlformat)
45964614
(package ocamlformat)
Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
(* This test is extracted from the compiler's testsuite. *)
2+
3+
let poly1 (id : 'a. 'a -> 'a) = id 3, id "three"
4+
5+
let _ = poly1 (fun x -> x)
6+
7+
let _ = poly1 (fun x -> x + 1)
8+
9+
let id x = x
10+
let _ = poly1 id
11+
12+
let _ = poly1 (id (fun x -> x))
13+
14+
let _ =
15+
poly1
16+
(let r = ref None in
17+
fun x ->
18+
r := Some x;
19+
x)
20+
21+
let escape f =
22+
poly1 (fun x ->
23+
f x;
24+
x)
25+
26+
let poly2 : ('a. 'a -> 'a) -> int * string = fun id -> id 3, id "three"
27+
28+
let _ = poly2 (fun x -> x)
29+
30+
let _ = poly2 (fun x -> x + 1)
31+
32+
let poly3 : 'b. ('a. 'a -> 'a) -> 'b -> 'b * 'b option =
33+
fun id x -> id x, id (Some x)
34+
35+
let _ = poly3 (fun x -> x) 8
36+
37+
let _ = poly3 (fun x -> x + 1) 8
38+
39+
let rec poly4 p (id : 'a. 'a -> 'a) =
40+
if p then poly4 false id else id 4, id "four"
41+
42+
let _ = poly4 true (fun x -> x)
43+
44+
let _ = poly4 true (fun x -> x + 1)
45+
46+
let rec poly5 : bool -> ('a. 'a -> 'a) -> int * string =
47+
fun p id -> if p then poly5 false id else id 5, id "five"
48+
49+
let _ = poly5 true (fun x -> x)
50+
51+
let _ = poly5 true (fun x -> x + 1)
52+
53+
let rec poly6 : 'b. bool -> ('a. 'a -> 'a) -> 'b -> 'b * 'b option =
54+
fun p id x -> if p then poly6 false id x else id x, id (Some x)
55+
56+
let _ = poly6 true (fun x -> x) 8
57+
58+
let _ = poly6 true (fun x -> x + 1) 8
59+
60+
let needs_magic (magic : 'a 'b. 'a -> 'b) = (magic 5 : string)
61+
let _ = needs_magic (fun x -> x)
62+
63+
let with_id (f : ('a. 'a -> 'a) -> 'b) = f (fun x -> x)
64+
65+
let _ = with_id (fun id -> id 4, id "four")
66+
67+
let non_principal1 p f = if p then with_id f else f (fun x -> x)
68+
69+
let non_principal2 p f = if p then f (fun x -> x) else with_id f
70+
71+
let principal1 p (f : ('a. 'a -> 'a) -> 'b) =
72+
if p then f (fun x -> x) else with_id f
73+
74+
let principal2 : bool -> (('a. 'a -> 'a) -> 'b) -> 'b =
75+
fun p f -> if p then f (fun x -> x) else with_id f
76+
77+
type poly = ('a. 'a -> 'a) -> int * string
78+
79+
let principal3 : poly option list = [ None; Some (fun x -> x 5, x "hello") ]
80+
81+
let non_principal3 =
82+
[
83+
(Some (fun x -> x 5, x "hello") : poly option);
84+
Some (fun y -> y 6, y "goodbye");
85+
]
86+
87+
let non_principal4 =
88+
[
89+
Some (fun y -> y 6, y "goodbye");
90+
(Some (fun x -> x 5, x "hello") : poly option);
91+
]
92+
93+
(* Functions with polymorphic parameters are separate from other functions *)
94+
type 'a arg = 'b constraint 'a = 'b -> 'c
95+
type really_poly = (('a. 'a -> 'a) -> string) arg
96+
97+
(* Polymorphic parameters are (mostly) treated as invariant *)
98+
type p1 = ('a. 'a -> 'a) -> int
99+
type p2 = ('a 'b. 'a -> 'b) -> int
100+
101+
let foo (f : p1) : p2 = f
102+
103+
let foo f = (f : p1 :> p2)
104+
105+
module Foo (X : sig
106+
val f : p1
107+
end) : sig
108+
val f : p2
109+
end =
110+
X
111+
112+
let foo (f : p1) : p2 = fun id -> f id
113+
114+
(* Following the existing behaviour for polymorphic methods, you can
115+
subtype from a polymorphic parameter to a monomorphic
116+
parameter. Elsewhere it still behaves as invariant. *)
117+
type p1 = (bool -> bool) -> int
118+
type p2 = ('a. 'a -> 'a) -> int
119+
120+
let foo (x : p1) : p2 = x
121+
122+
let foo x = (x : p1 :> p2)
123+
124+
module Foo (X : sig
125+
val f : p1
126+
end) : sig
127+
val f : p2
128+
end =
129+
X
130+
131+
let foo (f : p1) : p2 = fun id -> f id
132+
133+
class c (f : 'a. 'a -> 'a) =
134+
object
135+
method m = f 0
136+
method n = f "a"
137+
end
138+
139+
class c' (f : 'a. int -> int) =
140+
object
141+
method m = f 0
142+
end
143+
144+
let poly1' ~(id : 'a. 'a -> 'a) = id 3, id "three"
145+
146+
let poly2' ?(id : 'a. 'a -> 'a) = id 3, id "three"
147+
148+
let poly3' ?(id : 'a. int -> int) = id 3
149+
150+
(* This test illustrate a new occurrence of the bug discussed in
151+
https://github.com/ocaml/ocaml/pull/13984*)
152+
153+
module type T = sig
154+
type 'a t = 'a list
155+
end
156+
157+
let rec f (x : (module T)) =
158+
let (module LocalModule) = x in
159+
(assert false : ('a. 'a LocalModule.t) -> unit)
160+
161+
(* The following test requires full translation in the [approx_type] function if
162+
the annotation is partial. *)
163+
let rec f () = g () Fun.id
164+
165+
and g () : ('a. 'a -> 'a) -> unit = fun _ -> ()
166+
167+
let rec f () = g () Fun.id
168+
169+
and g : unit -> ('a. 'a -> 'a) -> unit = fun () _ -> ()
170+
171+
(* Attempts at breaking type_pattern_approx *)
172+
let rec f ([] : 'a. 'a list) = ()
173+
174+
let rec f () : ('a. 'a list) -> unit = fun [] -> ()
175+
176+
(* New expert trick: use 'a. to trigger "exact approximation" *)
177+
let rec f () = g (module Map.Make (Int))
178+
179+
and g (m : (module Map.S)) = ()
180+
181+
let rec f () = g (module Map.Make (Int))
182+
183+
and g (m : 'a. (module Map.S)) = ()
184+
185+
(* Check that we are getting the right behaviour for polymorphic variants
186+
in polymorphic parameters. *)
187+
188+
let poly_poly_var : [< `A | `B ] -> unit = function
189+
| `A -> ()
190+
| `B -> ()
191+
192+
let accept_poly_poly_var (g : 'a. ([< `A | `B ] as 'a) -> unit) = g `A
193+
194+
let () = accept_poly_poly_var poly_poly_var
195+
196+
let f (`B | _) = ()
197+
let h (f : 'a. ([> `A ] as 'a) -> unit) = f `B
198+
let error = h f
199+
200+
let ( let* ) x (id : 'a. 'a -> 'a) = id x, id 1
201+
202+
let ( let* ) (x : 'a. 'a option) (id : 'a. 'a -> 'a) = id x, id 1
203+
204+
let y =
205+
let* x = 3. in
206+
x
207+
208+
let f ((g, x) : 'a. ('a -> int) * 'a) = g 3, g "three"
209+
210+
let f (x : [< `A of ('a. 'a option) -> unit ]) =
211+
match x with
212+
| `A f -> f None
213+
214+
let f : type a. unit -> (a, ('b. 'b -> 'b) -> int) Type.eq -> a =
215+
fun () Equal f -> f 0

0 commit comments

Comments
 (0)