1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
open! Import
open Ast_builder.Default
module Buffer = Caml.Buffer
module Format = Caml.Format
let lident x = Longident.Lident x
let core_type_of_type_declaration td =
let loc = td.ptype_name.loc in
ptyp_constr ~loc
(Located.map lident td.ptype_name)
(List.map td.ptype_params ~f:fst)
let strip_gen_symbol_suffix =
let chop n ~or_more string pos f =
let target = !pos - n in
while !pos > 0 && (or_more || !pos > target) && f string.[!pos - 1] do
pos := !pos - 1
done;
!pos <= target
in
fun string ->
let pos = ref (String.length string) in
if
chop 1 ~or_more:false string pos (Char.equal '_')
&& chop 3 ~or_more:true string pos (function
| '0' .. '9' -> true
| _ -> false)
&& chop 2 ~or_more:false string pos (Char.equal '_')
then String.prefix string !pos
else string
let gen_symbol =
let cnt = ref 0 in
fun ?(prefix = "_x") () ->
cnt := !cnt + 1;
let prefix = strip_gen_symbol_suffix prefix in
Printf.sprintf "%s__%03i_" prefix !cnt
let name_type_params_in_td (td : type_declaration) : type_declaration =
let prefix_string i =
String.make ((i / 26) + 1) (Char.chr (Char.code 'a' + (i mod 26)))
in
let name_param i (tp, variance) =
let ptyp_desc =
match tp.ptyp_desc with
| Ptyp_any -> Ptyp_var (gen_symbol ~prefix:(prefix_string i) ())
| Ptyp_var _ as v -> v
| _ -> Location.raise_errorf ~loc:tp.ptyp_loc "not a type parameter"
in
({ tp with ptyp_desc }, variance)
in
{ td with ptype_params = List.mapi td.ptype_params ~f:name_param }
let combinator_type_of_type_declaration td ~f =
let td = name_type_params_in_td td in
let result_type =
f ~loc:td.ptype_name.loc (core_type_of_type_declaration td)
in
List.fold_right td.ptype_params ~init:result_type
~f:(fun (tp, _variance) acc ->
let loc = tp.ptyp_loc in
ptyp_arrow ~loc Nolabel (f ~loc tp) acc)
let string_of_core_type ct =
let buf = Buffer.create 128 in
let ppf = Format.formatter_of_buffer buf in
Pprintast.core_type ppf ct;
Format.pp_print_flush ppf ();
Buffer.contents buf
let get_type_param_name (ty, _) =
let loc = ty.ptyp_loc in
match ty.ptyp_desc with
| Ptyp_var name -> Located.mk ~loc name
| _ -> Location.raise_errorf ~loc "not a type parameter"
exception Type_is_recursive
class type_is_recursive rec_flag tds =
object (self)
inherit Ast_traverse.iter as super
val type_names : string list = List.map tds ~f:(fun td -> td.ptype_name.txt)
method return_true () = raise_notrace Type_is_recursive
method! core_type ctype =
match ctype.ptyp_desc with
| Ptyp_arrow _ -> ()
| Ptyp_constr ({ txt = Longident.Lident id; _ }, _)
when List.mem ~set:type_names id ->
self#return_true ()
| _ -> super#core_type ctype
method! constructor_declaration cd =
match cd.pcd_args with
| Pcstr_tuple args -> List.iter args ~f:self#core_type
| Pcstr_record fields -> List.iter fields ~f:self#label_declaration
method! attributes _ =
()
method go () =
match rec_flag with
| Nonrecursive -> Nonrecursive
| Recursive -> (
match List.iter tds ~f:self#type_declaration with
| exception Type_is_recursive -> Recursive
| () -> Nonrecursive)
end
let really_recursive rec_flag tds = (new type_is_recursive rec_flag tds)#go ()
let rec last x l = match l with [] -> x | x :: l -> last x l
let loc_of_name_and_payload name payload =
match payload with
| PStr [] -> name.loc
| PStr (x :: l) -> { x.pstr_loc with loc_end = (last x l).pstr_loc.loc_end }
| PSig [] -> name.loc
| PSig (x :: l) -> { x.psig_loc with loc_end = (last x l).psig_loc.loc_end }
| PTyp t -> t.ptyp_loc
| PPat (x, None) -> x.ppat_loc
| PPat (x, Some e) -> { x.ppat_loc with loc_end = e.pexp_loc.loc_end }
let loc_of_payload { attr_name; attr_payload; attr_loc = _ } =
loc_of_name_and_payload attr_name attr_payload
let loc_of_attribute { attr_name; attr_payload; attr_loc = _ } =
if Poly.( = ) attr_name.loc Location.none then
loc_of_name_and_payload attr_name attr_payload
else
{
attr_name.loc with
loc_end = (loc_of_name_and_payload attr_name attr_payload).loc_end;
}
let loc_of_extension (name, payload) =
if Poly.( = ) name.loc Location.none then loc_of_name_and_payload name payload
else
{ name.loc with loc_end = (loc_of_name_and_payload name payload).loc_end }
let curry_applications expr =
let open Ast_builder_generated.M in
match expr.pexp_desc with
| Pexp_apply (f, orig_forward_args) ->
let loc = expr.pexp_loc in
let rec loop = function
| [] -> f
| last_arg :: rev_front_args ->
pexp_apply ~loc (loop rev_front_args) [ last_arg ]
in
loop (List.rev orig_forward_args)
| _ -> expr
let rec assert_no_attributes = function
| [] -> ()
| { attr_name = name; attr_loc = _; attr_payload = _ } :: rest
when Name.ignore_checks name.Location.txt ->
assert_no_attributes rest
| attr :: _ ->
let loc = loc_of_attribute attr in
Location.raise_errorf ~loc "Attributes not allowed here"
let assert_no_attributes_in =
object
inherit Ast_traverse.iter
method! attribute a = assert_no_attributes [ a ]
end
let attribute_of_warning loc s =
{
attr_name = { loc; txt = "ocaml.ppwarning" };
attr_payload = PStr [ pstr_eval ~loc (estring ~loc s) [] ];
attr_loc = loc;
}
let is_polymorphic_variant =
let rec check = function
| { ptyp_desc = Ptyp_variant _; _ } -> `Definitely
| { ptyp_desc = Ptyp_alias (typ, _); _ } -> check typ
| { ptyp_desc = Ptyp_constr _; _ } -> `Maybe
| _ -> `Surely_not
in
fun td ~sig_ ->
match td.ptype_kind with
| Ptype_variant _ | Ptype_record _ | Ptype_open -> `Surely_not
| Ptype_abstract -> (
match td.ptype_manifest with
| None -> if sig_ then `Maybe else `Surely_not
| Some typ -> check typ)
let mk_named_sig ~loc ~sg_name ~handle_polymorphic_variant = function
| [ td ]
when String.equal td.ptype_name.txt "t" && List.is_empty td.ptype_cstrs ->
if
(not handle_polymorphic_variant)
&& Poly.( = ) (is_polymorphic_variant td ~sig_:true) `Definitely
then None
else
let arity = List.length td.ptype_params in
if arity >= 4 then None
else
let mty =
if arity = 0 then sg_name else Printf.sprintf "%s%d" sg_name arity
in
let td = name_type_params_in_td td in
let for_subst =
Ast_helper.Type.mk ~loc td.ptype_name ~params:td.ptype_params
~manifest:
(ptyp_constr ~loc
(Located.map_lident td.ptype_name)
(List.map ~f:fst td.ptype_params))
in
Some
(include_infos ~loc
(pmty_with ~loc
(pmty_ident ~loc (Located.lident mty ~loc))
[ Pwith_typesubst (Located.lident ~loc "t", for_subst) ]))
| _ -> None