2 * Copyright (c) 2015, 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.
16 open Typing_env_return_info
18 module Dep
= Typing_deps.Dep
20 module SG
= SN.Superglobals
21 module LEnvC
= Typing_per_cont_env
22 module C
= Typing_continuations
23 module TL
= Typing_logic
24 module Cls
= Decl_provider.Class
25 module Fake
= Typing_fake_members
26 module TPEnv
= Type_parameter_env
28 let show_env _
= "<env>"
29 let pp_env _ _
= Printf.printf
"%s\n" "<env>"
31 let ( ++ ) x y
= Typing_set.add x y
33 let get_tcopt env
= env
.genv
.tcopt
35 let set_log_level env key log_level
=
36 {env
with log_levels
= SMap.add key log_level env
.log_levels
}
38 let get_log_level env key
=
39 Option.value (SMap.get key env
.log_levels
) ~default
:0
41 let env_log_function = ref (fun _pos _name _old_env _new_env
-> ())
43 let set_env_log_function f
= env_log_function := f
45 let log_env_change name ?
(level
= 1) old_env new_env
=
46 if get_log_level new_env name
>= 1 || get_log_level new_env
"env" >= level
49 match old_env
.tyvars_stack
with
51 | _
-> old_env
.function_pos
in
52 !env_log_function pos name old_env new_env
56 let add_subst env x x'
=
58 then { env
with subst
= IMap.add x x' env
.subst
}
61 (* Apply variable-to-variable substitution from environment. Update environment
62 if we ended up iterating (cf path compression in union-find) *)
63 let rec get_var env x
=
64 let x'
= IMap.get
x env
.subst
in
68 let env, x'
= get_var env x'
in
69 let env = add_subst env x x'
in
73 (* This is basically union from union-find, but without balancing
74 * (linking the smaller tree to the larger tree). In practice this
75 * isn't important: path compression is much more significant. *)
77 let env, x = get_var env x in
78 let env, x'
= get_var env x'
in
79 let env = add_subst env x x'
in
83 let env, x = get_var env x in
85 | _
, Tvar
x'
-> add_subst env x x'
86 | _
-> { env with tenv
= IMap.add x ty
env.tenv
}
88 let empty_bounds = TySet.empty
90 let env_with_tvenv env tvenv
=
91 { env with tvenv
= tvenv
}
93 let empty_tyvar_info =
94 { tyvar_pos
= Pos.none
;
95 eager_solve_fail
= false;
96 lower_bounds
= empty_bounds;
97 upper_bounds
= empty_bounds;
98 appears_covariantly
= false;
99 appears_contravariantly
= false;
100 type_constants
= SMap.empty
;
103 let add_current_tyvar ?variance
env p v
=
104 match env.tyvars_stack
with
105 | (expr_pos
, tyvars
) :: rest
->
108 | Some
Ast_defs.Invariant
-> {empty_tyvar_info with appears_covariantly
= true; appears_contravariantly
= true}
109 | Some
Ast_defs.Covariant
-> {empty_tyvar_info with appears_covariantly
= true}
110 | Some
Ast_defs.Contravariant
-> {empty_tyvar_info with appears_contravariantly
= true}
111 | None
-> empty_tyvar_info
113 let env = env_with_tvenv env
114 (IMap.add v
{ tyvar with tyvar_pos
= p
} env.tvenv
) in
115 { env with tyvars_stack
= (expr_pos
, (v
:: tyvars
)) :: rest
}
118 let fresh_type_reason ?variance
env r
=
119 let v = Ident.tmp
() in
121 log_env_change "fresh_type" env @@
122 add_current_tyvar ?variance
env (Reason.to_pos r
) v in
125 let fresh_type env p
=
126 fresh_type_reason env (Reason.Rtype_variable p
)
128 let open_tyvars env p
=
129 { env with tyvars_stack
= (p
,[]) :: env.tyvars_stack
}
131 let close_tyvars env =
132 match env.tyvars_stack
with
133 | [] -> failwith
"close_tyvars: empty stack"
134 | _
::rest
-> { env with tyvars_stack
= rest
}
136 let get_current_tyvars env =
137 match env.tyvars_stack
with
139 | (_
,tyvars
)::_
-> tyvars
141 let get_type env x_reason
x =
142 let env, x = get_var env x in
143 let ty = IMap.get
x env.tenv
in
145 | None
-> env, (x_reason
, Tvar
x)
148 let get_type_unsafe env x =
149 let ty = IMap.get
x env.tenv
in
152 env, (Reason.none
, Typing_defs.make_tany
())
155 let get_tyvar_info env var
=
156 Option.value (IMap.get var
env.tvenv
) ~default
:empty_tyvar_info
158 let set_tyvar_info env var tyvar_info
=
159 { env with tvenv
= IMap.add var tyvar_info
env.tvenv
}
161 let get_tyvar_eager_solve_fail env var
=
162 let tvinfo = get_tyvar_info env var
in
163 tvinfo.eager_solve_fail
165 let expand_var env r
v =
166 let env, ty = get_type env r
v in
167 if get_tyvar_eager_solve_fail env v
168 then env, (Reason.Rsolve_fail
(Reason.to_pos r
), snd
ty)
171 let expand_type env x =
173 | r
, Tvar
x -> expand_var env r
x
176 let get_shape_field_name = function
177 | Ast_defs.SFlit_int
(_
, s
)
178 | Ast_defs.SFlit_str
(_
, s
) -> s
179 | Ast_defs.SFclass_const
((_
, s1
), (_
, s2
)) -> s1^
"::"^s2
181 let get_shape_field_name_pos = function
182 | Ast_defs.SFlit_int
(p
, _
)
183 | Ast_defs.SFlit_str
(p
, _
)
184 | Ast_defs.SFclass_const
((p
, _
), _
) -> p
186 let next_cont_opt env = LEnvC.get_cont_option
C.Next
env.lenv
.per_cont_env
189 match next_cont_opt env with
190 | None
-> TPEnv.empty
191 | Some entry
-> entry
.Typing_per_cont_env.tpenv
193 let get_lower_bounds env name
=
194 let tpenv = get_tpenv env in
195 let local = TPEnv.get_lower_bounds tpenv name
in
196 let global = TPEnv.get_lower_bounds env.global_tpenv name
in
197 TySet.union
local global
199 let get_upper_bounds env name
=
200 let tpenv = get_tpenv env in
201 let local = TPEnv.get_upper_bounds tpenv name
in
202 let global = TPEnv.get_upper_bounds env.global_tpenv name
in
203 TySet.union
local global
205 let get_reified env name
=
206 let tpenv = get_tpenv env in
207 let local = TPEnv.get_reified tpenv name
in
208 let global = TPEnv.get_reified env.global_tpenv name
in
209 match local, global with
210 | Reified
, _
| _
, Reified
-> Reified
211 | SoftReified
, _
| _
, SoftReified
-> SoftReified
214 let get_enforceable env name
=
215 let tpenv = get_tpenv env in
216 let local = TPEnv.get_enforceable tpenv name
in
217 let global = TPEnv.get_enforceable env.global_tpenv name
in
220 let get_newable env name
=
221 let tpenv = get_tpenv env in
222 let local = TPEnv.get_newable tpenv name
in
223 let global = TPEnv.get_newable env.global_tpenv name
in
226 (* Get bounds that are both an upper and lower of a given generic *)
227 let get_equal_bounds env name
=
228 let lower = get_lower_bounds env name
in
229 let upper = get_upper_bounds env name
in
230 TySet.inter
lower upper
232 let env_with_tpenv env tpenv =
234 { env.lenv
with per_cont_env
=
235 Typing_per_cont_env.(update_cont_entry
C.Next
env.lenv
.per_cont_env
236 (fun entry
-> { entry
with tpenv = tpenv })) } }
238 let env_with_global_tpenv env global_tpenv
=
239 { env with global_tpenv
}
241 let add_upper_bound_global env name
ty =
244 | (r
, Tabstract
(AKgeneric formal_super
, _
)) ->
245 TPEnv.add_lower_bound
env.global_tpenv formal_super
246 (r
, Tabstract
(AKgeneric name
, None
))
247 | _
-> env.global_tpenv
249 { env with global_tpenv
= TPEnv.add_upper_bound
tpenv name
ty }
251 (* Add a single new upper bound [ty] to generic parameter [name] in the local
252 * type parameter environment of [env].
253 * If the optional [intersect] operation is supplied, then use this to avoid
254 * adding redundant bounds by merging the type with existing bounds. This makes
255 * sense because a conjunction of upper bounds
256 * (T <: t1) /\ ... /\ (T <: tn)
257 * is equivalent to a single upper bound
258 * T <: (t1 & ... & tn)
260 let add_upper_bound ?intersect
env name
ty =
261 env_with_tpenv env (TPEnv.add_upper_bound ?intersect
(get_tpenv env) name
ty)
263 (* Add a single new upper lower [ty] to generic parameter [name] in the
264 * local type parameter environment [env].
265 * If the optional [union] operation is supplied, then use this to avoid
266 * adding redundant bounds by merging the type with existing bounds. This makes
267 * sense because a conjunction of lower bounds
268 * (t1 <: T) /\ ... /\ (tn <: T)
269 * is equivalent to a single lower bound
270 * (t1 | ... | tn) <: T
272 let add_lower_bound ?union
env name
ty =
273 env_with_tpenv env (TPEnv.add_lower_bound ?union
(get_tpenv env) name
ty)
275 (* Add type parameters to environment, initially with no bounds.
276 * Existing type parameters with the same name will be overridden. *)
277 let add_generic_parameters env tparaml
=
278 env_with_tpenv env (TPEnv.add_generic_parameters (get_tpenv env) tparaml
)
280 let is_generic_parameter env name
=
281 TPEnv.mem name
(get_tpenv env) || SSet.mem name
env.fresh_typarams
283 let get_generic_parameters env =
284 TPEnv.get_names
(TPEnv.union
(get_tpenv env) env.global_tpenv
)
286 let get_tpenv_size env =
287 TPEnv.size
(get_tpenv env) + TPEnv.size
env.global_tpenv
290 (*****************************************************************************
291 * Operations to get or add bounds to type variables.
292 * There is a lot of code duplication from the tpenv code here, which we
293 * should consider sharing in future.
294 *****************************************************************************)
296 let get_tyvar_lower_bounds env var
=
297 match IMap.get var
env.tvenv
with
298 | None
-> empty_bounds
299 | Some
{lower_bounds
; _
} -> lower_bounds
301 let get_tyvar_upper_bounds env var
=
302 match IMap.get var
env.tvenv
with
303 | None
-> empty_bounds
304 | Some
{upper_bounds
; _
} -> upper_bounds
306 let set_tyvar_lower_bounds env var lower_bounds
=
307 let tyvar_info = get_tyvar_info env var
in
308 let tyvar_info = { tyvar_info with lower_bounds
} in
309 let env = set_tyvar_info env var
tyvar_info in
312 let set_tyvar_upper_bounds env var upper_bounds
=
313 let tyvar_info = get_tyvar_info env var
in
314 let tyvar_info = { tyvar_info with upper_bounds
} in
315 let env = set_tyvar_info env var
tyvar_info in
318 let rec is_tvar ~elide_nullable
ty var
=
320 | (_
, Tvar var'
) -> var
= var'
321 | (_
, Toption
ty) when elide_nullable
-> is_tvar ~elide_nullable
ty var
324 let set_tyvar_info env var
tvinfo =
325 env_with_tvenv env (IMap.add var
tvinfo env.tvenv
)
327 let remove_tyvar env var
=
328 (* Don't remove it entirely if we have marked it as eager_solve_fail *)
329 log_env_change "remove_tyvar" env @@
330 let tvinfo = get_tyvar_info env var
in
331 if tvinfo.eager_solve_fail
332 then set_tyvar_info env var
{ empty_tyvar_info with eager_solve_fail
= true }
333 else env_with_tvenv env (IMap.remove var
env.tvenv
)
335 let set_tyvar_eager_solve_fail env var
=
336 let tvinfo = get_tyvar_info env var
in
337 set_tyvar_info env var
{ tvinfo with eager_solve_fail
= true }
339 let get_tyvar_appears_covariantly env var
=
340 let tvinfo = get_tyvar_info env var
in
341 tvinfo.appears_covariantly
343 let get_tyvar_appears_contravariantly env var
=
344 let tvinfo = get_tyvar_info env var
in
345 tvinfo.appears_contravariantly
347 let get_tyvar_appears_invariantly env var
=
348 (get_tyvar_appears_covariantly env var
) && (get_tyvar_appears_contravariantly env var
)
350 let get_tyvar_type_consts env var
=
351 let tvinfo = get_tyvar_info env var
in
352 tvinfo.type_constants
354 let get_tyvar_type_const env var
(_
, tyconstid
) =
355 SMap.get tyconstid
(get_tyvar_type_consts env var
)
357 let set_tyvar_type_const env var
(_
, tyconstid_
as tyconstid
) ty =
358 let tvinfo = get_tyvar_info env var
in
359 let type_constants = SMap.add tyconstid_
(tyconstid
, ty) tvinfo.type_constants in
360 set_tyvar_info env var
{ tvinfo with type_constants }
362 (* Conjoin a subtype proposition onto the subtype_prop in the environment *)
363 let add_subtype_prop env prop
=
364 log_env_change "add_subtype_prop" env @@
365 {env with subtype_prop
= TL.conj
env.subtype_prop prop
}
367 (* Generate a fresh generic parameter with a specified prefix but distinct
368 * from all generic parameters in the environment *)
369 let add_fresh_generic_parameter env prefix ~reified ~enforceable ~newable
=
371 let name = Printf.sprintf
"%s#%d" prefix i
in
372 if is_generic_parameter env name then iterate (i
+1) else name in
373 let name = iterate 1 in
374 let env = { env with fresh_typarams
= SSet.add name env.fresh_typarams
} in
380 lower_bounds
= empty_bounds;
381 upper_bounds
= empty_bounds;
389 let is_fresh_generic_parameter name =
390 String.contains
name '#'
&& not
(AbstractKind.is_generic_dep_ty
name)
392 let tparams_visitor env =
394 inherit [SSet.t
] Type_visitor.type_visitor
395 method! on_tabstract acc _ ak _ty_opt
=
397 | AKgeneric s
-> SSet.add s acc
399 method! on_tvar acc r ix
=
400 let _env, ty = get_type env r ix
in
403 | _
-> this#on_type acc
ty
406 let get_tparams_aux env acc
ty = (tparams_visitor env)#on_type acc
ty
407 let get_tparams env ty = get_tparams_aux env SSet.empty
ty
409 let get_tpenv_tparams env =
410 TPEnv.fold
begin fun _x
TPEnv.{ lower_bounds
; upper_bounds
; reified
= _
; enforceable
= _
; newable
= _
} acc
->
413 | _
, Tabstract
(AKgeneric _
, _
) -> acc
414 | _
-> get_tparams_aux env acc
ty in
415 TySet.fold
folder lower_bounds
@@
416 TySet.fold
folder upper_bounds acc
418 (get_tpenv env) SSet.empty
420 (* Replace types for locals with empty environment *)
421 let env_with_locals env locals
=
423 { env.lenv
with per_cont_env
= locals
; }
426 (* This is used whenever we start checking a method. Retain tpenv from the class type parameters *)
427 let reinitialize_locals env =
428 env_with_locals env LEnvC.(initial_locals
{ empty_entry
with tpenv = get_tpenv env })
430 let initial_local tpenv local_reactive
= {
431 per_cont_env
= LEnvC.(initial_locals
{ empty_entry
with tpenv = tpenv });
432 local_using_vars
= LID.Set.empty
;
433 local_mutability
= LID.Map.empty
;
434 local_reactive
= local_reactive
;
437 let empty ?
(mode
= FileInfo.Mstrict
) tcopt file ~droot
= {
438 function_pos
= Pos.none
;
441 fresh_typarams
= SSet.empty;
442 lenv
= initial_local TPEnv.empty Nonreactive
;
446 inside_constructor
= false;
447 inside_ppl_class
= false;
456 (* Actually should get set straight away anyway *)
457 return_type
= { et_type
= (Reason.Rnone
, Tunion
[]); et_enforced
= false; };
458 return_disposable
= false;
459 return_mutable
= false;
460 return_explicit
= false;
461 return_void_to_rx
= false;
463 params
= LID.Map.empty;
464 condition_types
= SMap.empty;
466 self
= Reason.none
, Typing_defs.make_tany
();
470 parent
= Reason.none
, Typing_defs.make_tany
();
471 fun_kind
= Ast_defs.FSync
;
476 global_tpenv
= TPEnv.empty;
477 subtype_prop
= TL.valid
;
478 log_levels
= TypecheckerOptions.log_levels tcopt
;
481 allow_wildcards
= false;
486 let set_env_reactive env reactive
=
487 { env with lenv
= {env.lenv
with local_reactive
= reactive
}}
489 let set_env_pessimize env =
490 let pessimize_coefficient = TypecheckerOptions.simple_pessimize
(get_tcopt env) in
491 let path = Pos.filename
env.function_pos
in
492 let open Relative_path
in
493 let pessimize = match prefix
path with
494 | Root
when pessimize_coefficient > 0.0 ->
495 let hash = Hashtbl.hash (suffix
path) in
496 ((Float.of_int
hash) /. (Float.of_int
Int.max_value
)) <= pessimize_coefficient
497 | _
-> pessimize_coefficient = 1.0 (* hack for test cases *) in
498 { env with pessimize }
500 let set_env_function_pos env function_pos
=
501 { env with function_pos
}
503 let set_condition_type env n
ty =
505 env.genv
with condition_types
= SMap.add n
ty env.genv
.condition_types
}
508 let get_condition_type env n
=
509 SMap.get n
env.genv
.condition_types
511 (* Some form (strict/shallow/local) of reactivity *)
512 let env_local_reactive env =
513 env_reactivity
env <> Nonreactive
515 let function_is_mutable env =
518 let set_fun_mutable env mut
=
519 { env with genv
= {env.genv
with fun_mutable
= mut
}}
521 let error_if_reactive_context env f
=
522 if env_local_reactive env && not
(TypecheckerOptions.unsafe_rx
env.genv
.tcopt
) then f
()
524 let error_if_shallow_reactive_context env f
=
525 match env_reactivity
env with
526 | Reactive _
| Shallow _
when not
(TypecheckerOptions.unsafe_rx
env.genv
.tcopt
) -> f
()
529 let add_wclass env x =
530 let dep = Dep.Class
x in
531 Option.iter
env.decl_env
.droot
(fun root
-> Typing_deps.add_idep root
dep);
534 let get_typedef env x =
536 Decl_provider.get_typedef x
539 match Naming_table.Types.get_pos
x with
540 | Some
(_p
, Naming_table.TTypedef
) -> true
543 let get_class env x =
545 Decl_provider.get_class x
547 let get_class_dep env x =
548 Decl_env.add_extends_dependency
env.decl_env
x;
551 let get_enum_constraint env x =
552 match get_class env x with
555 match (Cls.enum_type tc
) with
557 | Some e
-> e
.te_constraint
559 let env_with_mut env local_mutability
=
560 { env with lenv
= { env.lenv
with local_mutability
} }
562 let get_env_mutability env =
563 env.lenv
.local_mutability
567 match Decl_provider.get_class x with
568 | Some tc
when (Cls.enum_type tc
) <> None
-> Some tc
571 let is_enum env x = get_enum env x <> None
573 let get_typeconst env class_ mid
=
574 add_wclass env (Cls.name class_
);
575 let dep = Dep.Const
((Cls.name class_
), mid
) in
576 Option.iter
env.decl_env
.droot
(fun root
-> Typing_deps.add_idep root
dep);
577 Cls.get_typeconst class_ mid
579 (* Used to access class constants. *)
580 let get_const env class_ mid
=
581 add_wclass env (Cls.name class_
);
582 let dep = Dep.Const
((Cls.name class_
), mid
) in
583 Option.iter
env.decl_env
.droot
(fun root
-> Typing_deps.add_idep root
dep);
584 Cls.get_const class_ mid
586 (* Used to access "global constants". That is constants that were
587 * introduced with "const X = ...;" at topelevel, or "define('X', ...);"
589 let get_gconst env cst_name
=
590 let dep = Dep.GConst cst_name
in
591 Option.iter
env.decl_env
.droot
(fun root
-> Typing_deps.add_idep root
dep);
592 Decl_provider.get_gconst cst_name
594 let get_static_member is_method
env class_ mid
=
595 add_wclass env (Cls.name class_
);
597 let dep = if is_method
then Dep.SMethod
(x, mid
)
598 else Dep.SProp
(x, mid
) in
599 Option.iter
env.decl_env
.droot
(fun root
-> Typing_deps.add_idep root
dep);
601 add_dep (Cls.name class_
);
602 (* The type of a member is stored separately in the heap. This means that
603 * any user of the member also has a dependency on the class where the member
606 let ce_opt = if is_method
then Cls.get_smethod class_ mid
607 else Cls.get_sprop class_ mid
in
608 Option.iter
ce_opt (fun ce
-> add_dep ce
.ce_origin
);
611 (* Given a list of things whose name we can extract with `f`, return
612 the item whose name is closest to `name`. *)
613 let most_similar (name: string) (possibilities
: 'a
Sequence.t
) (f
: 'a
-> string): 'a
option =
614 let distance = String_utils.levenshtein_distance
in
615 let choose_closest x y
=
616 if distance (f
x) name < distance (f y
) name then x else y
618 Sequence.fold possibilities ~init
:None ~f
:(fun acc possibility
->
620 | None
-> Some possibility
621 | Some current_best
->
622 Some
(choose_closest current_best possibility
))
624 let suggest_member members mid
=
625 let pairs = Sequence.map members ~f
:begin fun (x, {ce_type
= lazy (r
, _
); _
}) ->
629 most_similar mid
pairs snd
631 let suggest_static_member is_method class_ mid
=
632 let mid = String.lowercase
mid in
633 let members = if is_method
then (Cls.smethods class_
) else (Cls.sprops class_
) in
634 suggest_member members mid
636 let get_member is_method
env class_
mid =
637 add_wclass env (Cls.name class_
);
639 let dep = if is_method
then Dep.Method
(x, mid)
640 else Dep.Prop
(x, mid) in
641 Option.iter
env.decl_env
.droot
(fun root
-> Typing_deps.add_idep root
dep)
643 add_dep (Cls.name class_
);
644 (* The type of a member is stored separately in the heap. This means that
645 * any user of the member also has a dependency on the class where the member
648 let ce_opt = if is_method
then Cls.get_method class_
mid
649 else Cls.get_prop class_
mid in
650 Option.iter
ce_opt (fun ce
-> add_dep ce
.ce_origin
);
653 let suggest_member is_method class_
mid =
654 let mid = String.lowercase
mid in
655 let members = if is_method
then (Cls.methods class_
) else (Cls.props class_
) in
656 suggest_member members mid
658 let get_construct env class_
=
659 add_wclass env (Cls.name class_
);
661 let dep = Dep.Cstr
(x) in
662 Option.iter
env.decl_env
.Decl_env.droot
663 (fun root
-> Typing_deps.add_idep root
dep);
665 add_dep (Cls.name class_
);
666 Option.iter
(fst
(Cls.construct class_
)) (fun ce
-> add_dep ce
.ce_origin
);
667 (Cls.construct class_
)
672 let set_return env x =
673 let genv = env.genv in
674 let genv = { genv with return
= x } in
675 { env with genv = genv }
680 let set_params env params
=
681 { env with genv = { env.genv with params
= params
} }
683 let set_param env x param
=
684 let params = get_params env in
685 let params = LID.Map.add x param
params in
686 set_params env params
688 let clear_params env =
689 set_params env LID.Map.empty
692 let ret = get_return env in
693 let params = get_params env in
694 let env, result
= f
env in
695 let env = set_params env params in
696 let env = set_return env ret in
699 let is_static env = env.genv.static
700 let get_val_kind env = env.genv.val_kind
701 let get_self env = env.genv.self
702 let get_self_id env = env.genv.self_id
703 let is_outside_class env = (env.genv.self_id
= "")
704 let get_parent env = env.genv.parent
705 let get_parent_id env = env.genv.parent_id
707 let get_fn_kind env = env.genv.fun_kind
709 let get_file env = env.genv.file
711 let set_fn_kind env fn_type
=
712 let genv = env.genv in
713 let genv = { genv with fun_kind
= fn_type
} in
714 { env with genv = genv }
716 let set_inside_ppl_class env inside_ppl_class
=
717 { env with inside_ppl_class
}
719 let add_anonymous env x =
720 let genv = env.genv in
721 let anon_id = Ident.tmp
() in
722 let genv = { genv with anons
= IMap.add anon_id x genv.anons
} in
723 { env with genv = genv }, anon_id
725 let get_anonymous env x =
726 IMap.get
x env.genv.anons
728 let set_self_id env x =
729 let genv = env.genv in
730 let genv = { genv with self_id
= x } in
731 { env with genv = genv }
734 let genv = env.genv in
735 let genv = { genv with self
= x } in
736 { env with genv = genv }
738 let set_parent_id env x =
739 let genv = env.genv in
740 let genv = { genv with parent_id
= x } in
741 { env with genv = genv }
743 let set_parent env x =
744 let genv = env.genv in
745 let genv = { genv with parent
= x } in
746 { env with genv = genv }
749 let genv = env.genv in
750 let genv = { genv with static
= true } in
751 { env with genv = genv }
753 let set_val_kind env x =
754 let genv = env.genv in
755 let genv = { genv with val_kind
= x } in
756 { env with genv = genv }
758 let set_mode env mode
=
759 let decl_env = env.decl_env in
760 let decl_env = { decl_env with mode
} in
761 { env with decl_env }
763 let get_mode env = env.decl_env.mode
765 let is_strict env = FileInfo.is_strict (get_mode env)
766 let is_decl env = get_mode env = FileInfo.Mdecl
768 let iter_anonymous env f
=
769 IMap.iter
(fun _id
{ counter
= ftys
; pos; _
} ->
770 let (untyped
,typed
) = !ftys
in f
pos (untyped
@ typed
)) env.genv.anons
772 (*****************************************************************************)
774 (*****************************************************************************)
776 let set_local_ env x ty =
777 let per_cont_env = LEnvC.add_to_cont
C.Next
x ty env.lenv
.per_cont_env in
778 { env with lenv
= { env.lenv
with per_cont_env } }
780 (* We maintain 2 states for a local: the type
781 * that the local currently has, and an expression_id generated from
782 * the last assignment to this local.
784 let set_local env x new_type
=
785 let new_type = match new_type with
786 | _
, Tunion
[ty] -> ty
788 match next_cont_opt env with
791 let expr_id = match LID.Map.get
x next_cont
.LEnvC.local_types
with
792 | None
-> Ident.tmp
()
793 | Some
(_
, y
) -> y
in
794 let local = new_type, expr_id in
795 set_local_ env x local
797 let is_using_var env x =
798 LID.Set.mem
x env.lenv
.local_using_vars
800 let set_using_var env x =
802 env.lenv
with local_using_vars
= LID.Set.add x env.lenv
.local_using_vars
} }
804 let unset_local env local =
805 let {per_cont_env; local_using_vars
; local_mutability
;
806 local_reactive
; } = env.lenv
in
807 let per_cont_env = LEnvC.remove_from_cont
C.Next
local per_cont_env in
808 let local_using_vars = LID.Set.remove
local local_using_vars in
809 let local_mutability = LID.Map.remove
local local_mutability in
811 lenv
= {per_cont_env; local_using_vars;
812 local_mutability; local_reactive
} }
816 let add_mutable_var env local mutability_type
=
819 (LID.Map.add local mutability_type
env.lenv
.local_mutability)
821 let local_is_mutable ~include_borrowed
env id
=
822 let module TME
= Typing_mutability_env
in
823 match LID.Map.get id
(get_env_mutability env) with
824 | Some
(_
, TME.Mutable
) -> true
825 | Some
(_
, TME.Borrowed
) -> include_borrowed
829 let dynamic_view_enabled = TypecheckerOptions.dynamic_view
(get_tcopt env) in
830 if dynamic_view_enabled then Tdynamic
else Typing_defs.make_tany
()
832 let get_local_in_ctx env ?error_if_undef_at_pos
:p
x ctx_opt
=
833 let not_found_is_ok x ctx
=
834 let xstr = LID.to_string
x in
835 (xstr = SG.globals
|| SG.is_superglobal
xstr) && not
(is_strict env) ||
836 Fake.is_valid ctx
.LEnvC.fake_members
x in
837 let error_if_pos_provided posopt ctx
=
840 let in_rx_scope = env_local_reactive env in
841 let lid = LID.to_string
x in
843 let suggest_most_similar lid =
844 let all_locals = Sequence.of_list
(LID.Map.elements ctx
.LEnvC.local_types
) in
845 let var_name = (fun (k
, _
) -> LID.to_string k
) in
846 match most_similar lid all_locals var_name with
847 | Some
(k
, _
) -> Some
(LID.to_string k
)
850 Errors.undefined ~
in_rx_scope p
lid (suggest_most_similar lid)
854 (* If the continuation is absent, we are in dead code so the variable should
855 have type nothing. *)
856 Some
(Typing_make_type.nothing
Reason.Rnone
, 0)
859 let lcl = LID.Map.get
x ctx
.LEnvC.local_types
in
861 | None
-> if not_found_is_ok x ctx
then () else error_if_pos_provided p ctx
866 let get_local_ty_in_ctx env ?error_if_undef_at_pos
x ctx_opt
=
867 match get_local_in_ctx env ?error_if_undef_at_pos
x ctx_opt
with
868 | None
-> false, (Reason.Rnone
, tany env)
869 | Some
(x, _
) -> true, x
871 let get_local_in_next_continuation ?error_if_undef_at_pos
:p
env x =
872 let next_cont = next_cont_opt env in
873 get_local_ty_in_ctx env ?error_if_undef_at_pos
:p
x next_cont
875 let get_local_ ?error_if_undef_at_pos
:p
env x =
876 get_local_in_next_continuation ?error_if_undef_at_pos
:p
env x
878 let get_local env x = snd
(get_local_ env x)
880 let get_locals env plids
=
881 let next_cont = next_cont_opt env in
882 List.fold plids ~init
:LID.Map.empty ~f
:(fun locals
(p
, lid) ->
883 match get_local_in_ctx env ~error_if_undef_at_pos
:p
lid next_cont with
885 | Some ty_eid
-> LID.Map.add lid ty_eid locals
)
887 let set_locals env locals
=
888 LID.Map.fold
(fun lid ty env -> set_local_ env lid ty) locals
env
890 let is_local_defined env x = fst
(get_local_ env x)
892 let get_local_check_defined env (p
, x) =
893 snd
(get_local_ ~error_if_undef_at_pos
:p
env x)
895 let set_local_expr_id env x new_eid
=
896 let per_cont_env = env.lenv
.per_cont_env in
897 match LEnvC.get_cont_option
C.Next
per_cont_env with
900 begin match LID.Map.get
x next_cont.LEnvC.local_types
with
901 | Some
(type_
, eid
) when eid
<> new_eid
->
902 let local = type_
, new_eid
in
903 let per_cont_env = LEnvC.add_to_cont
C.Next
x local per_cont_env in
904 let env ={ env with lenv
= { env.lenv
with per_cont_env } }
910 let get_local_expr_id env x =
911 match next_cont_opt env with
912 | None
-> (* dead code *) None
914 let lcl = LID.Map.get
x next_cont.LEnvC.local_types
in
915 Option.map
lcl ~f
:(fun (_
, x) -> x)
917 let set_fake_members env fake_members
=
919 LEnvC.update_cont_entry
C.Next
env.lenv
.per_cont_env
920 (fun entry
-> { entry
with LEnvC.fake_members
}) in
921 { env with lenv
= { env.lenv
with per_cont_env } }
923 let get_fake_members env =
924 match LEnvC.get_cont_option
C.Next
env.lenv
.per_cont_env with
926 | Some
next_cont -> next_cont.LEnvC.fake_members
928 let update_lost_info name blame
env ty =
929 let (pos, under_lambda
) =
931 | Fake.Blame_call
pos -> (pos, false)
932 | Fake.Blame_lambda
pos -> (pos, true) in
933 let info r
= Reason.Rlost_info
(name, r
, pos, under_lambda
) in
934 let rec update_ty env ty =
937 let env, v'
= get_var env v in
938 (match IMap.get
v'
env.tenv
with
942 let env, ty = update_ty env ty in
943 let env = add env v ty in
947 let env, tyl
= List.map_env
env tyl
update_ty in
948 env, (info r
, Tunion tyl
)
953 let forget_members env blame
=
954 let fake_members = get_fake_members env in
955 let fake_members = Fake.forget
fake_members blame
in
956 set_fake_members env fake_members
958 module FakeMembers
= struct
960 let update_fake_members env fake_members =
961 let per_cont_env = LEnvC.update_cont_entry
C.Next
env.lenv
.per_cont_env
963 LEnvC.{ entry
with fake_members }) in
964 { env with lenv
= { env.lenv
with per_cont_env } }
966 let is_valid env obj member_name
=
970 let fake_members = get_fake_members env in
971 let id = Fake.make_id obj member_name
in
972 Fake.is_valid fake_members id
975 let is_valid_static env cid member_name
=
976 let name = Fake.make_static_id cid member_name
in
977 let fake_members = get_fake_members env in
978 Fake.is_valid fake_members name
980 let check_static_invalid env cid member_name
ty =
981 let fake_members = get_fake_members env in
982 let fake_id = Fake.make_static_id cid member_name
in
983 match Fake.is_invalid
fake_members fake_id with
986 update_lost_info (Local_id.to_string
fake_id) blame
env ty
988 let check_instance_invalid env obj member_name
ty =
992 let fake_members = get_fake_members env in
993 let fake_id = Fake.make_id obj member_name
in
994 begin match Fake.is_invalid
fake_members fake_id with
997 update_lost_info (Local_id.to_string
fake_id) blame
env ty
1001 let add_member env fake_id =
1002 let fake_members = get_fake_members env in
1003 let fake_members = Fake.add fake_members fake_id in
1004 set_fake_members env fake_members
1006 let make env obj_name member_name
=
1007 let my_fake_local_id = Fake.make_id obj_name member_name
in
1008 let env = add_member env my_fake_local_id in
1009 env, my_fake_local_id
1011 let make_static env class_name member_name
=
1012 let my_fake_local_id = Fake.make_static_id class_name member_name
in
1013 let env = add_member env my_fake_local_id in
1014 env, my_fake_local_id
1018 (*****************************************************************************)
1019 (* Sets up/cleans up the environment when typing an anonymous function. *)
1020 (*****************************************************************************)
1022 let anon anon_lenv
env f
=
1023 (* Setting up the environment. *)
1024 let old_lenv = env.lenv
in
1025 let old_return = get_return env in
1026 let old_params = get_params env in
1027 let outer_fun_kind = get_fn_kind env in
1028 let env = { env with lenv
= anon_lenv
} in
1030 let env, tfun
, result
= f
env in
1031 (* Cleaning up the environment. *)
1032 let env = { env with lenv
= old_lenv } in
1033 let env = set_params env old_params in
1034 let env = set_return env old_return in
1035 let env = set_fn_kind env outer_fun_kind in
1039 let old_in_loop = env.in_loop in
1040 let env = { env with in_loop = true } in
1041 let env, result
= f
env in
1042 { env with in_loop = old_in_loop }, result
1045 let old_in_try = env.in_try in
1046 let env = { env with in_try = true } in
1047 let env, result
= f
env in
1048 { env with in_try = old_in_try }, result
1051 let old_in_case = env.in_case in
1052 let env = { env with in_case = true } in
1053 let env, result
= f
env in
1054 { env with in_case = old_in_case }, result
1056 (* Return the subset of env which is saved in the Typed AST's EnvAnnotation. *)
1057 let save local_tpenv
env =
1059 Tast.tcopt
= get_tcopt env;
1060 Tast.tenv
= env.tenv
;
1061 Tast.subst
= env.subst
;
1062 Tast.tpenv = TPEnv.union local_tpenv
env.global_tpenv
;
1063 Tast.reactivity
= env_reactivity
env;
1064 Tast.local_mutability = get_env_mutability env;
1065 Tast.fun_mutable
= function_is_mutable env;
1066 Tast.condition_types
= env.genv.condition_types
;
1069 (* Compute the type variables appearing covariantly (positively)
1070 * resp. contravariantly (negatively) in a given type ty.
1071 * Return a pair of sets of positive and negative type variables
1072 * (as well as an updated environment).
1074 let rec get_tyvars env ty =
1075 let get_tyvars_union (env, acc_positive
, acc_negative
) ty =
1076 let env, positive
, negative
= get_tyvars env ty in
1077 env, ISet.union acc_positive positive
, ISet.union acc_negative negative
in
1078 let get_tyvars_param (env, acc_positive
, acc_negative
) {fp_type
; fp_kind
; _
} =
1079 let env, positive
, negative
= get_tyvars env fp_type
.et_type
in
1081 (* Parameters are treated contravariantly *)
1083 env, ISet.union negative acc_positive
, ISet.union positive acc_negative
1084 (* Inout/ref parameters are both co- and contra-variant *)
1085 | FPinout
| FPref
->
1086 let tyvars = ISet.union negative positive
in
1087 env, ISet.union
tyvars acc_positive
, ISet.union
tyvars acc_negative
in
1088 let env, ety
= expand_type env ty in
1091 env, ISet.singleton
v, ISet.empty
1092 | Tany _
| Tnonnull
| Terr
| Tdynamic
| Tobject
| Tprim _
| Tanon _
->
1093 env, ISet.empty, ISet.empty
1096 | Ttuple tyl
| Tunion tyl
| Tintersection tyl
| Tdestructure tyl
->
1097 List.fold_left tyl ~init
:(env, ISet.empty, ISet.empty) ~f
:get_tyvars_union
1100 (fun _
{sft_ty
; _
} res
-> get_tyvars_union res sft_ty
)
1101 m
(env, ISet.empty, ISet.empty)
1103 let env, params_positive
, params_negative
=
1104 match ft
.ft_arity
with
1105 | Fstandard _
| Fellipsis _
-> (env, ISet.empty, ISet.empty)
1106 | Fvariadic
(_
, fp
) -> get_tyvars_param (env, ISet.empty, ISet.empty) fp
in
1107 let env, params_positive
, params_negative
=
1108 List.fold_left ft
.ft_params ~init
:(env, params_positive
, params_negative
)
1109 ~f
:get_tyvars_param in
1110 let env, ret_positive
, ret_negative
= get_tyvars env ft
.ft_ret
.et_type
in
1111 env, ISet.union ret_positive params_positive
, ISet.union ret_negative params_negative
1112 | Tabstract
(AKnewtype
(name, tyl
), _
) ->
1113 begin match get_typedef env name with
1114 | Some
{td_tparams
; _
} ->
1115 let variancel = List.map td_tparams
(fun t
-> t
.tp_variance
) in
1116 get_tyvars_variance_list
(env, ISet.empty, ISet.empty) variancel tyl
1117 | None
-> env, ISet.empty, ISet.empty
1119 | Tabstract
(_
, Some
ty) -> get_tyvars env ty
1120 | Tabstract
(_
, None
) -> env, ISet.empty, ISet.empty
1121 | Tclass
((_
, cid
), _
, tyl
) ->
1122 begin match get_class env cid
with
1124 let variancel = List.map
(Cls.tparams cls
) (fun t
-> t
.tp_variance
) in
1125 get_tyvars_variance_list
(env, ISet.empty, ISet.empty) variancel tyl
1126 | None
-> env, ISet.empty, ISet.empty
1130 | AKany
| AKempty
-> env, ISet.empty, ISet.empty
1131 | AKvarray
ty | AKvec
ty | AKvarray_or_darray
ty -> get_tyvars env ty
1132 | AKdarray
(ty1
, ty2
) | AKmap
(ty1
, ty2
) ->
1133 let env, positive1
, negative1
= get_tyvars env ty1
in
1134 let env, positive2
, negative2
= get_tyvars env ty2
in
1135 env, ISet.union positive1 positive2
, ISet.union negative1 negative2
1138 and get_tyvars_variance_list
(env, acc_positive
, acc_negative
) variancel tyl
=
1139 match variancel, tyl
with
1140 | variance
::variancel, ty::tyl
->
1141 let env, positive
, negative
= get_tyvars env ty in
1142 let acc_positive, acc_negative
=
1144 | Ast_defs.Covariant
->
1145 ISet.union
acc_positive positive
, ISet.union acc_negative negative
1146 | Ast_defs.Contravariant
->
1147 ISet.union
acc_positive negative
, ISet.union acc_negative positive
1148 | Ast_defs.Invariant
->
1149 let positive_or_negative = ISet.union positive negative
in
1150 ISet.union
acc_positive positive_or_negative,
1151 ISet.union acc_negative
positive_or_negative in
1152 get_tyvars_variance_list
(env, acc_positive, acc_negative
) variancel tyl
1153 | _
-> (env, acc_positive, acc_negative
)
1155 let rec set_tyvar_appears_covariantly env var
=
1156 let tvinfo = get_tyvar_info env var
in
1157 if tvinfo.appears_covariantly
1160 let env = env_with_tvenv env (IMap.add var
{ tvinfo with appears_covariantly
= true } env.tvenv
) in
1161 update_variance_of_tyvars_occurring_in_lower_bounds
env tvinfo.lower_bounds
1163 and set_tyvar_appears_contravariantly
env var
=
1164 let tvinfo = get_tyvar_info env var
in
1165 if tvinfo.appears_contravariantly
1168 let env = env_with_tvenv env (IMap.add var
{ tvinfo with appears_contravariantly
= true } env.tvenv
) in
1169 update_variance_of_tyvars_occurring_in_upper_bounds
env tvinfo.upper_bounds
1171 and update_variance_of_tyvars_occurring_in_lower_bounds
env tys
=
1173 (fun ty env -> update_variance_of_tyvars_occurring_in_lower_bound
env ty)
1176 and update_variance_of_tyvars_occurring_in_upper_bounds
env tys
=
1178 (fun ty env -> update_variance_of_tyvars_occurring_in_upper_bound
env ty)
1181 and update_variance_of_tyvars_occurring_in_lower_bound
env ty =
1182 let env, ety
= expand_type env ty in
1186 let env, positive
, negative
= get_tyvars env ty in
1189 (fun var
env -> set_tyvar_appears_covariantly env var
)
1193 (fun var
env -> set_tyvar_appears_contravariantly
env var
)
1197 and update_variance_of_tyvars_occurring_in_upper_bound
env ty =
1198 let env, ety
= expand_type env ty in
1202 let env, positive
, negative
= get_tyvars env ty in
1205 (fun var
env -> set_tyvar_appears_contravariantly
env var
)
1209 (fun var
env -> set_tyvar_appears_covariantly env var
)
1213 (* After a type variable var has been "solved", or bound to a type ty, we need
1214 * to update the variance of type variables occurring in ty. Suppose that
1215 * variable var is marked "appears covariantly", i.e. it appears (at least) in
1216 * positive positions in the type of an expression. Then when we substitute ty
1217 * for var, variables that appear positively in ty must now be marked as
1218 * appearing covariantly; variables that appear negatively in ty must now be
1219 * marked as appearing contravariantly. And the dual, if the variable var is marked
1220 * "appears contravariantly".
1222 and update_variance_after_bind
env var
ty =
1223 let appears_contravariantly = get_tyvar_appears_contravariantly env var
in
1224 let appears_covariantly = get_tyvar_appears_covariantly env var
in
1225 let env, positive
, negative
= get_tyvars env ty in
1230 if appears_contravariantly then set_tyvar_appears_contravariantly
env var
else env in
1231 if appears_covariantly then set_tyvar_appears_covariantly env var
else env)
1237 if appears_contravariantly then set_tyvar_appears_covariantly env var
else env in
1238 if appears_covariantly then set_tyvar_appears_contravariantly
env var
else env)
1242 let set_tyvar_variance env ?
(flip
= false) ty =
1243 log_env_change "set_tyvar_variance" env @@
1244 let tyvars = get_current_tyvars env in
1245 let env, positive
, negative
= get_tyvars env ty in
1246 let (positive
, negative
) = if flip
then (negative
, positive
) else (positive
, negative
) in
1247 List.fold_left
tyvars ~init
:env ~f
:(fun env var
->
1248 let env = if ISet.mem var positive
then set_tyvar_appears_covariantly env var
else env in
1249 let env = if ISet.mem var negative
then set_tyvar_appears_contravariantly
env var
else env in
1252 let fresh_invariant_type_var env p
=
1253 let v = Ident.tmp
() in
1255 log_env_change "fresh_invariant_type_var" env @@
1256 let env = add_current_tyvar env p
v in
1257 let env = set_tyvar_appears_covariantly env v in
1258 let env = set_tyvar_appears_contravariantly
env v in
1260 env, (Reason.Rtype_variable p
, Tvar
v)
1262 (* Add a single new upper bound [ty] to type variable [var] in [env.tvenv].
1263 * If the optional [intersect] operation is supplied, then use this to avoid
1264 * adding redundant bounds by merging the type with existing bounds. This makes
1265 * sense because a conjunction of upper bounds
1266 * (v <: t1) /\ ... /\ (v <: tn)
1267 * is equivalent to a single upper bound
1268 * v <: (t1 & ... & tn)
1270 let add_tyvar_upper_bound ?intersect
env var
ty =
1271 log_env_change "add_tyvar_upper_bound" env @@
1272 (* Don't add superfluous v <: v or v <: ?v to environment *)
1273 if is_tvar ~elide_nullable
:true ty var
then env else
1274 let tvinfo = get_tyvar_info env var
in
1275 let upper_bounds = match intersect
with
1276 | None
-> ty ++ tvinfo.upper_bounds
1278 TySet.of_list
(intersect
ty (TySet.elements
tvinfo.upper_bounds)) in
1279 let env = env_with_tvenv env
1280 (IMap.add var
{ tvinfo with upper_bounds } env.tvenv
) in
1281 if get_tyvar_appears_contravariantly env var
1282 then update_variance_of_tyvars_occurring_in_upper_bound
env ty
1285 (* Remove type variable `upper_var` from the upper bounds on `var`, if it exists
1287 let remove_tyvar_upper_bound env var upper_var
=
1288 log_env_change "remove_tyvar_upper_bound" env @@
1289 let tvinfo = get_tyvar_info env var
in
1290 let upper_bounds = TySet.filter
1291 (fun ty -> match expand_type env ty with _
, (_
, Tvar
v) -> v <> upper_var
| _
-> true)
1292 tvinfo.upper_bounds in
1293 env_with_tvenv env (IMap.add var
{ tvinfo with upper_bounds } env.tvenv
)
1296 (* Remove type variable `lower_var` from the lower bounds on `var`, if it exists
1298 let remove_tyvar_lower_bound env var lower_var
=
1299 log_env_change "remove_tyvar_lower_bound var" env @@
1300 let tvinfo = get_tyvar_info env var
in
1301 let lower_bounds = TySet.filter
1302 (fun ty -> match expand_type env ty with _
, (_
, Tvar
v) -> v <> lower_var
| _
-> true)
1303 tvinfo.lower_bounds in
1304 env_with_tvenv env (IMap.add var
{ tvinfo with lower_bounds } env.tvenv
)
1307 (* Add a single new upper bound [ty] to type variable [var] in [env.tvenv].
1308 * If the optional [union] operation is supplied, then use this to avoid
1309 * adding redundant bounds by merging the type with existing bounds. This makes
1310 * sense because a conjunction of lower bounds
1311 * (t1 <: v) /\ ... /\ (tn <: v)
1312 * is equivalent to a single lower bound
1313 * (t1 | ... | tn) <: v
1315 let add_tyvar_lower_bound ?union
env var
ty =
1316 log_env_change "add_tyvar_lower_bound" env @@
1317 (* Don't add superfluous v <: v to environment *)
1318 if is_tvar ~elide_nullable
:false ty var
then env else
1319 let tvinfo = get_tyvar_info env var
in
1320 let lower_bounds = match union
with
1321 | None
-> ty ++ tvinfo.lower_bounds
1323 TySet.of_list
(union
ty (TySet.elements
tvinfo.lower_bounds)) in
1324 let env = env_with_tvenv env
1325 (IMap.add var
{ tvinfo with lower_bounds } env.tvenv
) in
1326 if get_tyvar_appears_covariantly env var
1327 then update_variance_of_tyvars_occurring_in_lower_bound
env ty