2 * Copyright (c) 2016, Facebook, Inc.
5 * This source code is licensed under the MIT license found in the
6 * LICENSE file in the "hack" directory of this source tree.
13 module Inf
= Typing_inference_env
14 module Pr
= Typing_print
15 module TPEnv
= Type_parameter_env
16 module TySet
= Typing_set
20 (*****************************************************************************)
21 (* Logging type inference environment *)
22 (*****************************************************************************)
24 (* Eventual input to Tty.cprint *)
25 let out_channel = ref stdout
27 let logBuffer = ref []
29 let indentLevel = ref 0
31 let accumulatedLength = ref 0
38 ~
out_channel:!out_channel
39 ~color_mode
:Color_Auto
40 ((Normal White
, String.make
(2 * !indentLevel) ' '
)
41 :: List.rev
((Normal White
, "\n") :: !logBuffer));
42 Out_channel.flush
!out_channel;
43 accumulatedLength := 0;
47 Printf.ksprintf
(fun s
->
48 let len = String.length s
in
49 logBuffer := (c
, s
) :: !logBuffer;
50 if !accumulatedLength + len > 80 then
53 accumulatedLength := !accumulatedLength + len)
55 let lnewline_open () =
57 indentLevel := !indentLevel + 1
59 let lnewline_close () =
61 indentLevel := !indentLevel - 1
63 let indentEnv ?
(color
= Normal Yellow
) message f
=
65 lprintf color
"%s" message
;
70 (* Most recent environment. We only display diffs *)
73 (Typing_env_types.empty
74 (Provider_context.empty_for_debugging
75 ~popt
:ParserOptions.default
76 ~tcopt
:TypecheckerOptions.default
77 ~deps_mode
:(Typing_deps_mode.InMemoryMode None
))
81 let iterations : int Pos.Map.t
ref = ref Pos.Map.empty
83 let iterations_decl : int Pos_or_decl.Map.t
ref = ref Pos_or_decl.Map.empty
85 (* Universal representation of a delta between values
88 | Updated
of value (* For bools, atoms, lists, replaced sets, replaced maps *)
90 (* Set has had some elements removed, some added *)
95 (* Map has some new keys, some removed keys, and deltas to existing keys.
96 * All other keys assumed to be unchanged.
101 changed
: delta
SMap.t
;
104 let rec compute_value_delta (oldval
: value) (newval
: value) : delta
=
105 match (oldval
, newval
) with
106 | (Bool b1
, Bool b2
) ->
107 if Bool.equal b1 b2
then
111 | (Atom s1
, Atom s2
) ->
112 if String.equal s1 s2
then
116 | (Type t1
, Type t2
) ->
117 if equal_internal_type t1 t2
then
121 | (SubtypeProp p1
, SubtypeProp p2
) ->
122 if Typing_logic.equal_subtype_prop p1 p2
then
126 | (Set s1
, Set s2
) ->
127 let added = SSet.diff s2 s1
in
128 let removed = SSet.diff s1 s2
in
129 if SSet.is_empty
added && SSet.is_empty
removed then
132 Set_delta
{ added; removed }
133 | (List l1
, List l2
) ->
134 if List.equal equal_value l1 l2
then
138 | (Map m1
, Map m2
) ->
142 match SMap.find_opt i m2
with
143 | None
-> SSet.add i s
151 match SMap.find_opt i m1
with
152 | None
-> SMap.add i v m
160 match SMap.find_opt i m2
with
163 (match compute_value_delta oldx newx
with
165 | d
-> SMap.add i d m
))
169 if SSet.is_empty
removed && SMap.is_empty
added && SMap.is_empty
changed
173 Map_delta
{ removed; added; changed }
174 (* Type has changed! *)
175 | (_
, _
) -> Updated newval
177 let is_leaf_value v
=
183 | Map m
when SMap.is_empty m
-> true
187 let log_key key
= lprintf (Normal Yellow
) "%s" key
190 match SSet.elements s
with
191 | [] -> lprintf (Normal Green
) "{}"
192 | [s
] -> lprintf (Normal Green
) "{%s}" s
194 lprintf (Normal Green
) "{";
195 lprintf (Normal Green
) "%s" s
;
196 List.iter ss ~f
:(fun s
-> lprintf (Normal Green
) ",%s" s
);
197 lprintf (Normal Green
) "}"
199 let rec log_value env
value =
201 | Atom s
-> lprintf (Normal Green
) "%s" s
210 | List
[] -> lprintf (Normal Green
) "[]"
212 lprintf (Normal Green
) "[";
214 List.iter vs ~f
:(fun v
->
215 lprintf (Normal Green
) ",";
217 lprintf (Normal Green
) "]"
219 if SMap.is_empty m
then
220 lprintf (Normal Green
) "{}"
222 SMap.iter
(log_key_value env
"") m
223 | Set s
-> log_sset s
224 | Type ty
-> Typing_print.debug_i env ty
|> lprintf (Normal Green
) "%s"
225 | SubtypeProp prop
->
226 Typing_print.subtype_prop env prop
|> lprintf (Normal Green
) "%s"
228 and log_key_value env prefix k v
=
229 if is_leaf_value v
then (
230 lprintf (Normal Green
) "%s" prefix
;
232 lprintf (Normal Yellow
) " ";
237 lprintf (Normal Green
) "%s" prefix
;
244 let is_leaf_delta d
=
246 | Updated v
-> is_leaf_value v
247 (* | Added d | Removed d | Updated d -> is_leaf_delta d*)
250 let rec log_delta env delta
=
252 | Updated v
-> log_value env v
254 | Set_delta
{ added; removed } ->
255 if not
(SSet.is_empty
added) then (
256 lprintf (Bold Green
) " += ";
259 if not
(SSet.is_empty
removed) then (
260 lprintf (Bold Red
) " -= ";
263 | Map_delta
{ added; removed; changed } ->
266 lprintf (Bold Red
) "-";
270 SMap.iter
(log_key_value env
"+") added;
271 SMap.iter
(log_key_delta env
) changed
273 and log_key_delta env k d
=
274 if is_leaf_delta d
then (
276 lprintf (Normal Yellow
) " ";
282 lprintf (Normal Yellow
) " ";
288 let type_as_value env ty
= Atom
(Typing_print.debug env ty
)
290 let decl_type_as_value env ty
= Atom
(Typing_print.debug_decl env ty
)
292 let possibly_enforced_type_as_value env et
=
294 ((match et
.et_enforced
with
295 | Enforced
-> "enforced "
297 ^
Typing_print.debug env et
.et_type
)
299 let return_info_as_value env return_info
=
300 let Typing_env_return_info.{ return_type
; return_disposable
} = return_info
in
303 ("return_type", possibly_enforced_type_as_value env return_type
);
304 ("return_disposable", Bool return_disposable
);
307 let local_id_map_as_value f m
=
310 (fun id x m
-> SMap.add
(local_id_as_string id
) (f x
) m
)
314 let reify_kind_as_value k
=
317 | Aast.Erased
-> "erased"
318 | Aast.SoftReified
-> "soft_reified"
319 | Aast.Reified
-> "reified")
321 let tyset_as_value env tys
=
324 (fun t s
-> SSet.add
(Typing_print.debug env t
) s
)
328 let rec tparam_info_as_value env tpinfo
=
329 let Typing_kinding_defs.
343 ("lower_bounds", tyset_as_value env lower_bounds
);
344 ("upper_bounds", tyset_as_value env upper_bounds
);
345 ("reified", reify_kind_as_value reified
);
346 ("enforceable", bool_as_value enforceable
);
347 ("newable", bool_as_value newable
);
348 ("require_dynamic", bool_as_value require_dynamic
);
349 ("parameters", named_tparam_info_list_as_value env parameters
);
352 and named_tparam_info_list_as_value env parameters
=
354 List.map parameters ~f
:(fun (name
, param
) ->
356 [string_as_value
(snd name
); tparam_info_as_value env param
])
358 list_as_value
param_values
360 let tpenv_as_value env tpenv
=
366 (fun name tpinfo m
->
367 SMap.add name
(tparam_info_as_value env tpinfo
) m
)
370 ("consistent", bool_as_value
(TPEnv.is_consistent tpenv
));
373 let per_cont_entry_as_value env f entry
=
377 local_id_map_as_value f entry
.Typing_per_cont_env.local_types
);
379 Typing_fake_members.as_log_value entry
.Typing_per_cont_env.fake_members
381 ("tpenv", tpenv_as_value env entry
.Typing_per_cont_env.tpenv
);
384 let continuations_map_as_value f m
=
386 (Typing_continuations.Map.fold
387 (fun k x m
-> SMap.add
(Typing_continuations.to_string k
) (f x
) m
)
391 let local_as_value env
(ty
, _pos
, expr_id
) =
392 Atom
(Printf.sprintf
"%s [expr_id=%d]" (Typing_print.debug env ty
) expr_id
)
394 let per_cont_env_as_value env per_cont_env
=
395 continuations_map_as_value
396 (per_cont_entry_as_value env
(local_as_value env
))
399 let log_position p ?function_name f
=
401 match Pos.Map.find_opt p
!iterations with
403 iterations := Pos.Map.add p
1 !iterations;
406 iterations := Pos.Map.add p
(n + 1) !iterations;
409 (* If we've hit this many iterations then something must have gone wrong
410 * so let's not bother spewing to the log *)
416 ((Pos.string @@ Pos.to_absolute p
)
417 ^
(if Int.equal
n 1 then
420 "[" ^ string_of_int
n ^
"]")
422 match function_name
with
424 | Some
n -> " {" ^
n ^
"}")
427 let log_pos_or_decl p ?function_name f
=
429 match Pos_or_decl.Map.find_opt p
!iterations_decl with
431 iterations_decl := Pos_or_decl.Map.add p
1 !iterations_decl;
434 iterations_decl := Pos_or_decl.Map.add p
(n + 1) !iterations_decl;
437 (* If we've hit this many iterations then something must have gone wrong
438 * so let's not bother spewing to the log *)
444 (Pos_or_decl.show_as_absolute_file_line_characters p
445 ^
(if Int.equal
n 1 then
448 "[" ^ string_of_int
n ^
"]")
450 match function_name
with
452 | Some
n -> " {" ^
n ^
"}")
455 let log_with_level env key ~level log_f
=
456 if Typing_env_types.get_log_level env key
>= level
then
461 let log_subtype_prop env message prop
=
462 lprintf (Tty.Bold
Tty.Green
) "%s: " message
;
463 lprintf (Tty.Normal
Tty.Green
) "%s" (Typing_print.subtype_prop env prop
);
466 let fun_kind_to_string k
=
468 | Ast_defs.FSync
-> "normal"
469 | Ast_defs.FAsync
-> "async"
470 | Ast_defs.FGenerator
-> "generator"
471 | Ast_defs.FAsyncGenerator
-> "async generator"
473 let val_kind_to_string k
=
477 | LvalSubexpr
-> "lval subexpression"
479 let lenv_as_value env lenv
=
480 let { per_cont_env
; local_using_vars
} = lenv
in
483 ("per_cont_env", per_cont_env_as_value env per_cont_env
);
484 ("local_using_vars", local_id_set_as_value local_using_vars
);
487 let param_as_value env
(ty
, _pos
, ty_opt
) =
488 let ty_str = Typing_print.debug env ty
in
490 | None
-> Atom
ty_str
492 let ty2_str = Typing_print.debug env ty2
in
493 Atom
(Printf.sprintf
"%s inout %s" ty_str ty2_str)
495 let genv_as_value env genv
=
512 this_support_dynamic_type
;
518 ("readonly", bool_as_value readonly
);
519 ("return", return_info_as_value env return
);
520 ("callable_pos", pos_as_value callable_pos
);
521 ("params", local_id_map_as_value (param_as_value env
) params
);
523 smap_as_value
(decl_type_as_value env
) condition_types
);
524 ("static", bool_as_value static
);
525 ("val_kind", string_as_value
(val_kind_to_string val_kind
));
526 ("fun_kind", string_as_value
(fun_kind_to_string fun_kind
));
527 ("fun_is_ctor", bool_as_value fun_is_ctor
);
528 ("this_internal", bool_as_value this_internal
);
529 ("this_support_dynamic_type", bool_as_value this_support_dynamic_type
);
531 @ (match this_module
with
532 | Some this_module
->
533 [("this_module", string_as_value
@@ Ast_defs.show_id this_module
)]
536 | Some
(parent_id
, parent_ty
) ->
538 ("parent_id", string_as_value parent_id
);
539 ("parent_ty", decl_type_as_value env parent_ty
);
544 | Some
(self_id
, self_ty
) ->
546 ("self_id", string_as_value self_id
);
547 ("self_ty", type_as_value env self_ty
);
551 let fun_tast_info_as_map = function
552 | None
-> make_map
[]
555 let { has_implicit_return
; has_readonly
} = r
in
558 ("has_implicit_return", bool_as_value has_implicit_return
);
559 ("has_readonly", bool_as_value has_readonly
);
562 let env_as_value env
=
573 in_support_dynamic_type_method_check
;
585 ("fresh_typarams", Set fresh_typarams
);
586 ("lenv", lenv_as_value env lenv
);
587 ("genv", genv_as_value env genv
);
588 ("in_loop", bool_as_value in_loop
);
589 ("in_try", bool_as_value in_try
);
590 ("in_expr_tree", bool_as_value in_expr_tree
);
591 ("inside_constructor", bool_as_value inside_constructor
);
592 ( "in_support_dynamic_type_method_check",
593 bool_as_value in_support_dynamic_type_method_check
);
594 ("tpenv", tpenv_as_value env tpenv
);
595 ("allow_wildcards", bool_as_value allow_wildcards
);
597 Typing_inference_env.Log.inference_env_as_value inference_env
);
598 ("fun_tast_info", fun_tast_info_as_map fun_tast_info
);
601 let log_env_diff p ?function_name old_env new_env
=
602 let value = env_as_value new_env
in
603 let old_value = env_as_value old_env
in
604 let d = compute_value_delta old_value value in
607 | _
-> log_position p ?function_name
(fun () -> log_delta new_env
d)
609 (* Log the environment: local_types, subst, tenv and tpenv *)
610 let hh_show_env ?function_name p env
=
611 log_with_level env
"show" ~level
:0 @@ fun () ->
612 let old_env = !lastenv in
614 log_env_diff ?function_name p
old_env env
616 let hh_show_full_env p env
=
617 log_with_level env
"show" ~level
:0 @@ fun () ->
619 { env
with inference_env
= Typing_inference_env.empty_inference_env
}
621 log_env_diff p
empty_env env
623 (* Log the type of an expression *)
624 let hh_show p env ty
=
625 log_with_level env
"show" ~level
:0 @@ fun () ->
626 let s1 = Pr.with_blank_tyvars
(fun () -> Pr.debug env ty
) in
627 let s2 = Typing_print.constraints_for_type env ty
in
628 log_position p
(fun () ->
629 lprintf (Normal Green
) "%s" s1;
630 if String.( <> ) s2 "" then lprintf (Normal Green
) " %s" s2;
633 (* Simple type of possible log data *)
635 | Log_head
of string * log_structure list
636 | Log_type
of string * Typing_defs.locl_ty
637 | Log_decl_type
of string * Typing_defs.decl_ty
638 | Log_type_i
of string * Typing_defs.internal_type
640 let log_types p env items
=
641 log_pos_or_decl p
(fun () ->
643 List.iter items ~f
:(fun item
->
645 | Log_head
(message
, items
) ->
646 indentEnv ~color
:(Normal Yellow
) message
(fun () -> go items
)
647 | Log_type
(message
, ty
) ->
648 let s = Typing_print.debug env ty
in
649 lprintf (Bold Green
) "%s: " message
;
650 lprintf (Normal Green
) "%s" s;
652 | Log_decl_type
(message
, ty
) ->
653 let s = Typing_print.debug_decl env ty
in
654 lprintf (Bold Green
) "%s: " message
;
655 lprintf (Normal Green
) "%s" s;
657 | Log_type_i
(message
, ty
) ->
658 let s = Typing_print.debug_i env ty
in
659 lprintf (Bold Green
) "%s: " message
;
660 lprintf (Normal Green
) "%s" s;
665 let log_escape ?
(level
= 1) p env msg vars
=
666 log_with_level env
"escape" ~level
(fun () ->
667 log_pos_or_decl p
(fun () ->
668 indentEnv ~color
:(Normal Yellow
) msg
(fun () -> ());
669 if not
(List.is_empty vars
) then (
671 List.iter vars ~f
:(lprintf (Normal Green
) "%s ")
674 let log_global_inference_env p env global_tvenv
=
675 log_position p
(fun () ->
676 log_value env
@@ Inf.Log.global_inference_env_as_value global_tvenv
)
678 let log_prop level p message env prop
=
679 log_with_level env
"prop" ~level
(fun () ->
680 log_pos_or_decl p
(fun () -> log_subtype_prop env message prop
))
682 let log_new_tvar env p tvar message
=
683 log_with_level env
"prop" ~level
:2 (fun () ->
685 (Pos_or_decl.of_raw_pos p
)
687 [Log_head
(message
, [Log_type
("type variable", tvar
)])])
689 let log_tparam_instantiation env p tparam_name tvar
=
691 Printf.sprintf
"Instantiating type parameter %s with" tparam_name
693 log_new_tvar env p tvar
message
695 let log_new_tvar_for_new_object env p tvar cname tparam
=
698 "Creating new type var for type parameter %s while instantiating object %s"
702 log_new_tvar env p tvar
message
704 let log_new_tvar_for_tconst env
(p
, tvar
) (_p
, tconstid
) tvar_for_tconst
=
706 Printf.sprintf
"Creating new type var for #%d::%s" tvar tconstid
708 log_new_tvar env p tvar_for_tconst
message
710 let log_new_tvar_for_tconst_access env p tvar class_name
(_p
, tconst
) =
713 "Creating type var with the same constraints as %s::%s"
717 log_new_tvar env p tvar
message
719 let log_intersection ~level env r ty1 ty2 ~inter_ty
=
720 log_with_level env
"inter" ~level
(fun () ->
728 Log_type
("ty1", ty1
);
729 Log_type
("ty2", ty2
);
730 Log_type
("intersection", inter_ty
);
734 let log_type_access ~level root
(p
, type_const_name
) (env
, result_ty
) =
735 ( log_with_level env
"tyconst" ~level
@@ fun () ->
741 ( "Accessing type constant " ^ type_const_name ^
" of",
742 [Log_type
("type", root
); Log_type
("result", result_ty
)] );
746 let log_localize ~level ety_env
(decl_ty
: decl_ty
) (env
, result_ty
) =
747 ( log_with_level env
"localize" ~level
@@ fun () ->
757 "expand_visible_newtype: %b"
758 ety_env
.expand_visible_newtype
,
760 Log_decl_type
("decl type", decl_ty
);
761 Log_type
("result", result_ty
);
766 let log_pessimise_ ?
(level
= 1) env kind pos name
=
767 log_with_level env
"pessimise" ~level
@@ fun () ->
768 let p = Pos_or_decl.unsafe_to_raw_pos pos
in
770 let p = Pos.to_absolute
p in
771 (Pos.filename
p, Pos.line
p)
774 lprintf (Normal Yellow
) "pessimise:\t%s,%s,%d,%s" kind file line name
;
777 let log_pessimise_prop env pos prop_name
=
778 log_pessimise_ env
"prop" (Pos_or_decl.of_raw_pos pos
) ("$" ^ prop_name
)
780 let log_pessimise_param env ~is_promoted_property pos mode param_name
=
784 | Ast_defs.Pinout _
-> true
786 || Typing_env_types.get_log_level env
"pessimise.params" >= 1
790 (if is_promoted_property
then
794 (Pos_or_decl.of_raw_pos pos
)
797 let log_pessimise_return ?level env pos
=
798 log_pessimise_ ?level env
"ret" (Pos_or_decl.of_raw_pos pos
) ""
800 let increment_feature_count env
s =
801 if TypecheckerOptions.language_feature_logging env
.genv
.tcopt
then
804 module GlobalInference
= struct
807 let log_merging_subgraph env pos
=
808 log_with_level env
log_cat ~level
:1 (fun () ->
809 log_position pos
(fun () ->
810 log_key "merging subgraph for function at this position"))
812 let log_merging_var env pos var
=
813 log_with_level env
log_cat ~level
:1 (fun () ->
814 log_position pos
(fun () ->
815 log_key (Printf.sprintf
"merging type variable %d" var
)))
818 module GI
= GlobalInference