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.
10 include Typing_env_types
15 open Typing_env_return_info
16 open Type_parameter_env
18 module Dep
= Typing_deps.Dep
19 module TLazyHeap
= Typing_lazy_heap
20 module LEnvC
= Typing_lenv_cont
21 module Cont
= Typing_continuations
23 let show_env _
= "<env>"
24 let pp_env _ _
= Printf.printf
"%s\n" "<env>"
26 let ( ++ ) x y
= Typing_set.add x y
28 let get_tcopt env
= env
.genv
.tcopt
33 Reason.none
, Tvar
(Ident.tmp
())
35 let add_subst env x x'
=
37 then { env
with subst
= IMap.add x x' env
.subst
}
40 (* Apply variable-to-variable substitution from environment. Update environment
41 if we ended up iterating (cf path compression in union-find) *)
42 let rec get_var env x
=
43 let x'
= IMap.get
x env
.subst
in
47 let env, x'
= get_var env x'
in
48 let env = add_subst env x x'
in
52 (* This is basically union from union-find, but without balancing
53 * (linking the smaller tree to the larger tree). In practice this
54 * isn't important: path compression is much more significant. *)
56 let env, x = get_var env x in
57 let env, x'
= get_var env x'
in
58 let env = add_subst env x x'
in
62 let env, x = get_var env x in
64 | _
, Tvar
x'
-> add_subst env x x'
65 | _
-> { env with tenv
= IMap.add x ty
env.tenv
}
67 let fresh_unresolved_type env =
68 let v = Ident.tmp
() in
69 add env v (Reason.Rnone
, Tunresolved
[]), (Reason.Rnone
, Tvar
v)
71 let get_type env x_reason
x =
72 let env, x = get_var env x in
73 let ty = IMap.get
x env.tenv
in
75 | None
-> env, (x_reason
, Tany
)
78 let get_type_unsafe env x =
79 let ty = IMap.get
x env.tenv
in
82 env, (Reason.none
, Tany
)
85 let expand_type env x =
87 | r
, Tvar
x -> get_type env r
x
90 let make_ft p reactivity is_coroutine params ret_ty
=
91 let arity = List.length params
in
96 ft_is_coroutine
= is_coroutine
;
97 ft_arity
= Fstandard
(arity, arity);
99 ft_where_constraints
= [];
102 ft_ret_by_ref
= false;
103 ft_reactive
= reactivity
;
104 ft_return_disposable
= false;
105 ft_returns_mutable
= false;
107 ft_decl_errors
= None
;
108 ft_returns_void_to_rx
= false;
111 let get_shape_field_name = function
112 | Ast.SFlit
(_
, s
) -> s
113 | Ast.SFclass_const
((_
, s1
), (_
, s2
)) -> s1^
"::"^s2
115 let empty_bounds = TySet.empty
116 let singleton_bound ty = TySet.singleton
ty
118 let get_tpenv_lower_bounds tpenv name
=
119 match SMap.get name tpenv
with
120 | None
-> empty_bounds
121 | Some
{lower_bounds
; _
} -> lower_bounds
123 let get_tpenv_upper_bounds tpenv name
=
124 match SMap.get name tpenv
with
125 | None
-> empty_bounds
126 | Some
{upper_bounds
; _
} -> upper_bounds
128 let get_lower_bounds env name
=
129 let local = get_tpenv_lower_bounds env.lenv
.tpenv name
in
130 let global = get_tpenv_lower_bounds env.global_tpenv name
in
131 TySet.union
local global
133 let get_upper_bounds env name
=
134 let local = get_tpenv_upper_bounds env.lenv
.tpenv name
in
135 let global = get_tpenv_upper_bounds env.global_tpenv name
in
136 TySet.union
local global
138 (* Get bounds that are both an upper and lower of a given generic *)
139 let get_equal_bounds env name
=
140 let lower = get_lower_bounds env name
in
141 let upper = get_upper_bounds env name
in
142 TySet.inter
lower upper
144 let is_generic_param ty name
=
146 | (_
, Tabstract
(AKgeneric name'
, None
)) -> name
= name'
149 (* Add a single new upper bound [ty] to generic parameter [name] in [tpenv] *)
150 let add_upper_bound_ tpenv name
ty =
151 if is_generic_param ty name
153 else match SMap.get name tpenv
with
156 {lower_bounds
= empty_bounds; upper_bounds
= singleton_bound ty} tpenv
157 | Some
{lower_bounds
; upper_bounds
} ->
159 {lower_bounds
; upper_bounds
= ty++upper_bounds
} tpenv
161 (* Add a single new lower bound [ty] to generic parameter [name] in [tpenv] *)
162 let add_lower_bound_ tpenv name
ty =
163 if is_generic_param ty name
166 match SMap.get name tpenv
with
169 {lower_bounds
= singleton_bound ty; upper_bounds
= empty_bounds} tpenv
170 | Some
{lower_bounds
; upper_bounds
} ->
172 {lower_bounds
= ty++lower_bounds
; upper_bounds
} tpenv
174 let env_with_tpenv env tpenv
=
175 { env with lenv
= { env.lenv
with tpenv
= tpenv
} }
177 let env_with_global_tpenv env global_tpenv
=
178 { env with global_tpenv
}
180 let add_upper_bound_global env name
ty =
183 | (r
, Tabstract
(AKgeneric formal_super
, _
)) ->
184 add_lower_bound_ env.global_tpenv formal_super
185 (r
, Tabstract
(AKgeneric name
, None
))
186 | _
-> env.global_tpenv
188 { env with global_tpenv
=(add_upper_bound_ tpenv name
ty) }
190 let add_upper_bound env name
ty =
193 | (r
, Tabstract
(AKgeneric formal_super
, _
)) ->
194 add_lower_bound_ env.lenv
.tpenv formal_super
195 (r
, Tabstract
(AKgeneric name
, None
))
196 | _
-> env.lenv
.tpenv
198 env_with_tpenv env (add_upper_bound_ tpenv name
ty)
200 let add_lower_bound env name
ty =
203 | (r
, Tabstract
(AKgeneric formal_sub
, _
)) ->
204 add_upper_bound_ env.lenv
.tpenv formal_sub
205 (r
, Tabstract
(AKgeneric name
, None
))
206 | _
-> env.lenv
.tpenv
208 env_with_tpenv env (add_lower_bound_ tpenv name
ty)
210 (* Add type parameters to environment, initially with no bounds.
211 * Existing type parameters with the same name will be overridden. *)
212 let add_generic_parameters env tparaml
=
213 let add_empty_bounds tpenv (_
, (_
, name
), _
) =
214 SMap.add name
{lower_bounds
= empty_bounds;
215 upper_bounds
= empty_bounds} tpenv in
217 (List.fold_left tparaml ~f
:add_empty_bounds ~init
:env.lenv
.tpenv)
219 let is_generic_parameter env name
=
220 SMap.mem name
env.lenv
.tpenv
222 let get_generic_parameters env =
223 SMap.keys
(SMap.union
env.lenv
.tpenv env.global_tpenv
)
225 let get_tpenv_size env =
226 let local = SMap.fold
(fun _x
{ lower_bounds
; upper_bounds
} count
->
227 count
+ TySet.cardinal lower_bounds
+ TySet.cardinal upper_bounds
)
229 SMap.fold
(fun _x
{ lower_bounds
; upper_bounds
} count
->
230 count
+ TySet.cardinal lower_bounds
+ TySet.cardinal upper_bounds
)
231 env.global_tpenv
local
233 (* Generate a fresh generic parameter with a specified prefix but distinct
234 * from all generic parameters in the environment *)
235 let add_fresh_generic_parameter env prefix
=
237 let name = Printf.sprintf
"%s#%d" prefix i
in
238 if is_generic_parameter env name then iterate (i
+1) else name in
239 let name = iterate 1 in
242 (SMap.add name {lower_bounds
= empty_bounds;
243 upper_bounds
= empty_bounds} env.lenv
.tpenv) in
246 let is_fresh_generic_parameter name =
247 String.contains
name '#'
&& not
(AbstractKind.is_generic_dep_ty
name)
249 let tparams_visitor env =
251 inherit [SSet.t
] Type_visitor.type_visitor
252 method! on_tabstract acc _ ak _ty_opt
=
254 | AKgeneric s
-> SSet.add s acc
256 method! on_tvar acc r ix
=
257 let _env, ty = get_type env r ix
in
260 let get_tparams_aux env acc
ty = (tparams_visitor env)#on_type acc
ty
261 let get_tparams env ty = get_tparams_aux env SSet.empty
ty
263 let get_tpenv_tparams env =
264 SMap.fold
begin fun _x
{ lower_bounds
; upper_bounds
} acc
->
267 | _
, Tabstract
(AKgeneric _
, _
) -> acc
268 | _
-> get_tparams_aux env acc
ty in
269 TySet.fold
folder lower_bounds
@@
270 TySet.fold
folder upper_bounds acc
272 env.lenv
.tpenv SSet.empty
274 (* Replace types for locals with empty environment *)
275 let env_with_locals env locals history
=
277 env.lenv
with local_types
= locals
; local_type_history
= history
;
281 let empty_fake_members = {
283 invalid
= SSet.empty
;
287 let empty_local tpenv local_reactive
= {
289 fake_members
= empty_fake_members;
290 local_types
= Typing_continuations.Map.empty
;
291 local_using_vars
= Local_id.Set.empty
;
292 local_type_history
= Local_id.Map.empty
;
293 local_mutability
= Local_id.Map.empty
;
294 local_reactive
= local_reactive
;
297 let empty tcopt file ~droot
= {
298 function_pos
= Pos.none
;
300 outer_pos
= Pos.none
;
301 outer_reason
= Reason.URnone
;
304 lenv
= empty_local SMap.empty Nonreactive
;
307 inside_constructor
= false;
309 mode
= FileInfo.Mstrict
;
316 return_type
= fresh_type ();
317 return_disposable
= false;
318 return_mutable
= false;
319 return_explicit
= false;
320 return_by_ref
= false;
321 return_void_to_rx
= false;
323 params
= Local_id.Map.empty;
325 self
= Reason.none
, Tany
;
328 parent
= Reason.none
, Tany
;
329 fun_kind
= Ast.FSync
;
334 global_tpenv
= SMap.empty
337 let set_env_reactive env reactive
=
338 { env with lenv
= {env.lenv
with local_reactive
= reactive
}}
340 let set_env_function_pos env function_pos
=
341 { env with function_pos
}
343 let lambda_reactive = ref None
345 let env_reactivity env =
346 Option.value !lambda_reactive ~default
:env.lenv
.local_reactive
348 (* Some form (strict/shallow/local) of reactivity *)
349 let env_local_reactive env =
350 env_reactivity env <> Nonreactive
352 let function_is_mutable env =
355 let set_fun_mutable env mut
=
356 { env with genv
= {env.genv
with fun_mutable
= mut
}}
358 (* Takes in the typechecking function of a lambda
359 block and checks if it breaks reactivity rules *)
360 let check_lambda_reactive f
=
361 let old_lambda_reactive = !lambda_reactive in
362 lambda_reactive := Some
(Reactive None
);
363 let results = f
() in
364 let result = !lambda_reactive in
365 lambda_reactive := old_lambda_reactive;
367 | Some c
-> c
, results
368 | None
-> assert false
370 let not_lambda_reactive () =
371 lambda_reactive := (match !lambda_reactive with
372 | Some _
-> Some Nonreactive
375 let is_checking_lambda () =
376 Option.is_some
!lambda_reactive
378 let error_if_reactive_context env f
=
379 not_lambda_reactive ();
380 if env_local_reactive env then f
()
382 let error_if_shallow_reactive_context env f
=
383 not_lambda_reactive ();
384 match env_reactivity env with
385 | Reactive _
| Shallow _
-> f
()
388 let add_wclass env x =
389 let dep = Dep.Class
x in
390 Option.iter
env.decl_env
.droot
(fun root
-> Typing_deps.add_idep root
dep);
393 let get_typedef env x =
395 TLazyHeap.get_typedef env.genv
.tcopt
x
398 match Naming_heap.TypeIdHeap.get
x with
399 | Some
(_p
, `Typedef
) -> true
402 let get_class env x =
404 TLazyHeap.get_class env.genv
.tcopt
x
406 let get_enum_constraint env x =
407 match get_class env x with
410 match tc
.tc_enum_type
with
412 | Some e
-> e
.te_constraint
414 let add_wclass env x =
415 let dep = Dep.Class
x in
416 Option.iter
env.decl_env
.droot
(fun root
-> Typing_deps.add_idep root
dep);
420 let env_with_mut env local_mutability
=
421 { env with lenv
= { env.lenv
with local_mutability
} }
423 let get_env_mutability env =
424 env.lenv
.local_mutability
426 (* When we want to type something with a fresh typing environment *)
427 let fresh_tenv env f
=
430 lenv
= empty_local env.lenv
.tpenv env.lenv
.local_reactive
;
436 match TLazyHeap.get_class env.genv
.tcopt
x with
437 | Some tc
when tc
.tc_enum_type
<> None
-> Some tc
440 let is_enum env x = get_enum env x <> None
442 let get_typeconst env class_ mid
=
443 add_wclass env class_
.tc_name
;
444 let dep = Dep.Const
(class_
.tc_name
, mid
) in
445 Option.iter
env.decl_env
.droot
(fun root
-> Typing_deps.add_idep root
dep);
446 SMap.get mid class_
.tc_typeconsts
448 (* Used to access class constants. *)
449 let get_const env class_ mid
=
450 add_wclass env class_
.tc_name
;
451 let dep = Dep.Const
(class_
.tc_name
, mid
) in
452 Option.iter
env.decl_env
.droot
(fun root
-> Typing_deps.add_idep root
dep);
453 SMap.get mid class_
.tc_consts
455 (* Used to access "global constants". That is constants that were
456 * introduced with "const X = ...;" at topelevel, or "define('X', ...);"
458 let get_gconst env cst_name
=
459 let dep = Dep.GConst cst_name
in
460 Option.iter
env.decl_env
.droot
(fun root
-> Typing_deps.add_idep root
dep);
461 TLazyHeap.get_gconst env.genv
.tcopt cst_name
463 let get_static_member is_method
env class_ mid
=
464 add_wclass env class_
.tc_name
;
466 let dep = if is_method
then Dep.SMethod
(x, mid
)
467 else Dep.SProp
(x, mid
) in
468 Option.iter
env.decl_env
.droot
(fun root
-> Typing_deps.add_idep root
dep);
470 add_dep class_
.tc_name
;
471 (* The type of a member is stored separately in the heap. This means that
472 * any user of the member also has a dependency on the class where the member
475 let ce_opt = if is_method
then SMap.get mid class_
.tc_smethods
476 else SMap.get mid class_
.tc_sprops
in
477 Option.iter
ce_opt (fun ce
-> add_dep ce
.ce_origin
);
480 let suggest_member members mid
=
481 let members = SMap.fold
begin fun x {ce_type
= lazy (r
, _
); _
} acc
->
482 let pos = Reason.to_pos r
in
483 SMap.add (String.lowercase_ascii
x) (pos, x) acc
484 end members SMap.empty
488 let suggest_static_member is_method class_ mid
=
489 let mid = String.lowercase_ascii
mid in
490 let members = if is_method
then class_
.tc_smethods
else class_
.tc_sprops
in
491 suggest_member members mid
493 let get_member is_method
env class_
mid =
494 add_wclass env class_
.tc_name
;
496 let dep = if is_method
then Dep.Method
(x, mid)
497 else Dep.Prop
(x, mid) in
498 Option.iter
env.decl_env
.droot
(fun root
-> Typing_deps.add_idep root
dep)
500 add_dep class_
.tc_name
;
501 (* The type of a member is stored separately in the heap. This means that
502 * any user of the member also has a dependency on the class where the member
505 let ce_opt = if is_method
then (SMap.get
mid class_
.tc_methods
)
506 else SMap.get
mid class_
.tc_props
in
507 Option.iter
ce_opt (fun ce
-> add_dep ce
.ce_origin
);
510 let suggest_member is_method class_
mid =
511 let mid = String.lowercase_ascii
mid in
512 let members = if is_method
then class_
.tc_methods
else class_
.tc_props
in
513 suggest_member members mid
515 let get_construct env class_
=
516 add_wclass env class_
.tc_name
;
518 let dep = Dep.Cstr
(x) in
519 Option.iter
env.decl_env
.Decl_env.droot
520 (fun root
-> Typing_deps.add_idep root
dep);
522 add_dep class_
.tc_name
;
523 Option.iter
(fst class_
.tc_construct
) (fun ce
-> add_dep ce
.ce_origin
);
528 List.fold_left
env.todo ~f
:(fun (env, remaining
) f
->
529 let env, remove
= f
env in
530 if remove
then env, remaining
else env, f
::remaining
)
532 { env with todo
= List.rev remaining
}
537 let set_return env x =
538 let genv = env.genv in
539 let genv = { genv with return
= x } in
540 { env with genv = genv }
545 let set_params env params
=
546 { env with genv = { env.genv with params
= params
} }
548 let set_param env x param
=
549 let params = get_params env in
550 let params = Local_id.Map.add x param
params in
551 set_params env params
553 let clear_params env =
554 set_params env Local_id.Map.empty
557 let ret = get_return env in
558 let params = get_params env in
559 let env, result = f
env in
560 let env = set_params env params in
561 let env = set_return env ret in
564 let is_static env = env.genv.static
565 let get_self env = env.genv.self
566 let get_self_id env = env.genv.self_id
567 let is_outside_class env = (env.genv.self_id
= "")
568 let get_parent env = env.genv.parent
569 let get_parent_id env = env.genv.parent_id
571 let get_fn_kind env = env.genv.fun_kind
573 let get_file env = env.genv.file
576 let dep = Dep.Fun
x in
577 Option.iter
env.decl_env
.droot
(fun root
-> Typing_deps.add_idep root
dep);
578 TLazyHeap.get_fun env.genv.tcopt
x
580 let set_fn_kind env fn_type
=
581 let genv = env.genv in
582 let genv = { genv with fun_kind
= fn_type
} in
583 { env with genv = genv }
585 (* Add a function on environments that gets run at some later stage to check
586 * constraints, by which time unresolved type variables may be resolved.
587 * Because the validity of the constraint might depend on tpenv
588 * at the point that the `add_todo` is called, we extend the environment at
589 * the point that the function gets run with `tpenv` captured at the point
590 * that `add_todo` gets called.
591 * Typical examples are `instanceof` tests that introduce bounds on fresh
592 * type parameters (e.g. named T#1) or on existing type parameters, which
593 * are removed after the end of the `instanceof` conditional block. e.g.
594 * function foo<T as arraykey>(T $x): void { }
596 * class D extends C<arraykey> { }
597 * function test<Tu>(C<Tu> $x, Tu $y): void {
598 * if ($x instanceof D) {
599 * // Here we know Tu <: arraykey but the constraint is checked later
604 let tpenv_now = env.lenv
.tpenv in
606 let old_tpenv = env.lenv
.tpenv in
607 let env, remove
= f (env_with_tpenv env tpenv_now) in
608 env_with_tpenv env old_tpenv, remove
in
609 { env with todo
= f'
:: env.todo
}
611 let add_anonymous env x =
612 let genv = env.genv in
613 let anon_id = Ident.tmp
() in
614 let genv = { genv with anons
= IMap.add anon_id x genv.anons
} in
615 { env with genv = genv }, anon_id
617 let get_anonymous env x =
618 IMap.get
x env.genv.anons
620 let set_self_id env x =
621 let genv = env.genv in
622 let genv = { genv with self_id
= x } in
623 { env with genv = genv }
626 let genv = env.genv in
627 let genv = { genv with self
= x } in
628 { env with genv = genv }
630 let set_parent_id env x =
631 let genv = env.genv in
632 let genv = { genv with parent_id
= x } in
633 { env with genv = genv }
635 let set_parent env x =
636 let genv = env.genv in
637 let genv = { genv with parent
= x } in
638 { env with genv = genv }
641 let genv = env.genv in
642 let genv = { genv with static
= true } in
643 { env with genv = genv }
645 let set_mode env mode
=
646 let decl_env = env.decl_env in
647 let decl_env = { decl_env with mode
} in
648 { env with decl_env }
650 let get_mode env = env.decl_env.mode
652 let is_strict env = get_mode env = FileInfo.Mstrict
653 let is_decl env = get_mode env = FileInfo.Mdecl
655 let get_options env = env.genv.tcopt
657 let log_anonymous env =
658 if TypecheckerOptions.disallow_ambiguous_lambda
(get_options env)
659 then IMap.iter
(fun _
(_
, _
, counter
, pos, _
) ->
660 Errors.ambiguous_lambda
pos !counter
) env.genv.anons
664 Classes.iter begin fun cid class_ ->
665 Printf.printf "Type of class %s:" cid;
667 SMap.iter begin fun m _ ->
668 Printf.printf "%s " m;
669 end class_.tc_methods;
673 (*****************************************************************************)
674 (* This is used when we want member variables to be treated like locals
675 * We want to handle the following:
679 * The trick consists in replacing $this->x with a "fake" local. So that
680 * all the logic that normally applies to locals is applied in cases like
681 * this. Hence the name: FakeMembers.
682 * All the fake members are thrown away at the first call.
683 * We keep the invalidated fake members for better error messages.
685 (*****************************************************************************)
687 let get_last_call env =
688 match (env.lenv
.fake_members
).last_call
with
689 | None
-> assert false
692 let rec lost_info fake_name
env ty =
693 let info r
= Reason.Rlost_info
(fake_name
, r
, get_last_call env) in
696 let env, v'
= get_var env v in
697 (match IMap.get
v'
env.tenv
with
701 let env, ty = lost_info fake_name
env ty in
702 let env = add env v ty in
705 | r
, Tunresolved tyl
->
706 let env, tyl
= List.map_env
env tyl
(lost_info fake_name
) in
707 env, (info r
, Tunresolved tyl
)
711 let forget_members env call_pos
=
712 let fake_members = env.lenv
.fake_members in
713 let old_invalid = fake_members.invalid
in
714 let new_invalid = fake_members.valid
in
715 let new_invalid = SSet.union
new_invalid old_invalid in
717 last_call
= Some call_pos
;
718 invalid
= new_invalid;
721 { env with lenv
= { env.lenv
with fake_members } }
723 module FakeMembers
= struct
725 let make_id obj_name member_name
=
729 | _
, Lvar
(_
, x) -> x
732 Local_id.to_string
obj_name^
"->"^member_name
734 let make_static_id cid member_name
=
735 let class_name = class_id_to_str cid
in
736 class_name^
"::"^member_name
738 let get env obj member_name
=
742 let id = make_id obj member_name
in
743 if SSet.mem
id env.lenv
.fake_members.valid
744 then Some
(Hashtbl.hash
id)
748 let is_invalid env obj member_name
=
752 SSet.mem
(make_id obj member_name
) env.lenv
.fake_members.invalid
755 let get_static env cid member_name
=
756 let name = make_static_id cid member_name
in
757 if SSet.mem
name env.lenv
.fake_members.valid
758 then Some
(Hashtbl.hash
name)
761 let is_static_invalid env cid member_name
=
762 SSet.mem
(make_static_id cid member_name
) env.lenv
.fake_members.invalid
764 let add_member env fake_id
=
765 let fake_members = env.lenv
.fake_members in
766 let valid = SSet.add fake_id
fake_members.valid in
767 let fake_members = { fake_members with valid = valid } in
768 { env with lenv
= { env.lenv
with fake_members } }
770 let make _
env obj_name member_name
=
771 let my_fake_local_id = make_id obj_name member_name
in
772 let env = add_member env my_fake_local_id in
773 env, Local_id.get my_fake_local_id
775 let make_static _
env class_name member_name
=
776 let my_fake_local_id = make_static_id class_name member_name
in
777 let env = add_member env my_fake_local_id in
778 env, Local_id.get my_fake_local_id
783 (*****************************************************************************)
785 (*****************************************************************************)
787 (* We want to "take a picture" of the current type
788 * that is, the current type shouldn't be affected by a
789 * future unification.
792 let rec unbind seen
env ty =
793 let env, ty = expand_type env ty in
794 if List.exists seen
(fun ty'
->
795 let _, ty'
= expand_type env ty'
in Typing_defs.ty_equal
ty ty'
)
798 let seen = ty :: seen in
800 | r
, Tunresolved tyl
->
801 let env, tyl
= List.map_env
env tyl
(unbind seen) in
802 env, (r
, Tunresolved tyl
)
805 let unbind = unbind []
807 (* We maintain 3 states for a local, all the types that the
808 * local ever had (cf integrate in typing.ml), the type
809 * that the local currently has, and an expression_id generated from
810 * the last assignment to this local.
812 let set_local env x new_type
=
813 let {fake_members; local_types
; local_type_history
; local_using_vars
;
814 tpenv; local_mutability
; local_reactive
} = env.lenv
in
815 let env, new_type
= unbind env new_type
in
816 let next_cont = LEnvC.get_cont
Cont.Next local_types
in
817 let all_types, expr_id
=
819 (Local_id.Map.get x next_cont, Local_id.Map.get x local_type_history
)
821 | None
, None
-> [], Ident.tmp
()
822 | Some
(_, y
), Some
x -> x, y
823 | _ -> Exit_status.(exit Local_type_env_stale
)
826 if List.exists
all_types (fun ty'
->
827 let _, ty'
= expand_type env ty'
in Typing_defs.ty_equal new_type
ty'
)
829 else new_type
:: all_types
831 let local = new_type
, expr_id
in
832 let local_types = LEnvC.add_to_cont
Cont.Next
x local local_types in
833 let local_type_history = Local_id.Map.add x all_types local_type_history in
835 lenv
= {fake_members; local_types; local_type_history; local_using_vars
;
836 tpenv; local_mutability
; local_reactive
; } }
840 let is_using_var env x =
841 Local_id.Set.mem
x env.lenv
.local_using_vars
843 let set_using_var env x =
845 env.lenv
with local_using_vars
= Local_id.Set.add x env.lenv
.local_using_vars
} }
847 let unset_local env local =
848 let {fake_members; local_types ; local_type_history;
849 local_using_vars
; tpenv;
850 local_mutability
; local_reactive
; } = env.lenv
in
851 let local_types = LEnvC.remove_from_cont
Cont.Next
local local_types in
852 let local_using_vars = Local_id.Set.remove
local local_using_vars in
853 let local_type_history = Local_id.Map.remove
local local_type_history in
854 let local_mutability = Local_id.Map.remove
local local_mutability in
856 lenv
= {fake_members; local_types; local_type_history; local_using_vars;
857 tpenv; local_mutability; local_reactive
} }
862 let is_mutable env local =
863 Local_id.Map.mem
local env.lenv
.local_mutability
865 let add_mutable_var env local mutability_type
=
868 (Local_id.Map.add local mutability_type
env.lenv
.local_mutability)
871 LEnvC.get_cont
Cont.Next
env.lenv
.local_types
873 let get_local env x =
874 let next_cont = get_locals env in
875 let lcl = Local_id.Map.get x next_cont in
877 | None
-> (Reason.Rnone
, Tany
)
880 let set_local_expr_id env x new_eid
=
881 let local_types = env.lenv
.local_types in
882 let next_cont = LEnvC.get_cont
Cont.Next
local_types in
883 match Local_id.Map.get x next_cont with
884 | Some
(type_
, eid
) when eid
<> new_eid
->
885 let local = type_
, new_eid
in
886 let local_types = LEnvC.add_to_cont
Cont.Next
x local local_types in
887 let env ={ env with lenv
= { env.lenv
with local_types } }
892 let get_local_expr_id env x =
893 let next_cont = LEnvC.get_cont
Cont.Next
env.lenv
.local_types in
894 let lcl = Local_id.Map.get x next_cont in
895 Option.map
lcl ~
f:(fun (_, x) -> x)
897 (*****************************************************************************)
898 (* This function is called when we are about to type-check a block that will
899 * later be fully_integrated (cf Typing.fully_integrate).
900 * Integration is about keeping track of all the types that a local had in
901 * its lifetime. It's necessary to correctly type-check catch blocks.
902 * After we type-check a block, we want to take all the types that the local
903 * had in this block, and add it to the list of possible types.
905 * However, we are not interested in the types that the local had *before*
906 * we started typing the block.
908 * A concrete example:
912 * $x = 'hello'; // the type of $x is string
918 * The type of $x is string or int, NOT string or int or ?_.
919 * We don't really care about the fact that $x could be null before the
922 * This is what freeze_local does, just before we start type-checking the
923 * while loop, we "freeze" the type of locals to the current environment.
925 (*****************************************************************************)
927 let freeze_local_env env =
928 let local_types = env.lenv
.local_types in
929 let next_cont = LEnvC.get_cont
Cont.Next
local_types in
930 let local_type_history = Local_id.Map.map
931 (fun (type_
, _) -> [type_
])
934 env_with_locals env local_types local_type_history
936 (*****************************************************************************)
937 (* Sets up/cleans up the environment when typing an anonymous function. *)
938 (*****************************************************************************)
940 let anon anon_lenv
env f =
941 (* Setting up the environment. *)
942 let old_lenv = env.lenv
in
943 let old_return = get_return env in
944 let old_params = get_params env in
945 let outer_fun_kind = get_fn_kind env in
946 let env = { env with lenv
= anon_lenv
} in
948 let env, tfun
, result = f env in
949 (* Cleaning up the environment. *)
950 let env = { env with lenv
= old_lenv } in
951 let env = set_params env old_params in
952 let env = set_return env old_return in
953 let env = set_fn_kind env outer_fun_kind in
957 let old_in_loop = env.in_loop in
958 let env = { env with in_loop = true } in
959 let env, result = f env in
960 { env with in_loop = old_in_loop }, result
962 (*****************************************************************************)
963 (* Merge and un-merge locals *)
964 (*****************************************************************************)
966 let merge_locals_and_history lenv
=
967 let merge_fn _key locals history
=
968 match locals
, history
with
970 | Some
(type_
, exp_id
), Some hist
-> Some
(hist
, type_
, exp_id
)
971 | _ -> Exit_status.(exit Local_type_env_stale
)
973 let next_cont = LEnvC.get_cont
Cont.Next lenv
.local_types in
975 merge_fn next_cont lenv
.local_type_history
977 (* TODO: Right now the only continuation we have is next
978 * so I'm putting everything in next *)
979 let separate_locals_and_history locals_and_history
=
980 let conts = Typing_continuations.Map.empty in
981 let next_cont = Local_id.Map.map
982 (fun (_, type_
, exp_id
) -> type_
, exp_id
) locals_and_history
985 Typing_continuations.Map.add
990 let history = Local_id.Map.map
991 (fun (hist
, _, _) -> hist
) locals_and_history
996 (* Return the subset of env which is saved in the Typed AST's EnvAnnotation. *)
997 let save local_tpenv
env =
999 Tast.tcopt
= get_tcopt env;
1000 Tast.tenv
= env.tenv
;
1001 Tast.subst
= env.subst
;
1002 Tast.tpenv = SMap.union local_tpenv
env.global_tpenv
;