First stage of constraints refactoring: bounds environment
[hiphop-php.git] / hphp / hack / src / typing / typing_print.ml
blob7b8f5d72b7b1e540afbdfeb5ddddf8447deb4658
1 (**
2 * Copyright (c) 2015, Facebook, Inc.
3 * All rights reserved.
5 * This source code is licensed under the BSD-style license found in the
6 * LICENSE file in the "hack" directory of this source tree. An additional grant
7 * of patent rights can be found in the PATENTS file in the same directory.
9 *)
12 (*****************************************************************************)
13 (* Pretty printing of types *)
14 (*****************************************************************************)
16 open Core
17 open Typing_defs
18 open Utils
20 module SN = Naming_special_names
21 module Reason = Typing_reason
22 (*****************************************************************************)
23 (* Computes the string representing a type in an error message.
24 * We generally don't want to show the whole type. If an error was due
25 * because something is a Vector instead of an int, we don't want to show
26 * the type Vector<Vector<array<int>>> because it could be misleading.
27 * The error is due to the fact that it is a Vector, regardless of the
28 * type parameters.
30 (*****************************************************************************)
32 module ErrorString = struct
34 let tprim = function
35 | Nast.Tvoid -> "void"
36 | Nast.Tint -> "an int"
37 | Nast.Tbool -> "a bool"
38 | Nast.Tfloat -> "a float"
39 | Nast.Tstring -> "a string"
40 | Nast.Tnum -> "a num (int/float)"
41 | Nast.Tresource -> "a resource"
42 | Nast.Tarraykey -> "an array key (int/string)"
43 | Nast.Tnoreturn -> "noreturn (throws or exits)"
45 let rec type_: type a. a ty_ -> _ = function
46 | Tany -> "an untyped value"
47 | Tunresolved l -> unresolved l
48 | Tarray (x, y) -> array (x, y)
49 | Tarraykind AKempty -> array (None, None)
50 | Tarraykind AKany -> array (None, None)
51 | Tarraykind (AKvec x)
52 -> array (Some x, None)
53 | Tarraykind (AKmap (x, y))
54 -> array (Some x, Some y)
55 | Tarraykind (AKshape _)
56 -> "an array (used like a shape)"
57 | Tarraykind (AKtuple _)
58 -> "an array (used like a tuple)"
59 | Ttuple _ -> "a tuple"
60 | Tmixed -> "a mixed value"
61 | Toption _ -> "a nullable type"
62 | Tprim tp -> tprim tp
63 | Tvar _ -> "some value"
64 | Tanon _ -> "a function"
65 | Tfun _ -> "a function"
66 | Tgeneric (x, _) -> "a value of declared generic type "^x
67 | Tabstract (AKnewtype (x, _), _)
68 when x = SN.Classes.cClassname -> "a classname string"
69 | Tabstract (AKnewtype (x, _), _)
70 when x = SN.Classes.cTypename -> "a typename string"
71 | Tabstract (ak, cstr) -> abstract ak cstr
72 | Tclass ((_, x), _) -> "an object of type "^(strip_ns x)
73 | Tapply ((_, x), _)
74 when x = SN.Classes.cClassname -> "a classname string"
75 | Tapply ((_, x), _)
76 when x = SN.Classes.cTypename -> "a typename string"
77 | Tapply ((_, x), _) -> "an object of type "^(strip_ns x)
78 | Tobject -> "an object"
79 | Tshape _ -> "a shape"
80 | Taccess (root_ty, ids) -> tconst root_ty ids
81 | Tthis -> "the type 'this'"
83 and array: type a. a ty option * a ty option -> _ = function
84 | None, None -> "an array"
85 | Some _, None -> "an array (used like a vector)"
86 | Some _, Some _ -> "an array (used like a hashtable)"
87 | _ -> assert false
89 and abstract ak cstr =
90 let x = strip_ns @@ AbstractKind.to_string ak in
91 match ak, cstr with
92 | AKnewtype (_, _), _ -> "an object of type "^x
93 | AKenum _, _ -> "a value of "^x
94 | AKgeneric _, _ -> "a value of generic type "^x
95 | AKdependent (`cls c, []), Some (_, ty) ->
96 type_ ty^" (known to be exactly the class '"^strip_ns c^"')"
97 | AKdependent ((`static | `expr _), _), _ ->
98 "the expression dependent type "^x
99 | AKdependent (_, _::_), _ -> "the abstract type constant "^x
100 | AKdependent _, _ ->
101 "the type '"^x^"'"
102 ^Option.value_map cstr ~default:""
103 ~f:(fun (_, ty) -> "\n that is compatible with "^type_ ty)
105 and unresolved l =
106 let l = List.map l snd in
107 let l = List.map l type_ in
108 let s = List.fold_right l ~f:SSet.add ~init:SSet.empty in
109 let l = SSet.elements s in
110 unresolved_ l
112 and unresolved_ = function
113 | [] -> "an undefined value"
114 | [x] -> x
115 | x :: rl -> x^" or "^unresolved_ rl
117 and tconst: type a. a ty -> _ -> _ = fun root_ty ids ->
118 let f x =
119 let x =
120 if String.contains x '<'
121 then "this"
122 else x
124 List.fold_left ~f:(fun acc (_, sid) -> acc^"::"^sid)
125 ~init:("the type constant "^strip_ns x) ids in
126 match snd root_ty with
127 | Tgeneric (x, _) -> f x
128 | Tapply ((_, x), _) -> f x
129 | Tclass ((_, x), _) -> f x
130 | Tabstract (ak, _) -> f @@ AbstractKind.to_string ak
131 | Taccess _ as x ->
132 List.fold_left ~f:(fun acc (_, sid) -> acc^"::"^sid)
133 ~init:(type_ x) ids
134 | _ ->
135 "a type constant"
139 (*****************************************************************************)
140 (* Module used to "suggest" types.
141 * When a type is missing, it is nice to suggest a type to the user.
142 * However, there are some cases where parts of the type is still unresolved.
143 * When that is the case, we print '...' and let the user replace the missing
144 * parts with a real type. So if we inferred that something was a Vector,
145 * but we didn't manage to infer the type of the elements, the output becomes:
146 * Vector<...>.
148 (*****************************************************************************)
150 module Suggest = struct
152 let rec type_: type a. a ty -> string = fun (_, ty) ->
153 match ty with
154 | Tarray _ -> "array"
155 | Tarraykind _ -> "array"
156 | Tthis -> SN.Typehints.this
157 | Tunresolved _ -> "..."
158 | Ttuple (l) -> "("^list l^")"
159 | Tany -> "..."
160 | Tmixed -> "mixed"
161 | Tgeneric (s, _) -> s
162 | Tabstract (AKgeneric s, _) -> s
163 | Toption ty -> "?" ^ type_ ty
164 | Tprim tp -> prim tp
165 | Tvar _ -> "..."
166 | Tanon _ -> "..."
167 | Tfun _ -> "..."
168 | Tapply ((_, cid), []) -> Utils.strip_ns cid
169 | Tapply ((_, cid), [x]) -> (Utils.strip_ns cid)^"<"^type_ x^">"
170 | Tapply ((_, cid), l) -> (Utils.strip_ns cid)^"<"^list l^">"
171 | Tclass ((_, cid), []) -> Utils.strip_ns cid
172 | Tabstract ((AKnewtype (cid, []) | AKenum cid), _) -> Utils.strip_ns cid
173 | Tclass ((_, cid), [x]) -> (Utils.strip_ns cid)^"<"^type_ x^">"
174 | Tabstract (AKnewtype (cid, [x]), _) ->
175 (Utils.strip_ns cid)^"<"^type_ x^">"
176 | Tclass ((_, cid), l) -> (Utils.strip_ns cid)^"<"^list l^">"
177 | Tabstract (AKnewtype (cid, l), _) ->
178 (Utils.strip_ns cid)^"<"^list l^">"
179 | Tabstract (AKdependent (_, _), _) -> "..."
180 | Tobject -> "..."
181 | Tshape _ -> "..."
182 | Taccess (root_ty, ids) ->
183 let x =
184 match snd root_ty with
185 | Tapply ((_, x), _) -> Some x
186 | Tthis -> Some SN.Typehints.this
187 | _ -> None in
188 (match x with
189 | None -> "..."
190 | Some x ->
191 List.fold_left ids
192 ~f:(fun acc (_, sid) -> acc^"::"^sid)
193 ~init:(strip_ns x)
196 and list: type a. a ty list -> string = function
197 | [] -> ""
198 | [x] -> type_ x
199 | x :: rl -> type_ x ^ ", "^ list rl
201 and prim = function
202 | Nast.Tvoid -> "void"
203 | Nast.Tint -> "int"
204 | Nast.Tbool -> "bool"
205 | Nast.Tfloat -> "float"
206 | Nast.Tstring -> "string"
207 | Nast.Tnum -> "num (int/float)"
208 | Nast.Tresource -> "resource"
209 | Nast.Tarraykey -> "arraykey (int/string)"
210 | Nast.Tnoreturn -> "noreturn"
214 (*****************************************************************************)
215 (* Pretty-printer of the "full" type. *)
216 (*****************************************************************************)
218 module Full = struct
219 module Env = Typing_env
221 let rec list_sep o s f l =
222 match l with
223 | [] -> ()
224 | [x] -> f x
225 | x :: rl -> f x; o s; list_sep o s f rl
227 let rec ty: type a. _ -> _ -> _ -> a ty -> _ =
228 fun st env o (_, x) -> ty_ st env o x
230 and ty_: type a. _ -> _ -> _ -> a ty_ -> _ =
231 fun st env o x ->
232 let k: type b. b ty -> _ = fun x -> ty st env o x in
233 let list: type c. (c ty -> unit) -> c ty list -> _ =
234 fun x y -> list_sep o ", " x y in
235 match x with
236 | Tany -> o "_"
237 | Tthis -> o SN.Typehints.this
238 | Tmixed -> o "mixed"
239 | Tarraykind AKany -> o "array"
240 | Tarraykind AKempty -> o "array"
241 | Tarray (None, None) -> o "array"
242 | Tarraykind (AKvec x) -> o "array<"; k x; o ">"
243 | Tarray (Some x, None) -> o "array<"; k x; o ">"
244 | Tarray (Some x, Some y) -> o "array<"; k x; o ", "; k y; o ">"
245 | Tarraykind (AKmap (x, y)) -> o "array<"; k x; o ", "; k y; o ">"
246 | Tarraykind (AKshape _) -> o "[shape-like array]"
247 | Tarraykind (AKtuple _) -> o "[tuple-like array]"
248 | Tarray (None, Some _) -> assert false
249 | Tclass ((_, s), []) -> o s
250 | Tapply ((_, s), []) -> o s
251 | Tgeneric (s, _) -> o s
252 | Taccess (root_ty, ids) ->
253 k root_ty;
254 o (List.fold_left ids
255 ~f:(fun acc (_, sid) -> acc ^ "::" ^ sid) ~init:"")
256 | Toption x -> o "?"; k x
257 | Tprim x -> prim o x
258 | Tvar n when ISet.mem n st -> o "[rec]"
259 | Tvar n ->
260 let _, ety = Env.expand_type env (Reason.Rnone, x) in
261 let st = ISet.add n st in
262 ty st env o ety
263 | Tfun ft ->
264 if ft.ft_abstract then o "abs " else ();
265 o "(function"; fun_type st env o ft; o ")";
266 (match ft.ft_ret with
267 | (Reason.Rdynamic_yield _, _) -> o " [DynamicYield]"
268 | _ -> ())
269 | Tclass ((_, s), tyl) -> o s; o "<"; list k tyl; o ">"
270 | Tabstract (AKnewtype (s, []), _) -> o s
271 | Tabstract (AKnewtype (s, tyl), _) -> o s; o "<"; list k tyl; o ">"
272 | Tabstract (ak, _) -> o @@ AbstractKind.to_string ak;
273 (* Don't strip_ns here! We want the FULL type, including the initial slash.
275 | Tapply ((_, s), tyl) -> o s; o "<"; list k tyl; o ">"
276 | Ttuple tyl -> o "("; list k tyl; o ")"
277 | Tanon _ -> o "[fun]"
278 | Tunresolved tyl -> list_sep o " | " k tyl
279 | Tobject -> o "object"
280 | Tshape _ -> o "[shape]"
282 and prim o x =
283 o (match x with
284 | Nast.Tvoid -> "void"
285 | Nast.Tint -> "int"
286 | Nast.Tbool -> "bool"
287 | Nast.Tfloat -> "float"
288 | Nast.Tstring -> "string"
289 | Nast.Tnum -> "num"
290 | Nast.Tresource -> "resource"
291 | Nast.Tarraykey -> "arraykey"
292 | Nast.Tnoreturn -> "noreturn"
295 and fun_type: type a. _ -> _ -> _ -> a fun_type -> _ =
296 fun st env o ft ->
297 (match ft.ft_tparams, ft.ft_arity with
298 | [], Fstandard _ -> ()
299 | [], _ -> o "<...>";
300 | l, Fstandard _ -> o "<"; list_sep o ", " (tparam o) l; o ">"
301 | l, _ -> o "<"; list_sep o ", " (tparam o) l; o "..."; o ">"
303 o "("; list_sep o ", " (fun_param st env o) ft.ft_params; o "): ";
304 ty st env o ft.ft_ret
306 and fun_param: type a. _ -> _ -> _ -> a fun_param -> _ =
307 fun st env o (param_name, param_type) ->
308 match param_name, param_type with
309 | None, _ -> ty st env o param_type
310 | Some param_name, (_, Tany) -> o param_name
311 | Some param_name, param_type ->
312 ty st env o param_type; o " "; o param_name
314 and tparam: type a. _ -> a tparam -> _ =
315 fun o (_, (_, x), _) -> o x
317 let to_string env x =
318 let buf = Buffer.create 50 in
319 ty ISet.empty env (Buffer.add_string buf) x;
320 Buffer.contents buf
322 let to_string_strip_ns env x =
323 let buf = Buffer.create 50 in
324 let add_string str =
325 let str = Utils.strip_ns str in
326 Buffer.add_string buf str
328 ty ISet.empty env add_string x;
329 Buffer.contents buf
331 let to_string_decl (x: decl ty) =
332 let env =
333 Typing_env.empty TypecheckerOptions.default Relative_path.default
334 ~droot:None in
335 to_string env x
338 (*****************************************************************************)
339 (* Prints the internal type of a class, this code is meant to be used for
340 * debugging purposes only.
342 (*****************************************************************************)
344 module PrintClass = struct
346 let indent = " "
347 let bool = string_of_bool
348 let sset s =
349 let contents = SSet.fold (fun x acc -> x^" "^acc) s "" in
350 Printf.sprintf "Set( %s)" contents
352 let pos p =
353 let line, start, end_ = Pos.info_pos p in
354 Printf.sprintf "(line %d: chars %d-%d)" line start end_
356 let class_kind = function
357 | Ast.Cabstract -> "Cabstract"
358 | Ast.Cnormal -> "Cnormal"
359 | Ast.Cinterface -> "Cinterface"
360 | Ast.Ctrait -> "Ctrait"
361 | Ast.Cenum -> "Cenum"
363 let constraint_ty_opt = function
364 | None -> ""
365 | Some (Ast.Constraint_as, ty) -> "as " ^ (Full.to_string_decl ty)
366 | Some (Ast.Constraint_super, ty) -> "super " ^ (Full.to_string_decl ty)
368 let variance = function
369 | Ast.Covariant -> "+"
370 | Ast.Contravariant -> "-"
371 | Ast.Invariant -> ""
373 let tparam (var, (position, name), cstr_opt) =
374 variance var^pos position^" "^name^" "^
375 (constraint_ty_opt cstr_opt)
377 let tparam_list l =
378 List.fold_right l ~f:(fun x acc -> tparam x^", "^acc) ~init:""
380 let class_elt ce =
381 let vis =
382 match ce.ce_visibility with
383 | Vpublic -> "public"
384 | Vprivate _ -> "private"
385 | Vprotected _ -> "protected"
387 let synth = (if ce.ce_synthesized then "synthetic " else "") in
388 let type_ = Full.to_string_decl ce.ce_type in
389 synth^vis^" "^type_
391 let class_elt_smap m =
392 SMap.fold begin fun field v acc ->
393 "("^field^": "^class_elt v^") "^acc
394 end m ""
396 let class_elt_smap_with_breaks m =
397 SMap.fold begin fun field v acc ->
398 "\n"^indent^field^": "^(class_elt v)^acc
399 end m ""
401 let class_const_smap m =
402 SMap.fold begin fun field cc acc ->
403 let synth = if cc.cc_synthesized then "synthetic " else "" in
404 "("^field^": "^synth^Full.to_string_decl cc.cc_type^") "^acc
405 end m ""
407 let typeconst {
408 ttc_name = tc_name;
409 ttc_constraint = tc_constraint;
410 ttc_type = tc_type;
411 ttc_origin = origin;
413 let name = snd tc_name in
414 let ty x = Full.to_string_decl x in
415 let constraint_ =
416 match tc_constraint with
417 | None -> ""
418 | Some x -> " as "^ty x
420 let type_ =
421 match tc_type with
422 | None -> ""
423 | Some x -> " = "^ty x
425 name^constraint_^type_^" (origin:"^origin^")"
427 let typeconst_smap m =
428 SMap.fold begin fun _ v acc ->
429 "\n("^(typeconst v)^")"^acc
430 end m ""
432 let ancestors_smap tcopt m =
433 (* Format is as follows:
434 * ParentKnownToHack
435 * ! ParentCompletelyUnknown
436 * ~ ParentPartiallyKnown (interface|abstract|trait)
438 * ParentPartiallyKnown must inherit one of the ! Unknown parents, so that
439 * sigil could be omitted *)
440 SMap.fold begin fun field v acc ->
441 let sigil, kind = match Typing_lazy_heap.get_class tcopt field with
442 | None -> "!", ""
443 | Some {tc_members_fully_known; tc_kind; _} ->
444 (if tc_members_fully_known then " " else "~"),
445 " ("^class_kind tc_kind^")"
447 let ty_str = Full.to_string_decl v in
448 "\n"^indent^sigil^" "^ty_str^kind^acc
449 end m ""
451 let constructor (ce_opt, consist) =
452 let consist_str = if consist then " (consistent in hierarchy)" else "" in
453 let ce_str = match ce_opt with
454 | None -> ""
455 | Some ce -> class_elt ce
456 in ce_str^consist_str
458 let req_ancestors xs =
459 List.fold_left xs ~init:"" ~f:begin fun acc (_p, x) ->
460 acc ^ Full.to_string_decl x ^ ", "
463 let class_type tcopt c =
464 let tc_need_init = bool c.tc_need_init in
465 let tc_members_fully_known = bool c.tc_members_fully_known in
466 let tc_abstract = bool c.tc_abstract in
467 let tc_deferred_init_members = sset c.tc_deferred_init_members in
468 let tc_kind = class_kind c.tc_kind in
469 let tc_name = c.tc_name in
470 let tc_tparams = tparam_list c.tc_tparams in
471 let tc_consts = class_const_smap c.tc_consts in
472 let tc_typeconsts = typeconst_smap c.tc_typeconsts in
473 let tc_props = class_elt_smap c.tc_props in
474 let tc_sprops = class_elt_smap c.tc_sprops in
475 let tc_methods = class_elt_smap_with_breaks c.tc_methods in
476 let tc_smethods = class_elt_smap_with_breaks c.tc_smethods in
477 let tc_construct = constructor c.tc_construct in
478 let tc_ancestors = ancestors_smap tcopt c.tc_ancestors in
479 let tc_req_ancestors = req_ancestors c.tc_req_ancestors in
480 let tc_req_ancestors_extends = sset c.tc_req_ancestors_extends in
481 let tc_extends = sset c.tc_extends in
482 "tc_need_init: "^tc_need_init^"\n"^
483 "tc_members_fully_known: "^tc_members_fully_known^"\n"^
484 "tc_abstract: "^tc_abstract^"\n"^
485 "tc_deferred_init_members: "^tc_deferred_init_members^"\n"^
486 "tc_kind: "^tc_kind^"\n"^
487 "tc_name: "^tc_name^"\n"^
488 "tc_tparams: "^tc_tparams^"\n"^
489 "tc_consts: "^tc_consts^"\n"^
490 "tc_typeconsts: "^tc_typeconsts^"\n"^
491 "tc_props: "^tc_props^"\n"^
492 "tc_sprops: "^tc_sprops^"\n"^
493 "tc_methods: "^tc_methods^"\n"^
494 "tc_smethods: "^tc_smethods^"\n"^
495 "tc_construct: "^tc_construct^"\n"^
496 "tc_ancestors: "^tc_ancestors^"\n"^
497 "tc_extends: "^tc_extends^"\n"^
498 "tc_req_ancestors: "^tc_req_ancestors^"\n"^
499 "tc_req_ancestors_extends: "^tc_req_ancestors_extends^"\n"^
503 module PrintFun = struct
505 let fparam (sopt, ty) =
506 let s = match sopt with
507 | None -> "[None]"
508 | Some s -> s in
509 s ^ " " ^ (Full.to_string_decl ty) ^ ", "
511 let farity = function
512 | Fstandard (min, max) -> Printf.sprintf "non-variadic: %d to %d" min max
513 | Fvariadic (min, _) ->
514 Printf.sprintf "variadic: ...$arg-style (PHP 5.6); min: %d" min
515 | Fellipsis min -> Printf.sprintf "variadic: ...-style (Hack); min: %d" min
517 let fparams l =
518 List.fold_right l ~f:(fun x acc -> (fparam x)^acc) ~init:""
520 let fun_type f =
521 let ft_pos = PrintClass.pos f.ft_pos in
522 let ft_abstract = string_of_bool f.ft_abstract in
523 let ft_arity = farity f.ft_arity in
524 let ft_tparams = PrintClass.tparam_list f.ft_tparams in
525 let ft_params = fparams f.ft_params in
526 let ft_ret = Full.to_string_decl f.ft_ret in
527 "ft_pos: "^ft_pos^"\n"^
528 "ft_abstract: "^ft_abstract^"\n"^
529 "ft_arity: "^ft_arity^"\n"^
530 "ft_tparams: "^ft_tparams^"\n"^
531 "ft_params: "^ft_params^"\n"^
532 "ft_ret: "^ft_ret^"\n"^
536 module PrintTypedef = struct
538 let typedef = function
539 | {td_pos; td_vis = _; td_tparams; td_constraint; td_type} ->
540 let tparaml_s = PrintClass.tparam_list td_tparams in
541 let constr_s = match td_constraint with
542 | None -> "[None]"
543 | Some constr -> Full.to_string_decl constr in
544 let ty_s = Full.to_string_decl td_type in
545 let pos_s = PrintClass.pos td_pos in
546 "ty: "^ty_s^"\n"^
547 "tparaml: "^tparaml_s^"\n"^
548 "constraint: "^constr_s^"\n"^
549 "pos: "^pos_s^"\n"^
554 (*****************************************************************************)
555 (* User API *)
556 (*****************************************************************************)
558 let error: type a. a ty_ -> _ = fun ty -> ErrorString.type_ ty
559 let suggest: type a. a ty -> _ = fun ty -> Suggest.type_ ty
560 let full env ty = Full.to_string env ty
561 let full_strip_ns env ty = Full.to_string_strip_ns env ty
562 let debug env ty =
563 let e_str = error (snd ty) in
564 let f_str = full_strip_ns env ty in
565 e_str^" "^f_str
566 let class_ tcopt c = PrintClass.class_type tcopt c
567 let gconst gc = Full.to_string_decl gc
568 let fun_ f = PrintFun.fun_type f
569 let typedef td = PrintTypedef.typedef td