Introduce __ReturnsVoidToRx
[hiphop-php.git] / hphp / hack / src / typing / typing_env.ml
blobd174a15ef70f708a0fa3878648c393444ae2bbf2
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 include Typing_env_types
11 open Hh_core
12 open Decl_env
13 open Typing_defs
14 open Nast
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
29 let fresh () =
30 Ident.tmp()
32 let fresh_type () =
33 Reason.none, Tvar (Ident.tmp())
35 let add_subst env x x' =
36 if x <> x'
37 then { env with subst = IMap.add x x' env.subst }
38 else env
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
44 (match x' with
45 | None -> env, x
46 | Some x' ->
47 let env, x' = get_var env x' in
48 let env = add_subst env x x' in
49 env, x'
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. *)
55 let rename env x x' =
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
59 env
61 let add env x ty =
62 let env, x = get_var env x in
63 match ty with
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
74 match ty with
75 | None -> env, (x_reason, Tany)
76 | Some ty -> env, ty
78 let get_type_unsafe env x =
79 let ty = IMap.get x env.tenv in
80 match ty with
81 | None ->
82 env, (Reason.none, Tany)
83 | Some ty -> env, ty
85 let expand_type env x =
86 match x with
87 | r, Tvar x -> get_type env r x
88 | x -> env, x
90 let make_ft p reactivity is_coroutine params ret_ty =
91 let arity = List.length params in
93 ft_pos = p;
94 ft_deprecated = None;
95 ft_abstract = false;
96 ft_is_coroutine = is_coroutine;
97 ft_arity = Fstandard (arity, arity);
98 ft_tparams = [];
99 ft_where_constraints = [];
100 ft_params = params;
101 ft_ret = ret_ty;
102 ft_ret_by_ref = false;
103 ft_reactive = reactivity;
104 ft_return_disposable = false;
105 ft_returns_mutable = false;
106 ft_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 =
145 match ty with
146 | (_, Tabstract (AKgeneric name', None)) -> name = name'
147 | _ -> false
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
152 then tpenv
153 else match SMap.get name tpenv with
154 | None ->
155 SMap.add name
156 {lower_bounds = empty_bounds; upper_bounds = singleton_bound ty} tpenv
157 | Some {lower_bounds; upper_bounds} ->
158 SMap.add name
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
164 then tpenv
165 else
166 match SMap.get name tpenv with
167 | None ->
168 SMap.add name
169 {lower_bounds = singleton_bound ty; upper_bounds = empty_bounds} tpenv
170 | Some {lower_bounds; upper_bounds} ->
171 SMap.add name
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 =
181 let tpenv =
182 begin match ty with
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
187 end in
188 { env with global_tpenv=(add_upper_bound_ tpenv name ty) }
190 let add_upper_bound env name ty =
191 let tpenv =
192 begin match ty with
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
197 end in
198 env_with_tpenv env (add_upper_bound_ tpenv name ty)
200 let add_lower_bound env name ty =
201 let tpenv =
202 begin match ty with
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
207 end in
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
216 env_with_tpenv env
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)
228 env.lenv.tpenv 0 in
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 =
236 let rec iterate i =
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
240 let env =
241 env_with_tpenv env
242 (SMap.add name {lower_bounds = empty_bounds;
243 upper_bounds = empty_bounds} env.lenv.tpenv) in
244 env, name
246 let is_fresh_generic_parameter name =
247 String.contains name '#' && not (AbstractKind.is_generic_dep_ty name)
249 let tparams_visitor env =
250 object(this)
251 inherit [SSet.t] Type_visitor.type_visitor
252 method! on_tabstract acc _ ak _ty_opt =
253 match ak with
254 | AKgeneric s -> SSet.add s acc
255 | _ -> acc
256 method! on_tvar acc r ix =
257 let _env, ty = get_type env r ix in
258 this#on_type acc ty
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 ->
265 let folder ty acc =
266 match ty with
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 =
276 { env with lenv = {
277 env.lenv with local_types = locals; local_type_history = history;
281 let empty_fake_members = {
282 last_call = None;
283 invalid = SSet.empty;
284 valid = SSet.empty;
287 let empty_local tpenv local_reactive = {
288 tpenv = tpenv;
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;
299 pos = Pos.none;
300 outer_pos = Pos.none;
301 outer_reason = Reason.URnone;
302 tenv = IMap.empty;
303 subst = IMap.empty;
304 lenv = empty_local SMap.empty Nonreactive;
305 todo = [];
306 in_loop = false;
307 inside_constructor = false;
308 decl_env = {
309 mode = FileInfo.Mstrict;
310 droot;
311 decl_tcopt = tcopt;
313 genv = {
314 tcopt = tcopt;
315 return = {
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;
324 self_id = "";
325 self = Reason.none, Tany;
326 static = false;
327 parent_id = "";
328 parent = Reason.none, Tany;
329 fun_kind = Ast.FSync;
330 fun_mutable = false;
331 anons = IMap.empty;
332 file = file;
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 =
353 env.genv.fun_mutable
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;
366 match result with
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
373 | None -> None)
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()
386 | _ -> ()
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 =
394 add_wclass env x;
395 TLazyHeap.get_typedef env.genv.tcopt x
397 let is_typedef x =
398 match Naming_heap.TypeIdHeap.get x with
399 | Some (_p, `Typedef) -> true
400 | _ -> false
402 let get_class env x =
403 add_wclass env x;
404 TLazyHeap.get_class env.genv.tcopt x
406 let get_enum_constraint env x =
407 match get_class env x with
408 | None -> None
409 | Some tc ->
410 match tc.tc_enum_type with
411 | None -> None
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 =
428 f { env with
429 todo = [];
430 lenv = empty_local env.lenv.tpenv env.lenv.local_reactive;
431 tenv = IMap.empty;
432 in_loop = false
435 let get_enum env x =
436 match TLazyHeap.get_class env.genv.tcopt x with
437 | Some tc when tc.tc_enum_type <> None -> Some tc
438 | _ -> None
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;
465 let add_dep x =
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
473 * originated.
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);
478 ce_opt
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
486 SMap.get mid members
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;
495 let add_dep x =
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
503 * originated.
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);
508 ce_opt
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;
517 let add_dep x =
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);
524 class_.tc_construct
526 let check_todo env =
527 let env, remaining =
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)
531 ~init:(env, []) in
532 { env with todo = List.rev remaining }
534 let get_return env =
535 env.genv.return
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 }
542 let get_params env =
543 env.genv.params
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
556 let with_env env f =
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
562 env, result
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
575 let get_fun env x =
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 { }
595 * class C<+T> { }
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
600 * foo($y);
603 let add_todo env f =
604 let tpenv_now = env.lenv.tpenv in
605 let f' env =
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 }
625 let set_self env x =
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 }
640 let set_static env =
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
663 let debug_env env =
664 Classes.iter begin fun cid class_ ->
665 Printf.printf "Type of class %s:" cid;
666 Printf.printf "{ ";
667 SMap.iter begin fun m _ ->
668 Printf.printf "%s " m;
669 end class_.tc_methods;
670 Printf.printf "}\n"
671 end env.genv.classes
673 (*****************************************************************************)
674 (* This is used when we want member variables to be treated like locals
675 * We want to handle the following:
676 * if($this->x) {
677 * ... $this->x ...
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
690 | Some pos -> pos
692 let rec lost_info fake_name env ty =
693 let info r = Reason.Rlost_info (fake_name, r, get_last_call env) in
694 match ty with
695 | _, Tvar v ->
696 let env, v' = get_var env v in
697 (match IMap.get v' env.tenv with
698 | None ->
699 env, ty
700 | Some ty ->
701 let env, ty = lost_info fake_name env ty in
702 let env = add env v ty in
703 env, ty
705 | r, Tunresolved tyl ->
706 let env, tyl = List.map_env env tyl (lost_info fake_name) in
707 env, (info r, Tunresolved tyl)
708 | r, ty ->
709 env, (info r, ty)
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
716 let fake_members = {
717 last_call = Some call_pos;
718 invalid = new_invalid;
719 valid = SSet.empty;
720 } in
721 { env with lenv = { env.lenv with fake_members } }
723 module FakeMembers = struct
725 let make_id obj_name member_name =
726 let obj_name =
727 match obj_name with
728 | _, This -> this
729 | _, Lvar (_, x) -> x
730 | _ -> assert false
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 =
739 match obj with
740 | _, This
741 | _, Lvar _ ->
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)
745 else None
746 | _ -> None
748 let is_invalid env obj member_name =
749 match obj with
750 | _, This
751 | _, Lvar _ ->
752 SSet.mem (make_id obj member_name) env.lenv.fake_members.invalid
753 | _ -> false
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)
759 else None
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 (*****************************************************************************)
784 (* Locals *)
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')
796 then env, ty
797 else
798 let seen = ty :: seen in
799 match ty with
800 | r, Tunresolved tyl ->
801 let env, tyl = List.map_env env tyl (unbind seen) in
802 env, (r, Tunresolved tyl)
803 | ty -> env, ty
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 =
818 match
819 (Local_id.Map.get x next_cont, Local_id.Map.get x local_type_history)
820 with
821 | None, None -> [], Ident.tmp()
822 | Some (_, y), Some x -> x, y
823 | _ -> Exit_status.(exit Local_type_env_stale)
825 let all_types =
826 if List.exists all_types (fun ty' ->
827 let _, ty' = expand_type env ty' in Typing_defs.ty_equal new_type ty')
828 then all_types
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
834 let env = { env with
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 =
844 { env with lenv = {
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
855 let env = { env with
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 =
866 env_with_mut
868 (Local_id.Map.add local mutability_type env.lenv.local_mutability)
870 let get_locals env =
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
876 match lcl with
877 | None -> (Reason.Rnone, Tany)
878 | Some (x, _) -> x
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 } }
890 | _ -> env
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:
910 * $x = null;
912 * $x = 'hello'; // the type of $x is string
914 * while (...) {
915 * $x = 0;
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
920 * block.
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_])
932 next_cont
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
947 (* Typing *)
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
954 env, tfun, result
956 let in_loop env f =
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
969 | None, None -> None
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
974 Local_id.Map.merge
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
984 let locals =
985 Typing_continuations.Map.add
986 Cont.Next
987 next_cont
988 conts
990 let history = Local_id.Map.map
991 (fun (hist, _, _) -> hist) locals_and_history
993 locals, 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;