Skip to content

Commit ae69c1c

Browse files
authored
Fix invalid syntax generated for let type a = A in ... (#2783)
* Add more tests for let-struct-items Taken from the compiler's testsuite.
1 parent 1f5bea5 commit ae69c1c

8 files changed

Lines changed: 566 additions & 18 deletions

File tree

CHANGES.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ profile. This started with version 0.26.0.
99
### Highlight
1010

1111
- \* Support OCaml 5.5 syntax
12-
(#2772, #2774, #2775, #2777, #2780, #2781, #2782, @Julow)
12+
(#2772, #2774, #2775, #2777, #2780, #2781, #2782, #2783, @Julow)
1313
The update brings several tiny changes, they are listed below.
1414

1515
- \* Update Odoc's parser to 3.0 (#2757, @Julow)

lib/Fmt_ast.ml

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3472,8 +3472,8 @@ and fmt_class_params c ctx params =
34723472
( wrap_fits_breaks c.conf "[" "]" (list_fl params fmt_param)
34733473
$ space_break ) )
34743474

3475-
and fmt_type_declaration c ?(kw = "") ?(nonrec_kw = "") ?name ?(eq = "=")
3476-
{ast= decl; _} =
3475+
and fmt_type_declaration c ?(pro = noop) ?(kw = "") ?(nonrec_kw = "") ?name
3476+
?(eq = "=") {ast= decl; _} =
34773477
protect c (Td decl)
34783478
@@
34793479
let { ptype_name= {txt; loc}
@@ -3513,7 +3513,7 @@ and fmt_type_declaration c ?(kw = "") ?(nonrec_kw = "") ?name ?(eq = "=")
35133513
in
35143514
let box_manifest k =
35153515
hvbox c.conf.fmt_opts.type_decl_indent.v
3516-
( str kw
3516+
( pro $ str kw
35173517
$ fmt_extension_suffix c ext
35183518
$ fmt_attributes c attrs_before
35193519
$ str nonrec_kw $ str " "
@@ -3901,9 +3901,9 @@ and fmt_module_type c ?(rec_ = false) ({ast= mty; ctx= ctx0} as xmty) =
39013901
| Pmty_with _ ->
39023902
let wcs, mt = Sugar.mod_with (sub_mty ~ctx mty) in
39033903
let fmt_cstr ~first ~last:_ wc =
3904-
let pre = if first then "with" else " and" in
3904+
let pro = if first then str "with" else str " and" in
39053905
fmt_or first space_break cut_break
3906-
$ fmt_with_constraint c ctx ~pre wc
3906+
$ fmt_with_constraint c ctx ~pro wc
39073907
in
39083908
let fmt_cstrs ~first:_ ~last:_ (wcs_and, loc, attr) =
39093909
Cmts.fmt c loc
@@ -4336,28 +4336,28 @@ and fmt_module_statement c ~attributes ?(epi = noop) ?keyword mod_expr =
43364336
$ fmt_item_attributes c ~pre:Blank attrs_after
43374337
$ epi $ doc_after
43384338

4339-
and fmt_with_constraint c ctx ~pre = function
4339+
and fmt_with_constraint c ctx ~pro = function
43404340
| Pwith_type (lid, td) ->
4341-
fmt_type_declaration ~kw:(pre ^ " type") c ~name:lid (sub_td ~ctx td)
4341+
fmt_type_declaration ~pro ~kw:" type" c ~name:lid (sub_td ~ctx td)
43424342
| Pwith_module (m1, m2) ->
4343-
str pre $ str " module " $ fmt_longident_loc c m1 $ str " = "
4343+
pro $ str " module " $ fmt_longident_loc c m1 $ str " = "
43444344
$ fmt_longident_loc c m2
43454345
| Pwith_typesubst (lid, td) ->
4346-
fmt_type_declaration ~kw:(pre ^ " type") c ~eq:":=" ~name:lid
4346+
fmt_type_declaration ~pro ~kw:" type" c ~eq:":=" ~name:lid
43474347
(sub_td ~ctx td)
43484348
| Pwith_modsubst (m1, m2) ->
4349-
str pre $ str " module " $ fmt_longident_loc c m1 $ str " := "
4349+
pro $ str " module " $ fmt_longident_loc c m1 $ str " := "
43504350
$ fmt_longident_loc c m2
43514351
| Pwith_modtype (m1, m2) ->
43524352
let m1 = {m1 with txt= Some (str_longident c m1.txt)} in
43534353
let m2 = Some (sub_mty ~ctx m2) in
4354-
str pre $ break 1 2
4354+
pro $ break 1 2
43554355
$ fmt_module c ctx (str "module type") m1 [] None ~rec_flag:false m2
43564356
~attrs:Ast_helper.Attr.empty_ext_attrs
43574357
| Pwith_modtypesubst (m1, m2) ->
43584358
let m1 = {m1 with txt= Some (str_longident c m1.txt)} in
43594359
let m2 = Some (sub_mty ~ctx m2) in
4360-
str pre $ break 1 2
4360+
pro $ break 1 2
43614361
$ fmt_module c ctx ~eqty:":=" (str "module type") m1 [] None
43624362
~rec_flag:false m2 ~attrs:Ast_helper.Attr.empty_ext_attrs
43634363

@@ -4624,17 +4624,19 @@ and fmt_structure c ctx itms =
46244624
let ast (x, _) = Str x in
46254625
fmt_item_list c ctx update_config ast fmt_item itms
46264626

4627-
and fmt_type c ?eq rec_flag decls ctx =
4627+
and fmt_type c ?pro ?(epi = noop) ?eq rec_flag decls ctx =
46284628
let update_config c td = update_config_attrs c td.ptype_attributes in
46294629
let is_rec = Asttypes.is_recursive rec_flag in
4630-
let fmt_decl c ctx ~prev ~next:_ decl =
4631-
let first = Option.is_none prev in
4630+
let fmt_decl c ctx ~prev ~next decl =
4631+
let first = Option.is_none prev and last = Option.is_none next in
46324632
let kw, nonrec_kw =
46334633
if first then
46344634
if is_rec then ("type", None) else ("type", Some " nonrec")
46354635
else ("and", None)
46364636
in
4637-
fmt_type_declaration c ~kw ?nonrec_kw ?eq (sub_td ~ctx decl)
4637+
let pro = if first then pro else None in
4638+
fmt_type_declaration c ?pro ~kw ?nonrec_kw ?eq (sub_td ~ctx decl)
4639+
$ fmt_if last epi
46384640
in
46394641
let ast x = Td x in
46404642
fmt_item_list c ctx update_config ast fmt_decl decls
@@ -4700,7 +4702,7 @@ and fmt_structure_item' ~ctx0 c ~last:last_item ~semisemi ~pro ?epi ~ctx si =
47004702
fmt_recmodule c ctx mbs fmt_module_binding ~pro ?epi
47014703
(fun x -> Mb (ctx, x))
47024704
sub_mb
4703-
| Pstr_type (rec_flag, decls) -> fmt_type c rec_flag decls ctx
4705+
| Pstr_type (rec_flag, decls) -> fmt_type c ~pro ?epi rec_flag decls ctx
47044706
| Pstr_typext te -> fmt_type_extension c ctx ~pro ?epi te
47054707
| Pstr_value {pvbs_rec= rec_flag; pvbs_bindings= bindings} ->
47064708
let update_config c i =

test/passing/gen/dune.inc

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3779,6 +3779,24 @@
37793779
(package ocamlformat)
37803780
(action (diff let_punning.ml.err let_punning.ml.stderr)))
37813781

3782+
(rule
3783+
(deps .ocamlformat)
3784+
(package ocamlformat)
3785+
(action
3786+
(with-stdout-to let_struct_item.ml.stdout
3787+
(with-stderr-to let_struct_item.ml.stderr
3788+
(run %{bin:ocamlformat} --name let_struct_item.ml --margin-check %{dep:../tests/let_struct_item.ml})))))
3789+
3790+
(rule
3791+
(alias runtest)
3792+
(package ocamlformat)
3793+
(action (diff let_struct_item.ml.ref let_struct_item.ml.stdout)))
3794+
3795+
(rule
3796+
(alias runtest)
3797+
(package ocamlformat)
3798+
(action (diff let_struct_item.ml.err let_struct_item.ml.stderr)))
3799+
37823800
(rule
37833801
(deps .ocamlformat)
37843802
(package ocamlformat)
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
let () =
2+
()
3+
|> let type u = A in
4+
f ~y:0
5+
6+
let _ =
7+
let type t = A in
8+
A
9+
10+
let _ =
11+
let type t = .. in
12+
let type t += A in
13+
A
14+
15+
type u = ..
16+
17+
let _ =
18+
let type u += A in
19+
A
20+
21+
let _ =
22+
let class c =
23+
object
24+
method f = 12
25+
end in
26+
new c
27+
28+
let _ =
29+
let external f : 'a -> 'a = "%identity" in
30+
f
31+
32+
let _ =
33+
let type t =
34+
| A of int
35+
| B
36+
in
37+
let _ = [ A 42; B ] in
38+
let type t = .. in
39+
let type t += A of string in
40+
let _ = A "hello" in
41+
let class c =
42+
object
43+
method f = 42
44+
end in
45+
let class type ct = object
46+
method f : int
47+
end in
48+
let class d : ct =
49+
object (self)
50+
inherit c
51+
initializer print_int self#f
52+
end in
53+
let external f : 'a -> 'a = "%identity" in
54+
let [@@@warning "-unused-var"] in
55+
let v = 42, 12 in
56+
assert (f v == v);
57+
"OK"
58+
59+
(* PR#14554, a regression reported by Antonio Monteiro.
60+
(The regressions or fixes are after 5.4, which is the last release
61+
without the generic [Pexp_struct_item] typing rules of #13839).
62+
63+
In each example below, we expect the inferred type
64+
{[
65+
val dog : < bark : 'this -> unit > t as 'this
66+
]}
67+
where the ['this] variable has been generalized,
68+
it is not a weak variable like ['_this].
69+
*)
70+
71+
type 'a t
72+
73+
(* This was correct in OCaml 5.4,
74+
and was temporarily broken by #13839. *)
75+
let dog : 'this =
76+
let module Dog = struct
77+
external make : bark:('self -> unit) -> < bark : 'self -> unit > t
78+
= "%identity"
79+
end in
80+
Dog.make ~bark:(fun (o : 'this) -> ())
81+
82+
(* This variant from Samuel Vivien would also
83+
suffer from the same regression. *)
84+
let dog : 'this =
85+
let external make : bark:('self -> unit) -> < bark : 'self -> unit > t
86+
= "%identity"
87+
in
88+
make ~bark:(fun (o : 'this) -> ())
89+
90+
(* This variant from Gabriel Scherer was already wrong in OCaml 5.4,
91+
and has been fixed at the same time as the other two. *)
92+
let dog : 'this =
93+
let open struct
94+
external make : bark:('self -> unit) -> < bark : 'self -> unit > t
95+
= "%identity"
96+
end in
97+
make ~bark:(fun (o : 'this) -> ())
98+
99+
(* </end of #14554> *)
100+
101+
let _ =
102+
let type t =
103+
| Foooooooooooooooooooooooooooooooooooooooooooooooooo
104+
| Baaaaaaaaaaaaaaaaaaaaaaaar
105+
in
106+
let module rec Foooooooooooooooooooooooooooooooooooooooooooooooooooooooo =
107+
struct end
108+
in
109+
()
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
let () =
2+
()
3+
|> let type u = A in
4+
f ~y:0
5+
6+
let _ =
7+
let type t = A in
8+
A
9+
10+
let _ =
11+
let type t = .. in
12+
let type t += A in
13+
A
14+
15+
type u = ..
16+
17+
let _ =
18+
let type u += A in
19+
A
20+
21+
let _ =
22+
let class c =
23+
object
24+
method f = 12
25+
end in
26+
new c
27+
28+
let _ =
29+
let external f : 'a -> 'a = "%identity" in
30+
f
31+
32+
let _ =
33+
let type t = A of int | B in
34+
let _ = [ A 42; B ] in
35+
let type t = .. in
36+
let type t += A of string in
37+
let _ = A "hello" in
38+
let class c =
39+
object
40+
method f = 42
41+
end in
42+
let class type ct = object
43+
method f : int
44+
end in
45+
let class d : ct =
46+
object (self)
47+
inherit c
48+
initializer print_int self#f
49+
end in
50+
let external f : 'a -> 'a = "%identity" in
51+
let [@@@warning "-unused-var"] in
52+
let v = (42, 12) in
53+
assert (f v == v);
54+
"OK"
55+
56+
(* PR#14554, a regression reported by Antonio Monteiro.
57+
(The regressions or fixes are after 5.4, which is the last release
58+
without the generic [Pexp_struct_item] typing rules of #13839).
59+
60+
In each example below, we expect the inferred type
61+
{[
62+
val dog : < bark : 'this -> unit > t as 'this
63+
]}
64+
where the ['this] variable has been generalized,
65+
it is not a weak variable like ['_this].
66+
*)
67+
68+
type 'a t
69+
70+
(* This was correct in OCaml 5.4,
71+
and was temporarily broken by #13839. *)
72+
let dog : 'this =
73+
let module Dog = struct
74+
external make : bark:('self -> unit) -> < bark : 'self -> unit > t
75+
= "%identity"
76+
end in
77+
Dog.make ~bark:(fun (o : 'this) -> ())
78+
79+
(* This variant from Samuel Vivien would also
80+
suffer from the same regression. *)
81+
let dog : 'this =
82+
let external make : bark:('self -> unit) -> < bark : 'self -> unit > t
83+
= "%identity"
84+
in
85+
make ~bark:(fun (o : 'this) -> ())
86+
87+
(* This variant from Gabriel Scherer was already wrong in OCaml 5.4,
88+
and has been fixed at the same time as the other two. *)
89+
let dog : 'this =
90+
let open struct
91+
external make : bark:('self -> unit) -> < bark : 'self -> unit > t
92+
= "%identity"
93+
end in
94+
make ~bark:(fun (o : 'this) -> ())
95+
96+
(* </end of #14554> *)
97+
98+
let _ =
99+
let type t =
100+
| Foooooooooooooooooooooooooooooooooooooooooooooooooo
101+
| Baaaaaaaaaaaaaaaaaaaaaaaar
102+
in
103+
let module rec Foooooooooooooooooooooooooooooooooooooooooooooooooooooooo =
104+
struct end
105+
in
106+
()

0 commit comments

Comments
 (0)