separate typing_env_types and typing_env
[hiphop-php.git] / hphp / hack / src / typing / typing_env.ml
blobc4cff4f27856a76ca56511bd54f2d291a5abb2da
1 (*
2 * Copyright (c) 2015, Facebook, Inc.
3 * All rights reserved.
5 * This source code is licensed under the MIT license found in the
6 * LICENSE file in the "hack" directory of this source tree.
8 *)
10 open Core_kernel
11 open Common
12 open Typing_env_types
13 open Decl_env
14 open Typing_defs
15 open Aast
16 open Typing_env_return_info
18 module Dep = Typing_deps.Dep
19 module LID = Local_id
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
47 then begin
48 let pos =
49 match old_env.tyvars_stack with
50 | (p,_)::_ -> p
51 | _ -> old_env.function_pos in
52 !env_log_function pos name old_env new_env
53 end;
54 new_env
56 let add_subst env x x' =
57 if x <> x'
58 then { env with subst = IMap.add x x' env.subst }
59 else env
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
65 (match x' with
66 | None -> env, x
67 | Some x' ->
68 let env, x' = get_var env x' in
69 let env = add_subst env x x' in
70 env, x'
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. *)
76 let rename env x x' =
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
80 env
82 let add env x ty =
83 let env, x = get_var env x in
84 match ty with
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 ->
106 let tyvar =
107 match variance with
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 }
116 | _ -> env
118 let fresh_type_reason ?variance env r =
119 let v = Ident.tmp () in
120 let env =
121 log_env_change "fresh_type" env @@
122 add_current_tyvar ?variance env (Reason.to_pos r) v in
123 env, (r, Tvar v)
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
138 | [] -> []
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
144 match ty with
145 | None -> env, (x_reason, Tvar x)
146 | Some ty -> env, ty
148 let get_type_unsafe env x =
149 let ty = IMap.get x env.tenv in
150 match ty with
151 | None ->
152 env, (Reason.none, Typing_defs.make_tany ())
153 | Some ty -> env, ty
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)
169 else env, ty
171 let expand_type env x =
172 match x with
173 | r, Tvar x -> expand_var env r x
174 | x -> env, 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
188 let get_tpenv 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
212 | _ -> Erased
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
218 local || global
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
224 local || global
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 =
233 { env with lenv =
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 =
242 let tpenv =
243 begin match ty with
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
248 end in
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 =
319 match ty with
320 | (_, Tvar var') -> var = var'
321 | (_, Toption ty) when elide_nullable -> is_tvar ~elide_nullable ty var
322 | _ -> false
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 =
370 let rec iterate i =
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
375 let env =
376 env_with_tpenv env
377 (TPEnv.add
378 name
379 TPEnv.{
380 lower_bounds = empty_bounds;
381 upper_bounds = empty_bounds;
382 reified;
383 enforceable;
384 newable
386 (get_tpenv env)) in
387 env, name
389 let is_fresh_generic_parameter name =
390 String.contains name '#' && not (AbstractKind.is_generic_dep_ty name)
392 let tparams_visitor env =
393 object(this)
394 inherit [SSet.t] Type_visitor.type_visitor
395 method! on_tabstract acc _ ak _ty_opt =
396 match ak with
397 | AKgeneric s -> SSet.add s acc
398 | _ -> acc
399 method! on_tvar acc r ix =
400 let _env, ty = get_type env r ix in
401 begin match ty with
402 | _, Tvar _ -> acc
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 ->
411 let folder ty acc =
412 match ty with
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 =
422 { env with lenv =
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;
439 tenv = IMap.empty;
440 subst = IMap.empty;
441 fresh_typarams = SSet.empty;
442 lenv = initial_local TPEnv.empty Nonreactive;
443 in_loop = false;
444 in_try = false;
445 in_case = false;
446 inside_constructor = false;
447 inside_ppl_class = false;
448 decl_env = {
449 mode;
450 droot;
451 decl_tcopt = tcopt;
453 genv = {
454 tcopt = tcopt;
455 return = {
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;
465 self_id = "";
466 self = Reason.none, Typing_defs.make_tany ();
467 static = false;
468 val_kind = Other;
469 parent_id = "";
470 parent = Reason.none, Typing_defs.make_tany ();
471 fun_kind = Ast_defs.FSync;
472 fun_mutable = None;
473 anons = IMap.empty;
474 file = file;
476 global_tpenv = TPEnv.empty;
477 subtype_prop = TL.valid;
478 log_levels = TypecheckerOptions.log_levels tcopt;
479 tvenv = IMap.empty;
480 tyvars_stack = [];
481 allow_wildcards = false;
482 big_envs = ref [];
483 pessimize = 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 =
504 { env with genv = {
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 =
516 env.genv.fun_mutable
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 ()
527 | _ -> ()
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 =
535 add_wclass env x;
536 Decl_provider.get_typedef x
538 let is_typedef x =
539 match Naming_table.Types.get_pos x with
540 | Some (_p, Naming_table.TTypedef) -> true
541 | _ -> false
543 let get_class env x =
544 add_wclass 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;
549 get_class env x
551 let get_enum_constraint env x =
552 match get_class env x with
553 | None -> None
554 | Some tc ->
555 match (Cls.enum_type tc) with
556 | None -> None
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
565 let get_enum env x =
566 add_wclass env x;
567 match Decl_provider.get_class x with
568 | Some tc when (Cls.enum_type tc) <> None -> Some tc
569 | _ -> None
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_);
596 let add_dep x =
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
604 * originated.
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);
609 ce_opt
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 ->
619 match acc with
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, _); _}) ->
626 (Reason.to_pos r, x)
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_);
638 let add_dep x =
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
646 * originated.
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);
651 ce_opt
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_);
660 let add_dep x =
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_)
669 let get_return env =
670 env.genv.return
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 }
677 let get_params env =
678 env.genv.params
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
691 let with_env env f =
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
697 env, result
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 }
733 let set_self env x =
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 }
748 let set_static env =
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 (*****************************************************************************)
773 (* Locals *)
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
787 | _ -> new_type in
788 match next_cont_opt env with
789 | None -> env
790 | Some next_cont ->
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 =
801 { env with lenv = {
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
810 let env = { env with
811 lenv = {per_cont_env; local_using_vars;
812 local_mutability; local_reactive} }
816 let add_mutable_var env local mutability_type =
817 env_with_mut
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
826 | _ -> false
828 let tany env =
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 =
838 match posopt with
839 | Some p ->
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)
848 | None -> None
850 Errors.undefined ~in_rx_scope p lid (suggest_most_similar lid)
851 | None -> () in
852 match ctx_opt with
853 | None ->
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)
858 | Some ctx ->
859 let lcl = LID.Map.get x ctx.LEnvC.local_types in
860 begin match lcl with
861 | None -> if not_found_is_ok x ctx then () else error_if_pos_provided p ctx
862 | Some _ -> ()
863 end;
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
884 | None -> locals
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
898 | None -> env
899 | Some next_cont ->
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 } }
907 | _ -> env
910 let get_local_expr_id env x =
911 match next_cont_opt env with
912 | None -> (* dead code *) None
913 | Some next_cont ->
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 =
918 let per_cont_env =
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
925 | None -> Fake.empty
926 | Some next_cont -> next_cont.LEnvC.fake_members
928 let update_lost_info name blame env ty =
929 let (pos, under_lambda) =
930 match blame with
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 =
935 match ty with
936 | _, Tvar v ->
937 let env, v' = get_var env v in
938 (match IMap.get v' env.tenv with
939 | None ->
940 env, ty
941 | Some ty ->
942 let env, ty = update_ty env ty in
943 let env = add env v ty in
944 env, ty
946 | r, Tunion tyl ->
947 let env, tyl = List.map_env env tyl update_ty in
948 env, (info r, Tunion tyl)
949 | r, ty ->
950 env, (info r, ty) in
951 update_ty env ty
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
962 (fun entry ->
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 =
967 match obj with
968 | _, This
969 | _, Lvar _ ->
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
973 | _ -> false
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
984 | None -> env, ty
985 | Some blame ->
986 update_lost_info (Local_id.to_string fake_id) blame env ty
988 let check_instance_invalid env obj member_name ty =
989 match obj with
990 | _, This
991 | _, Lvar _ ->
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
995 | None -> env, ty
996 | Some blame ->
997 update_lost_info (Local_id.to_string fake_id) blame env ty
999 | _ -> 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
1029 (* Typing *)
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
1036 env, tfun, result
1038 let in_loop env f =
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
1044 let in_try env f =
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
1050 let in_case env f =
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
1080 match fp_kind with
1081 (* Parameters are treated contravariantly *)
1082 | FPnormal ->
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
1089 match snd ety with
1090 | Tvar v ->
1091 env, ISet.singleton v, ISet.empty
1092 | Tany _ | Tnonnull | Terr | Tdynamic | Tobject | Tprim _ | Tanon _ ->
1093 env, ISet.empty, ISet.empty
1094 | Toption ty ->
1095 get_tyvars env ty
1096 | Ttuple tyl | Tunion tyl | Tintersection tyl | Tdestructure tyl ->
1097 List.fold_left tyl ~init:(env, ISet.empty, ISet.empty) ~f:get_tyvars_union
1098 | Tshape (_, m) ->
1099 Nast.ShapeMap.fold
1100 (fun _ {sft_ty; _} res -> get_tyvars_union res sft_ty)
1101 m (env, ISet.empty, ISet.empty)
1102 | Tfun ft ->
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
1123 | Some cls ->
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
1128 | Tarraykind ak ->
1129 begin match ak with
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 =
1143 match variance with
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
1158 then env
1159 else
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
1166 then env
1167 else
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 =
1172 TySet.fold
1173 (fun ty env -> update_variance_of_tyvars_occurring_in_lower_bound env ty)
1174 tys env
1176 and update_variance_of_tyvars_occurring_in_upper_bounds env tys =
1177 TySet.fold
1178 (fun ty env -> update_variance_of_tyvars_occurring_in_upper_bound env ty)
1179 tys env
1181 and update_variance_of_tyvars_occurring_in_lower_bound env ty =
1182 let env, ety = expand_type env ty in
1183 match snd ety with
1184 | Tvar _ -> env
1185 | _ ->
1186 let env, positive, negative = get_tyvars env ty in
1187 let env =
1188 ISet.fold
1189 (fun var env -> set_tyvar_appears_covariantly env var)
1190 positive env in
1191 let env =
1192 ISet.fold
1193 (fun var env -> set_tyvar_appears_contravariantly env var)
1194 negative env in
1197 and update_variance_of_tyvars_occurring_in_upper_bound env ty =
1198 let env, ety = expand_type env ty in
1199 match snd ety with
1200 | Tvar _ -> env
1201 | _ ->
1202 let env, positive, negative = get_tyvars env ty in
1203 let env =
1204 ISet.fold
1205 (fun var env -> set_tyvar_appears_contravariantly env var)
1206 positive env in
1207 let env =
1208 ISet.fold
1209 (fun var env -> set_tyvar_appears_covariantly env var)
1210 negative env in
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
1226 let env =
1227 ISet.fold
1228 (fun var env ->
1229 let env =
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)
1232 positive env in
1233 let env =
1234 ISet.fold
1235 (fun var env ->
1236 let 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)
1239 negative env in
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
1250 env)
1252 let fresh_invariant_type_var env p =
1253 let v = Ident.tmp () in
1254 let env =
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
1259 env 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
1277 | Some intersect ->
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
1283 else env
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
1322 | Some union ->
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
1328 else env