Correctly instantiate generic attributes for XHP
[hiphop-php.git] / hphp / hack / src / typing / typing.ml
blobb2d82d2f087cfd05284fb2be1fee61511108e4e0
1 (**
2 * Copyright (c) 2015, Facebook, Inc.
3 * All rights reserved.
5 * This source code is licensed under the BSD-style license found in the
6 * LICENSE file in the "hack" directory of this source tree. An additional grant
7 * of patent rights can be found in the PATENTS file in the same directory.
9 *)
11 (* This module implements the typing.
13 * Given an Nast.program, it infers the type of all the local
14 * variables, and checks that all the types are correct (aka
15 * consistent) *)
16 open Autocomplete
17 open Core
18 open Nast
19 open Typing_defs
20 open Utils
22 module TUtils = Typing_utils
23 module Reason = Typing_reason
24 module Inst = Decl_instantiate
25 module Type = Typing_ops
26 module Env = Typing_env
27 module LEnv = Typing_lenv
28 module Dep = Typing_deps.Dep
29 module Async = Typing_async
30 module SubType = Typing_subtype
31 module Unify = Typing_unify
32 module TGen = Typing_generic
33 module SN = Naming_special_names
34 module TAccess = Typing_taccess
35 module TI = Typing_instantiability
36 module TVis = Typing_visibility
37 module TNBody = Typing_naming_body
38 module TS = Typing_structure
39 module Phase = Typing_phase
40 module Subst = Decl_subst
41 module ExprDepTy = Typing_dependent_type.ExprDepTy
43 (*****************************************************************************)
44 (* Debugging *)
45 (*****************************************************************************)
47 (* A guess as to the last position we were typechecking, for use in debugging,
48 * such as figuring out what a runaway hh_server thread is doing. Updated
49 * only best-effort -- it's an approximation to point debugging in the right
50 * direction, nothing more. *)
51 let debug_last_pos = ref Pos.none
52 let debug_print_last_pos _ = print_endline (Pos.string (Pos.to_absolute
53 !debug_last_pos))
55 (****************************************************************************)
56 (* Hooks *)
57 (****************************************************************************)
59 let expr_hook = ref None
61 let with_expr_hook hook f = with_context
62 ~enter: (fun () -> expr_hook := Some hook)
63 ~exit: (fun () -> expr_hook := None)
64 ~do_: f
66 (*****************************************************************************)
67 (* Helpers *)
68 (*****************************************************************************)
70 let suggest env p ty =
71 let ty = Typing_expand.fully_expand env ty in
72 (match Typing_print.suggest ty with
73 | "..." -> Errors.expecting_type_hint p
74 | ty -> Errors.expecting_type_hint_suggest p ty
77 let suggest_return env p ty =
78 let ty = Typing_expand.fully_expand env ty in
79 (match Typing_print.suggest ty with
80 | "..." -> Errors.expecting_return_type_hint p
81 | ty -> Errors.expecting_return_type_hint_suggest p ty
84 let any = Reason.Rnone, Tany
86 let compare_field_kinds x y =
87 match x, y with
88 | Nast.AFvalue (p1, _), Nast.AFkvalue ((p2, _), _)
89 | Nast.AFkvalue ((p2, _), _), Nast.AFvalue (p1, _) ->
90 Errors.field_kinds p1 p2;
91 false
92 | _ ->
93 true
95 let check_consistent_fields x l =
96 List.for_all l (compare_field_kinds x)
98 let unbound_name env (pos, name) =
99 (match Env.get_mode env with
100 | FileInfo.Mstrict ->
101 Errors.unbound_name_typing pos name
102 | FileInfo.Mdecl | FileInfo.Mpartial ->
105 env, (Reason.Rnone, Tany)
108 (*****************************************************************************)
109 (* Handling function/method arguments *)
110 (*****************************************************************************)
112 let rec wfold_left_default f (env, def1) l1 l2 =
113 match l1, def1, l2 with
114 | _, _, [] -> env
115 | [], None, _ -> env
116 | [], Some d1, x2 :: rl2 ->
117 let env = f env d1 x2 in
118 wfold_left_default f (env, def1) [] rl2
119 | x1 :: rl1, _, x2 :: rl2 ->
120 let env = f env x1 x2 in
121 wfold_left_default f (env, def1) rl1 rl2
123 let rec check_memoizable env param (pname, ty) =
124 let env, ty = Env.expand_type env ty in
125 let p = param.param_pos in
126 match ty with
127 | _, Tprim (Tarraykey | Tbool | Tint | Tfloat | Tstring | Tnum)
128 | _, Tmixed
129 | _, Tany ->
131 | _, Tprim (Tvoid | Tresource | Tnoreturn) ->
132 let ty_str = Typing_print.error (snd ty) in
133 let msgl = Reason.to_string ("This is "^ty_str) (fst ty) in
134 Errors.invalid_memoized_param p msgl
135 | _, Toption ty ->
136 check_memoizable env param (pname, ty)
137 | _, Tshape (_, fdm) ->
138 ShapeMap.iter begin fun name _ ->
139 match ShapeMap.get name fdm with
140 | Some ty -> check_memoizable env param (pname, ty)
141 | None ->
142 let ty_str = Typing_print.error (snd ty) in
143 let msgl = Reason.to_string ("This is "^ty_str) (fst ty) in
144 Errors.invalid_memoized_param p msgl;
145 end fdm
146 | _, Ttuple tyl ->
147 List.iter tyl begin fun ty ->
148 check_memoizable env param (pname, ty)
150 | _, Tabstract (AKenum _, _) ->
152 | _, Tabstract (AKnewtype (_, _), _) ->
153 let env, t', _ =
154 let ety_env = Phase.env_with_self env in
155 Typing_tdef.force_expand_typedef ~ety_env env ty in
156 check_memoizable env param (pname, t')
157 (* Just accept all generic types for now. Stricter checks to come later. *)
158 | _, Tabstract (AKgeneric _, _) ->
160 (* For parameter type 'this::TID' defined by 'type const TID as Bar' checks
161 * Bar recursively.
163 | _, Tabstract (AKdependent _, Some ty) ->
164 check_memoizable env param (pname, ty)
165 (* Allow unconstrined dependent type `abstract type const TID` just as we
166 * allow unconstrained generics. *)
167 | _, Tabstract (AKdependent _, None) ->
169 (* Handling Tunresolved case here for completeness, even though it
170 * shouldn't be possible to have an unresolved type when checking
171 * the method declaration. No corresponding test case for this.
173 | _, Tunresolved tyl ->
174 List.iter tyl begin fun ty ->
175 check_memoizable env param (pname, ty)
177 (* Allow untyped arrays. *)
178 | _, Tarraykind AKany
179 | _, Tarraykind AKempty ->
181 | _, Tarraykind (AKvec ty)
182 | _, Tarraykind (AKmap(_, ty)) ->
183 check_memoizable env param (pname, ty)
184 | _, Tarraykind (AKshape fdm) ->
185 ShapeMap.iter begin fun _ (_, tv) ->
186 check_memoizable env param (pname, tv)
187 end fdm
188 | _, Tarraykind (AKtuple fields) ->
189 IMap.iter begin fun _ tv ->
190 check_memoizable env param (pname, tv)
191 end fields
192 | _, Tclass (_, _) ->
193 let type_param = Env.fresh_type() in
194 let container_type =
195 Reason.none,
196 Tclass ((Pos.none, SN.Collections.cContainer), [type_param]) in
197 let env, is_container =
198 Errors.try_
199 (fun () ->
200 SubType.sub_type env container_type ty, true)
201 (fun _ -> env, false) in
202 if is_container then
203 check_memoizable env param (pname, type_param)
204 else
205 let r, _ = ty in
206 let memoizable_type =
207 r, Tclass ((Pos.none, SN.Classes.cIMemoizeParam), []) in
208 if SubType.is_sub_type env memoizable_type ty
209 then ()
210 else
211 let ty_str = Typing_print.error (snd ty) in
212 let msgl = Reason.to_string ("This is "^ty_str) (fst ty) in
213 Errors.invalid_memoized_param p msgl;
214 | _, Tfun _
215 | _, Tvar _
216 | _, Tanon (_, _)
217 | _, Tobject ->
218 let ty_str = Typing_print.error (snd ty) in
219 let msgl = Reason.to_string ("This is "^ty_str) (fst ty) in
220 Errors.invalid_memoized_param p msgl
222 (* This function is used to determine the type of an argument.
223 * When we want to type-check the body of a function, we need to
224 * introduce the type of the arguments of the function in the environment
225 * Let's take an example, we want to check the code of foo:
227 * function foo(int $x): int {
228 * // CALL TO make_param_type on (int $x)
229 * // Now we know that the type of $x is int
231 * return $x; // in the environment $x is an int, the code is correct
234 * When we localize, we want to resolve to "static" or "$this" depending on
235 * the context. Even though we are passing in CIstatic, resolve_with_class_id
236 * is smart enough to know what to do. Why do this? Consider the following
238 * abstract class C {
239 * abstract const type T;
241 * private this::T $val;
243 * final public function __construct(this::T $x) {
244 * $this->val = $x;
247 * public static function create(this::T $x): this {
248 * return new static($x);
252 * class D extends C { const type T = int; }
254 * In __construct() we want to be able to assign $x to $this->val. The type of
255 * $this->val will expand to '$this::T', so we need $x to also be '$this::T'.
256 * We can do this soundly because when we construct a new class such as,
257 * 'new D(0)' we can determine the late static bound type (D) and resolve
258 * 'this::T' to 'D::T' which is int.
260 * A similar line of reasoning is applied for the static method create.
262 let make_param_local_ty env param =
263 let ety_env =
264 { (Phase.env_with_self env) with from_class = Some CIstatic; } in
265 let env, ty =
266 match param.param_hint with
267 | None ->
268 (* if the type is missing, use an unbound type variable *)
269 let _r, ty = Env.fresh_type () in
270 let r = Reason.Rwitness param.param_pos in
271 env, (r, ty)
272 | Some x ->
273 let ty = Decl_hint.hint env.Env.decl_env x in
274 Phase.localize ~ety_env env ty
276 let ty = match ty with
277 | _, t when param.param_is_variadic ->
278 (* when checking the body of a function with a variadic
279 * argument, "f(C ...$args)", $args is an array<C> *)
280 let r = Reason.Rvar_param param.param_pos in
281 let arr_values = r, t in
282 r, Tarraykind (AKvec arr_values)
283 | x -> x
285 Typing_hooks.dispatch_infer_ty_hook ty param.param_pos env;
286 env, (Some param.param_name, ty)
288 let rec bind_param env (_, ty1) param =
289 let env, ty2 = opt expr env param.param_expr in
290 Option.iter param.param_expr Typing_sequencing.sequence_check_expr;
291 let ty2 = match ty2 with
292 | None -> Reason.none, Tany
293 | Some ty -> ty
295 Typing_suggest.save_param (param.param_name) env ty1 ty2;
296 let env = Type.sub_type param.param_pos Reason.URhint env ty1 ty2 in
297 Env.set_local env (Local_id.get param.param_name) ty1
299 (* In strict mode, we force you to give a type declaration on a parameter *)
300 (* But the type checker is nice: it makes a suggestion :-) *)
301 and check_param env param (_, ty) =
302 match (param.param_hint) with
303 | None -> suggest env param.param_pos ty
304 | Some _ -> ()
306 (*****************************************************************************)
307 (* Now we are actually checking stuff! *)
308 (*****************************************************************************)
309 and fun_def tcopt f =
310 (* reset the expression dependent display ids for each function body *)
311 Reason.expr_display_id_map := IMap.empty;
312 Typing_hooks.dispatch_enter_fun_def_hook f;
313 let nb = TNBody.func_body tcopt f in
314 let dep = Typing_deps.Dep.Fun (snd f.f_name) in
315 let env = Env.empty tcopt (Pos.filename (fst f.f_name)) (Some dep) in
316 NastCheck.fun_ env f nb;
317 (* Fresh type environment is actually unnecessary, but I prefer to
318 * have a guarantee that we are using a clean typing environment. *)
319 Env.fresh_tenv env (
320 fun env_up ->
321 let env = { env_up with Env.lenv = Env.empty_local } in
322 let env = Env.set_mode env f.f_mode in
323 let env = Phase.localize_generic_parameters_with_bounds env f.f_tparams
324 ~ety_env:(Phase.env_with_self env) in
325 let env, hret =
326 match f.f_ret with
327 | None -> env, (Reason.Rwitness (fst f.f_name), Tany)
328 | Some ret ->
329 let ty = TI.instantiable_hint env ret in
330 Phase.localize_with_self env ty
332 let f_params = match f.f_variadic with
333 | FVvariadicArg param -> param :: f.f_params
334 | _ -> f.f_params
336 TI.check_params_instantiable env f_params;
337 TI.check_tparams_instantiable env f.f_tparams;
338 let env, params = List.map_env env f_params make_param_local_ty in
339 let env = List.fold2_exn ~f:bind_param ~init:env params f_params in
340 let env = fun_ env hret (fst f.f_name) nb f.f_fun_kind in
341 let env = fold_fun_list env env.Env.todo in
342 if Env.is_strict env then begin
343 List.iter2_exn f_params params (check_param env);
344 match f.f_ret with
345 | None -> suggest_return env (fst f.f_name) hret
346 | Some _ -> ()
349 Typing_hooks.dispatch_exit_fun_def_hook f
351 (*****************************************************************************)
352 (* function used to type closures, functions and methods *)
353 (*****************************************************************************)
355 and fun_ ?(abstract=false) env hret pos named_body f_kind =
356 Env.with_return env begin fun env ->
357 debug_last_pos := pos;
358 let env = Env.set_return env hret in
359 let env = Env.set_fn_kind env f_kind in
360 let env = block env named_body.fnb_nast in
361 Typing_sequencing.sequence_check_block named_body.fnb_nast;
362 let ret = Env.get_return env in
363 let env =
364 if Nast_terminality.Terminal.block env named_body.fnb_nast ||
365 abstract ||
366 named_body.fnb_unsafe ||
367 !auto_complete
368 then env
369 else fun_implicit_return env pos ret named_body.fnb_nast f_kind in
370 debug_last_pos := Pos.none;
374 and fun_implicit_return env pos ret _b = function
375 | Ast.FGenerator | Ast.FAsyncGenerator -> env
376 | Ast.FSync ->
377 (* A function without a terminal block has an implicit return; the
378 * "void" type *)
379 let rty = Reason.Rno_return pos, Tprim Nast.Tvoid in
380 Typing_suggest.save_return env ret rty;
381 Type.sub_type pos Reason.URreturn env ret rty
382 | Ast.FAsync ->
383 (* An async function without a terminal block has an implicit return;
384 * the Awaitable<void> type *)
385 let r = Reason.Rno_return_async pos in
386 let rty = r, Tclass ((pos, SN.Classes.cAwaitable), [r, Tprim Nast.Tvoid]) in
387 Typing_suggest.save_return env ret rty;
388 Type.sub_type pos Reason.URreturn env ret rty
390 and block env stl =
391 List.fold_left stl ~f:stmt ~init:env
393 and stmt env = function
394 | Fallthrough
395 | Noop ->
397 | Expr e ->
398 let env, ty = expr env e in
399 (* NB: this check does belong here and not in expr, even though it only
400 * applies to expressions -- we actually want to perform the check on
401 * statements that are expressions, e.g., "foo();" we want to check, but
402 * "return foo();" we do not even though the expression "foo()" is a
403 * subexpression of the statement "return foo();". *)
404 (match snd e with
405 | Nast.Binop (Ast.Eq _, _, _) -> ()
406 | _ -> Async.enforce_not_awaitable env (fst e) ty);
408 | If (e, b1, b2) ->
409 let env, ty = expr env e in
410 Async.enforce_not_awaitable env (fst e) ty;
411 let parent_lenv = env.Env.lenv in
412 let env = condition env true e in
413 let env = block env b1 in
414 let lenv1 = env.Env.lenv in
415 let env = { env with Env.lenv = parent_lenv } in
416 let env = condition env false e in
417 let env = block env b2 in
418 let lenv2 = env.Env.lenv in
419 let terminal1 = Nast_terminality.Terminal.block env b1 in
420 let terminal2 = Nast_terminality.Terminal.block env b2 in
421 if terminal1 && terminal2
422 then
423 let env = LEnv.integrate env parent_lenv lenv1 in
424 let env = LEnv.integrate env env.Env.lenv lenv2 in
425 LEnv.integrate env env.Env.lenv parent_lenv
426 else if terminal1
427 then begin
428 let env = LEnv.integrate env parent_lenv lenv1 in
429 LEnv.integrate env env.Env.lenv lenv2
431 else if terminal2
432 then begin
433 let env = LEnv.integrate env parent_lenv lenv2 in
434 LEnv.integrate env env.Env.lenv lenv1
436 else LEnv.intersect env parent_lenv lenv1 lenv2
437 | Return (p, None) ->
438 let rty = match Env.get_fn_kind env with
439 | Ast.FSync -> (Reason.Rwitness p, Tprim Tvoid)
440 | Ast.FGenerator
441 | Ast.FAsyncGenerator -> any (* Return type checked against the "yield". *)
442 | Ast.FAsync -> (Reason.Rwitness p, Tclass ((p, SN.Classes.cAwaitable), [(Reason.Rwitness p, Tprim Tvoid)])) in
443 let expected_return = Env.get_return env in
444 Typing_suggest.save_return env expected_return rty;
445 let env = Type.sub_type p Reason.URreturn env expected_return rty in
447 | Return (p, Some e) ->
448 let pos = fst e in
449 let env, rty = expr env e in
450 let rty = match Env.get_fn_kind env with
451 | Ast.FSync -> rty
452 | Ast.FGenerator
453 | Ast.FAsyncGenerator -> any (* Is an error, but caught in NastCheck. *)
454 | Ast.FAsync -> (Reason.Rwitness p), Tclass ((p, SN.Classes.cAwaitable), [rty]) in
455 let expected_return = Env.get_return env in
456 (match snd (Env.expand_type env expected_return) with
457 | r, Tprim Tvoid ->
458 (* Yell about returning a value from a void function. This catches
459 * more issues than just unifying with void would do -- in particular
460 * just unifying allows you to return a Tany from a void function,
461 * which is clearly wrong. Note this check is best-effort; if the
462 * function returns a generic type which later ends up being Tvoid
463 * then there's not much we can do here. *)
464 Errors.return_in_void p (Reason.to_pos r);
466 | _, Tunresolved _ ->
467 (* we allow return types to grow for anonymous functions *)
468 let env, rty = TUtils.unresolved env rty in
469 let env, _ = Type.unify pos Reason.URreturn env expected_return rty in
471 | _, (Tany | Tmixed | Tarraykind _ | Toption _ | Tprim _
472 | Tvar _ | Tfun _ | Tabstract (_, _) | Tclass (_, _) | Ttuple _
473 | Tanon (_, _) | Tobject | Tshape _) ->
474 Typing_suggest.save_return env expected_return rty;
475 let env = Type.sub_type pos Reason.URreturn env expected_return rty in
478 | Do (b, e) as st ->
479 (* NOTE: leaks scope as currently implemented; this matches
480 the behavior in naming (cf. `do_stmt` in naming/naming.ml).
482 let parent_lenv = env.Env.lenv in
483 let env = Env.freeze_local_env env in
484 let env = block env b in
485 let env, ty = expr env e in
486 Async.enforce_not_awaitable env (fst e) ty;
487 let after_block = env.Env.lenv in
488 let alias_depth =
489 if env.Env.in_loop then 1 else Typing_alias.get_depth st in
490 let env = Env.in_loop env begin
491 iter_n_acc alias_depth begin fun env ->
492 let env = condition env true e in
493 let env = block env b in
496 end in
497 let env =
498 if Nast_visitor.HasContinue.block b
499 then LEnv.fully_integrate env parent_lenv
500 else
501 let env = LEnv.integrate env parent_lenv env.Env.lenv in
502 let env = { env with Env.lenv = after_block } in
505 condition env false e
506 | While (e, b) as st ->
507 let env, ty = expr env e in
508 Async.enforce_not_awaitable env (fst e) ty;
509 let parent_lenv = env.Env.lenv in
510 let env = Env.freeze_local_env env in
511 let alias_depth =
512 if env.Env.in_loop then 1 else Typing_alias.get_depth st in
513 let env = Env.in_loop env begin
514 iter_n_acc alias_depth begin fun env ->
515 let env = condition env true e in
516 let env = block env b in
519 end in
520 let env = LEnv.fully_integrate env parent_lenv in
521 condition env false e
522 | For (e1, e2, e3, b) as st ->
523 (* For loops leak their initalizer, but nothing that's defined in the
524 body
526 let (env, _) = expr env e1 in (* initializer *)
527 let (env, _) = expr env e2 in
528 let parent_lenv = env.Env.lenv in
529 let env = Env.freeze_local_env env in
530 let alias_depth =
531 if env.Env.in_loop then 1 else Typing_alias.get_depth st in
532 let env = Env.in_loop env begin
533 iter_n_acc alias_depth begin fun env ->
534 let env = condition env true e2 in (* iteration 0 *)
535 let env = block env b in
536 let (env, _) = expr env e3 in
539 end in
540 let env = LEnv.fully_integrate env parent_lenv in
541 condition env false e2
542 | Switch (e, cl) ->
543 Nast_terminality.SafeCase.check (fst e) env cl;
544 let env, ty = expr env e in
545 Async.enforce_not_awaitable env (fst e) ty;
546 let env = check_exhaustiveness env (fst e) ty cl in
547 let parent_lenv = env.Env.lenv in
548 let env, cl = case_list parent_lenv ty env cl in
549 LEnv.intersect_list env parent_lenv cl
550 | Foreach (e1, e2, b) as st ->
551 let env, ty1 = expr env e1 in
552 let env, ty1 = TUtils.fold_unresolved env ty1 in
553 let env, ety1 = Env.expand_type env ty1 in
554 let parent_lenv = env.Env.lenv in
555 let env = Env.freeze_local_env env in
556 let env, ty2 = as_expr env (fst e1) e2 in
557 let env = Type.sub_type (fst e1) Reason.URforeach env ty2 ety1 in
558 let alias_depth =
559 if env.Env.in_loop then 1 else Typing_alias.get_depth st in
560 let env = Env.in_loop env begin
561 iter_n_acc alias_depth begin fun env ->
562 let env = bind_as_expr env ty2 e2 in
563 let env = block env b in
566 end in
567 let env = LEnv.fully_integrate env parent_lenv in
569 | Try (tb, cl, fb) ->
570 let env = try_catch (tb, cl) env in
571 let env = block env fb in
573 | Static_var el ->
574 let env = List.fold_left el ~f:begin fun env e ->
575 match e with
576 | _, Binop (Ast.Eq _, (_, Lvar (p, x)), _) ->
577 Env.add_todo env (TGen.no_generic p x)
578 | _ -> env
579 end ~init:env in
580 let env, _ = List.map_env env el expr in
582 | Throw (_, e) ->
583 let p = fst e in
584 let env, ty = expr env e in
585 exception_ty p env ty
586 | Continue _
587 | Break _ -> env
589 and check_exhaustiveness env pos ty caselist =
590 (* Right now we only do exhaustiveness checking for enums. *)
591 let env, (_, ty) = Env.expand_type env ty in
592 match ty with
593 | Tunresolved tyl ->
594 List.fold_left tyl ~init:env ~f:begin fun env ty ->
595 check_exhaustiveness env pos ty caselist
597 | Tabstract (AKenum id, _) ->
598 let tc = unsafe_opt @@ Env.get_enum env id in
599 Typing_enum.check_enum_exhaustiveness pos tc caselist;
601 | Tany | Tmixed | Tarraykind _ | Tclass _ | Toption _ | Tprim _
602 | Tvar _ | Tfun _ | Tabstract (_, _) | Ttuple _ | Tanon (_, _)
603 | Tobject | Tshape _ -> env
605 and case_list parent_lenv ty env cl =
606 let env = { env with Env.lenv = parent_lenv } in
607 case_list_ parent_lenv ty env cl
609 and try_catch (tb, cl) env =
610 let parent_lenv = env.Env.lenv in
611 let env = Env.freeze_local_env env in
612 let env = block env tb in
613 let after_try = env.Env.lenv in
614 let env, term_lenv_l = List.map_env env cl
615 begin fun env (_, _, b as catch_block) ->
616 let env, lenv = catch parent_lenv after_try env catch_block in
617 let term = Nast_terminality.Terminal.block env b in
618 env, (term, lenv)
619 end in
620 let term_lenv_l =
621 (Nast_terminality.Terminal.block env tb, after_try) :: term_lenv_l in
622 LEnv.intersect_list env parent_lenv term_lenv_l
624 and case_list_ parent_lenv ty env = function
625 | [] -> env, []
626 | Default b :: _ ->
627 (* TODO this is wrong, should continue on to the other cases, but it
628 * doesn't matter in practice since our parser won't parse default
629 * anywhere but in the last position :) Should fix all of this as well
630 * as totality detection for switch. *)
631 let env = block env b in
632 env, [Nast_terminality.Terminal.case env (Default b), env.Env.lenv]
633 | Case (e, b) :: rl ->
634 (* TODO - we should consider handling the comparisons the same
635 * way as Binop Ast.EqEq, since case statements work using ==
636 * comparison rules *)
638 (* The way we handle terminal/nonterminal here is not quite right, you
639 * can still break the type system with things like P3131824. *)
640 let ty_num = (Reason.Rnone, Tprim Nast.Tnum) in
641 let ty_arraykey = (Reason.Rnone, Tprim Nast.Tarraykey) in
642 let both_are_sub_types env tprim ty1 ty2 =
643 (SubType.is_sub_type env tprim ty1) &&
644 (SubType.is_sub_type env tprim ty2) in
645 if Nast_terminality.Terminal.block env b then
646 let env, ty2 = expr env e in
647 let env, _ =
648 if (both_are_sub_types env ty_num ty ty2) ||
649 (both_are_sub_types env ty_arraykey ty ty2)
650 then env, ty
651 else Type.unify (fst e) Reason.URnone env ty ty2 in
652 let env = block env b in
653 let lenv = env.Env.lenv in
654 let env, rl = case_list parent_lenv ty env rl in
655 env, (Nast_terminality.Terminal.case env (Case (e, b)), lenv) :: rl
656 else
657 let env, ty2 = expr env e in
658 let env, _ =
659 if (both_are_sub_types env ty_num ty ty2) ||
660 (both_are_sub_types env ty_arraykey ty ty2)
661 then env, ty
662 else Type.unify (fst e) Reason.URnone env ty ty2 in
663 (* Since this block is not terminal we will end up falling through to the
664 * next block. This means the lenv will include what our current
665 * environment is, intersected (or integrated?) with the environment
666 * after executing the block. Example:
668 * $x = 0; // $x = int
669 * switch (0) {
670 * case 1:
671 * $x = ''; // $x = string
672 * // FALLTHROUGH
673 * case 2:
674 * $x; // $x = int & string
675 * ...
677 let lenv1 = env.Env.lenv in
678 let env = block env b in
679 (* PERF: If the case is empty or a Noop then we do not need to intersect
680 * the lenv since they will be the same.
682 * This saves the cost of intersecting the lenv for the common pattern of
683 * case 1:
684 * case 2:
685 * case 3:
686 * ...
688 let env = match b with
689 | [] | [Noop] -> env
690 | _ -> LEnv.intersect env parent_lenv lenv1 env.Env.lenv in
691 case_list_ parent_lenv ty env rl
693 and catch parent_lenv after_try env (ety, exn, b) =
694 let env = { env with Env.lenv = after_try } in
695 let env = LEnv.fully_integrate env parent_lenv in
696 let cid = CI ety in
697 let ety_p = (fst ety) in
698 TUtils.process_class_id cid;
699 let env, _ = instantiable_cid ety_p env cid in
700 let env, ety = static_class_id ety_p env cid in
701 let env = exception_ty ety_p env ety in
702 let env = Env.set_local env (snd exn) ety in
703 let env = block env b in
704 (* Only keep the local bindings if this catch is non-terminal *)
705 env, env.Env.lenv
707 and as_expr env pe = function
708 | As_v _ ->
709 let ty = Env.fresh_type() in
710 let tvector = Tclass ((pe, SN.Collections.cTraversable), [ty]) in
711 env, (Reason.Rforeach pe, tvector)
712 | As_kv _ ->
713 let ty1 = Env.fresh_type() in
714 let ty2 = Env.fresh_type() in
715 let tmap = Tclass((pe, SN.Collections.cKeyedTraversable), [ty1; ty2]) in
716 env, (Reason.Rforeach pe, tmap)
717 | Await_as_v _ ->
718 let ty = Env.fresh_type() in
719 let tvector = Tclass ((pe, SN.Classes.cAsyncIterator), [ty]) in
720 env, (Reason.Rasyncforeach pe, tvector)
721 | Await_as_kv _ ->
722 let ty1 = Env.fresh_type() in
723 let ty2 = Env.fresh_type() in
724 let tmap = Tclass ((pe, SN.Classes.cAsyncKeyedIterator), [ty1; ty2]) in
725 env, (Reason.Rasyncforeach pe, tmap)
727 and bind_as_expr env ty aexpr =
728 let env, ety = Env.expand_type env ty in
729 match ety with
730 | _, Tclass ((p, _), [ty2]) ->
731 (match aexpr with
732 | As_v ev
733 | Await_as_v (_, ev) -> fst (assign p env ev ty2)
734 | As_kv ((_, Lvar (_, k)), ev)
735 | Await_as_kv (_, (_, Lvar (_, k)), ev) ->
736 let env, _ = set_valid_rvalue p env k (Reason.Rnone, Tmixed) in
737 fst (assign p env ev ty2)
738 | _ -> (* TODO Probably impossible, should check that *)
741 | _, Tclass ((p, _), [ty1; ty2]) ->
742 (match aexpr with
743 | As_v ev
744 | Await_as_v (_, ev) -> fst (assign p env ev ty2)
745 | As_kv ((_, Lvar (_, k)), ev)
746 | Await_as_kv (_, (_, Lvar (_, k)), ev) ->
747 let env, _ = set_valid_rvalue p env k ty1 in
748 fst (assign p env ev ty2)
749 | _ -> (* TODO Probably impossible, should check that *)
752 | _, (Tany | Tmixed | Tarraykind _ | Toption _ | Tprim _
753 | Tvar _ | Tfun _ | Tabstract (_, _) | Tclass (_, _) | Ttuple _
754 | Tanon (_, _) | Tunresolved _ | Tobject | Tshape _
755 ) -> assert false
757 and expr env e =
758 raw_expr ~in_cond:false env e
760 and raw_expr ~in_cond ?valkind:(valkind=`other) env e =
761 debug_last_pos := fst e;
762 let env, ty = expr_ ~in_cond ~valkind env e in
763 let () = match !expr_hook with
764 | Some f -> f e (Typing_expand.fully_expand env ty)
765 | None -> () in
766 Typing_hooks.dispatch_infer_ty_hook ty (fst e) env;
767 env, ty
769 and lvalue env e =
770 let valkind = `lvalue in
771 expr_ ~in_cond:false ~valkind env e
773 (* $x ?? 0 is handled similarly to $x ?: 0, except that the latter will also
774 * look for sketchy null checks in the condition. *)
775 and eif env ~coalesce ~in_cond p c e1 e2 =
776 let env, tyc = raw_expr in_cond env c in
777 Async.enforce_not_awaitable env (fst c) tyc;
778 let _, parent_locals as lenv = env.Env.lenv in
779 let c = if coalesce then (p, Binop (Ast.Diff2, c, (p, Null))) else c in
780 let env = condition env true c in
781 let env, ty1 = match e1 with
782 | None ->
783 non_null env tyc
784 | Some e1 ->
785 expr env e1
787 let fake1, _locals1 = env.Env.lenv in
788 let env = { env with Env.lenv = lenv } in
789 let env = condition env false c in
790 let env, ty2 = expr env e2 in
791 let fake2, _locals2 = env.Env.lenv in
792 let fake_members = LEnv.intersect_fake fake1 fake2 in
793 (* we restore the locals to their parent state so as not to leak the
794 * effects of the `condition` calls above *)
795 let env = { env with Env.lenv = fake_members, parent_locals } in
796 (* This is a shortened form of what we do in Typing_lenv.intersect. The
797 * latter takes local environments as arguments, but our types here
798 * aren't assigned to local variables in an environment *)
799 let env, ty1 = TUtils.unresolved env ty1 in
800 let env, ty2 = TUtils.unresolved env ty2 in
801 Unify.unify env ty1 ty2
803 and expr_
804 ~in_cond
805 ~(valkind: [> `lvalue | `lvalue_subexpr | `other ])
806 env (p, e) =
807 match e with
808 | Any -> env, (Reason.Rwitness p, Tany)
809 | Array [] -> env, (Reason.Rwitness p, Tarraykind AKempty)
810 | Array l when Typing_arrays.is_shape_like_array env l ->
811 let env, fdm = List.fold_left_env env l ~init:ShapeMap.empty
812 ~f:begin fun env fdm x ->
813 let env, (key, value) = akshape_field env x in
814 env, Nast.ShapeMap.add key value fdm
815 end in
816 env, (Reason.Rwitness p, Tarraykind (AKshape fdm))
817 | Array (x :: rl as l) ->
818 let fields_consistent = check_consistent_fields x rl in
819 let is_vec = match x with
820 | Nast.AFvalue _ -> true
821 | Nast.AFkvalue _ -> false in
822 if fields_consistent && is_vec then
823 let env, fields = List.foldi l ~f:begin fun index (env, acc) e ->
824 let env, ty = aktuple_field env e in
825 env, IMap.add index ty acc
826 end ~init:(env, IMap.empty) in
827 env, (Reason.Rwitness p, Tarraykind (AKtuple fields))
828 else
829 let env, value = Env.fresh_unresolved_type env in
830 let env, values = List.rev_map_env env l array_field_value in
831 let has_unknown = List.exists values (fun (_, ty) -> ty = Tany) in
832 let env, values = List.rev_map_env env values TUtils.unresolved in
833 let unify_value = Type.unify p Reason.URarray_value in
834 let env, value =
835 if has_unknown (* If one of the values comes from PHP land,
836 * we have to be conservative and consider that
837 * we don't know what the type of the values are.
839 then env, (Reason.Rnone, Tany)
840 else List.fold_left_env env values ~init:value ~f:unify_value in
841 if is_vec then
842 env, (Reason.Rwitness p, Tarraykind (AKvec value))
843 else
844 let env, key = Env.fresh_unresolved_type env in
845 let env, keys = List.rev_map_env env l array_field_key in
846 let env, keys = List.rev_map_env env keys TUtils.unresolved in
847 let unify_key = Type.unify p Reason.URarray_key in
848 let env, key = List.fold_left_env env keys ~init:key ~f:unify_key in
849 env, (Reason.Rwitness p, Tarraykind (AKmap (key, value)))
850 | ValCollection (name, el) ->
851 let env, x = Env.fresh_unresolved_type env in
852 let env, tyl = List.map_env env el expr in
853 let env, tyl = List.map_env env tyl Typing_env.unbind in
854 let env, tyl = List.map_env env tyl TUtils.unresolved in
855 let env, v =
856 List.fold_left_env env tyl ~init:x ~f:(Type.unify p Reason.URvector) in
857 let tvector = Tclass ((p, name), [v]) in
858 let ty = Reason.Rwitness p, tvector in
859 env, ty
860 | KeyValCollection (kind, l) ->
861 let kl, vl = List.unzip l in
862 let env, kl = List.map_env env kl expr in
863 let env, kl = List.map_env env kl Typing_env.unbind in
864 let env, vl = List.map_env env vl expr in
865 let env, vl = List.map_env env vl Typing_env.unbind in
866 let env, k = Env.fresh_unresolved_type env in
867 let env, v = Env.fresh_unresolved_type env in
868 let env, kl = List.map_env env kl TUtils.unresolved in
869 let env, k =
870 List.fold_left_env env kl ~init:k ~f:(Type.unify p Reason.URkey) in
871 let env, vl = List.map_env env vl TUtils.unresolved in
872 let env, v =
873 List.fold_left_env env vl ~init:v ~f:(Type.unify p Reason.URvalue) in
874 let ty = Tclass ((p, kvc_kind_to_name kind), [k; v])
876 env, (Reason.Rwitness p, ty)
877 | Clone e -> expr env e
878 | This when Env.is_static env ->
879 Errors.this_in_static p;
880 env, (Reason.Rwitness p, Tany)
881 | This when valkind = `lvalue ->
882 Errors.this_lvalue p;
883 env, (Reason.Rwitness p, Tany)
884 | This ->
885 let r, _ = Env.get_self env in
886 if r = Reason.Rnone
887 then Errors.this_var_outside_class p;
888 let env, (_, ty) = Env.get_local env this in
889 let r = Reason.Rwitness p in
890 let ty = (r, ty) in
891 let ty = r, TUtils.this_of ty in
892 (* '$this' always refers to the late bound static type *)
893 env, ExprDepTy.make env CIstatic ty
894 | Assert (AE_assert e) ->
895 let env = condition env true e in
896 env, (Reason.Rwitness p, Tprim Tvoid)
897 | True
898 | False ->
899 env, (Reason.Rwitness p, Tprim Tbool)
900 | Int _ ->
901 env, (Reason.Rwitness p, Tprim Tint)
902 | Float _ ->
903 env, (Reason.Rwitness p, Tprim Tfloat)
904 | Null ->
905 let ty = Env.fresh_type() in
906 env, (Reason.Rwitness p, Toption ty)
907 | String _ ->
908 env, (Reason.Rwitness p, Tprim Tstring)
909 | String2 idl ->
910 let env = string2 env idl in
911 env, (Reason.Rwitness p, Tprim Tstring)
912 | Fun_id x ->
913 Typing_hooks.dispatch_id_hook x env;
914 let env, fty = fun_type_of_id env x in
915 begin match fty with
916 | _, Tfun fty -> check_deprecated (fst x) fty;
917 | _ -> ()
918 end;
919 env, fty
920 | Id ((cst_pos, cst_name) as id) ->
921 Typing_hooks.dispatch_id_hook id env;
922 (match Env.get_gconst env cst_name with
923 | None when Env.is_strict env ->
924 Errors.unbound_global cst_pos;
925 env, (Reason.Rwitness cst_pos, Tany)
926 | None ->
927 env, (Reason.Rnone, Tany)
928 | Some ty ->
929 Phase.localize_with_self env ty
931 | Method_id (instance, meth) ->
932 (* Method_id is used when creating a "method pointer" using the magic
933 * inst_meth function.
935 * Typing this is pretty simple, we just need to check that instance->meth
936 * is public+not static and then return its type.
938 let env, ty1 = expr env instance in
939 let env, result, vis =
940 obj_get_with_visibility ~is_method:true ~nullsafe:None env ty1
941 (CIexpr instance) meth (fun x -> x) in
942 let has_lost_info = Env.FakeMembers.is_invalid env instance (snd meth) in
943 if has_lost_info
944 then
945 let name = "the method "^snd meth in
946 let env, result = Env.lost_info name env result in
947 env, result
948 else
949 begin
950 (match result with
951 | _, Tfun fty -> check_deprecated p fty
952 | _ -> ());
953 (match vis with
954 | Some (method_pos, Vprivate _) ->
955 Errors.private_inst_meth method_pos p
956 | Some (method_pos, Vprotected _) ->
957 Errors.protected_inst_meth method_pos p
958 | _ -> ()
960 env, result
962 | Method_caller ((pos, class_name) as pos_cname, meth_name) ->
963 (* meth_caller('X', 'foo') desugars to:
964 * $x ==> $x->foo()
966 let class_ = Env.get_class env class_name in
967 (match class_ with
968 | None -> unbound_name env pos_cname
969 | Some class_ ->
970 (* Create a class type for the given object instantiated with unresolved
971 * types for its type parameters.
973 let env, tvarl =
974 List.map_env env class_.tc_tparams TUtils.unresolved_tparam in
975 let params = List.map class_.tc_tparams begin fun (_, (p, n), cstr) ->
976 Reason.Rwitness p, Tgeneric (n, cstr)
977 end in
978 let obj_type = Reason.Rwitness p, Tapply (pos_cname, params) in
979 let ety_env = {
980 (Phase.env_with_self env) with
981 substs = Subst.make class_.tc_tparams tvarl;
982 } in
983 (* AKENN: what about the bounds on the generic parameters? *)
984 let env, local_obj_ty = Phase.localize ~ety_env env obj_type in
985 let env, fty =
986 obj_get ~is_method:true ~nullsafe:None env local_obj_ty
987 (CI (pos, class_name)) meth_name (fun x -> x) in
988 (match fty with
989 | reason, Tfun fty ->
990 check_deprecated p fty;
991 (* We are creating a fake closure:
992 * function<T as Class>(T $x): return_type_of(Class:meth_name)
994 let tparam = Ast.Invariant, pos_cname,
995 [(Ast.Constraint_as, obj_type)] in
996 let env, tvar = TUtils.unresolved_tparam env tparam in
997 let param = Reason.Rwitness pos,
998 Tgeneric (class_name, [(Ast.Constraint_as, obj_type)]) in
999 let ety_env = {
1000 ety_env with
1001 substs = Subst.make (tparam :: class_.tc_tparams) (tvar :: tvarl)
1002 } in
1003 (* AKENN: do we need to generate bounds here? *)
1004 let env, param = Phase.localize ~ety_env env param in
1005 let fty = { fty with
1006 ft_params = (None, param) :: fty.ft_params } in
1007 let fun_arity = match fty.ft_arity with
1008 | Fstandard (min, max) -> Fstandard (min + 1, max + 1)
1009 | Fvariadic (min, x) -> Fvariadic (min + 1, x)
1010 | Fellipsis min -> Fellipsis (min + 1) in
1011 let caller = {
1012 ft_pos = pos;
1013 ft_deprecated = None;
1014 ft_abstract = false;
1015 ft_arity = fun_arity;
1016 ft_tparams = fty.ft_tparams;
1017 ft_params = fty.ft_params;
1018 ft_ret = fty.ft_ret;
1019 } in
1020 env, (reason, Tfun caller)
1021 | _ ->
1022 (* This can happen if the method lives in PHP *)
1023 env, (Reason.Rwitness pos, Tany)
1026 | Smethod_id (c, meth) ->
1027 (* Smethod_id is used when creating a "method pointer" using the magic
1028 * class_meth function.
1030 * Typing this is pretty simple, we just need to check that c::meth is
1031 * public+static and then return its type.
1033 let class_ = Env.get_class env (snd c) in
1034 (match class_ with
1035 | None ->
1036 (* The class given as a static string was not found. *)
1037 unbound_name env c
1038 | Some class_ ->
1039 let smethod = Env.get_static_member true env class_ (snd meth) in
1040 (match smethod with
1041 | None -> (* The static method wasn't found. *)
1042 smember_not_found p ~is_const:false ~is_method:true class_ (snd meth);
1043 env, (Reason.Rnone, Tany)
1044 | Some smethod ->
1045 let cid = CI c in
1046 let env, cid_ty = static_class_id (fst c) env cid in
1047 let ety_env = {
1048 type_expansions = [];
1049 substs = SMap.empty;
1050 this_ty = cid_ty;
1051 from_class = Some cid;
1052 } in
1053 let env, smethod_type = Phase.localize ~ety_env env smethod.ce_type in
1054 (match smethod_type with
1055 | _, Tfun fty -> check_deprecated p fty
1056 | _ -> ());
1057 (match smethod_type, smethod.ce_visibility with
1058 | (r, (Tfun _ as ty)), Vpublic ->
1059 env, (r, ty)
1060 | (r, Tfun _), Vprivate _ ->
1061 Errors.private_class_meth (Reason.to_pos r) p;
1062 env, (r, Tany)
1063 | (r, Tfun _), Vprotected _ ->
1064 Errors.protected_class_meth (Reason.to_pos r) p;
1065 env, (r, Tany)
1066 | (r, _), _ ->
1067 Errors.internal_error p "We have a method which isn't callable";
1068 env, (r, Tany)
1072 | Lplaceholder p ->
1073 let r = Reason.Rplaceholder p in
1074 let ty = r, Tprim Tvoid in
1075 env, ty
1076 | Dollardollar (_, x) ->
1077 Env.get_local env x
1078 | Lvar ((_, x) as id) ->
1079 Typing_hooks.dispatch_lvar_hook id env;
1080 let env, x = Env.get_local env x in
1081 env, x
1082 | List el ->
1083 let env, tyl = List.map_env env el expr in
1084 let env, tyl = List.map_env env tyl Typing_env.unbind in
1085 let ty = Reason.Rwitness p, Ttuple tyl in
1086 env, ty
1087 | Pair (e1, e2) ->
1088 let env, ty1 = expr env e1 in
1089 let env, ty1 = Typing_env.unbind env ty1 in
1090 let env, ty2 = expr env e2 in
1091 let env, ty2 = Typing_env.unbind env ty2 in
1092 let ty = Reason.Rwitness p, Tclass ((p, SN.Collections.cPair), [ty1; ty2]) in
1093 env, ty
1094 | Expr_list el ->
1095 let env, tyl = List.map_env env el expr in
1096 let ty = Reason.Rwitness p, Ttuple tyl in
1097 env, ty
1098 | Array_get (e, None) ->
1099 let env, ty1 = update_array_type p env e None valkind in
1100 let is_lvalue = (valkind == `lvalue) in
1101 array_append is_lvalue p env ty1
1102 | Array_get (e1, Some e2) ->
1103 let env, ty1 = update_array_type p env e1 (Some e2) valkind in
1104 let env, ty1 = TUtils.fold_unresolved env ty1 in
1105 let env, ety1 = Env.expand_type env ty1 in
1106 let env, ty2 = expr env e2 in
1107 let is_lvalue = (valkind == `lvalue) in
1108 array_get is_lvalue p env ty1 ety1 e2 ty2
1109 | Call (Cnormal, (_, Id (_, hh_show)), [x], [])
1110 when hh_show = SN.PseudoFunctions.hh_show ->
1111 let env, ty = expr env x in
1112 Env.debug env ty;
1113 env, Env.fresh_type()
1114 | Call (call_type, e, el, uel) ->
1115 let env, result = dispatch_call p env call_type e el uel in
1116 let env = Env.forget_members env p in
1117 env, result
1118 | Binop (Ast.Eq (Some op), e1, e2) ->
1119 let e2 = p, Binop (op, e1, e2) in
1120 let env, ty = raw_expr in_cond env (p, Binop (Ast.Eq None, e1, e2)) in
1121 env, ty
1122 | Binop (Ast.Eq None, e1, e2) ->
1123 let env, ty2 = raw_expr in_cond env e2 in
1124 let env, ty = assign p env e1 ty2 in
1125 Typing_hooks.dispatch_assign_hook p ty2 env;
1126 (* If we are assigning a local variable to another local variable then
1127 * the expression ID associated with e2 is transferred to e1
1129 (match e1, e2 with
1130 | (_, Lvar (_, x1)), (_, Lvar (_, x2)) ->
1131 let eid2 = Env.get_local_expr_id env x2 in
1132 let env =
1133 Option.value_map
1134 eid2 ~default:env
1135 ~f:(Env.set_local_expr_id env x1) in
1136 env, ty
1137 | _ -> env, ty
1139 | Binop ((Ast.AMpamp | Ast.BArbar as bop), e1, e2) ->
1140 let c = bop = Ast.AMpamp in
1141 let lenv = env.Env.lenv in
1142 let env, ty1 = expr env e1 in
1143 let env = condition env c e1 in
1144 let env, ty2 = raw_expr in_cond env e2 in
1145 let env = { env with Env.lenv = lenv } in
1146 Typing_hooks.dispatch_binop_hook p bop ty1 ty2;
1147 env, (Reason.Rlogic_ret p, Tprim Tbool)
1148 | Binop (bop, e, (_, Null))
1149 | Binop (bop, (_, Null), e)
1150 when Env.is_strict env && (bop = Ast.EQeqeq || bop = Ast.Diff2) ->
1151 let _, ty = raw_expr in_cond env e in
1152 if not in_cond
1153 then Typing_equality_check.assert_nullable p bop env ty;
1154 env, (Reason.Rcomp p, Tprim Tbool)
1155 | Binop (bop, e1, e2) ->
1156 let env, ty1 = raw_expr in_cond env e1 in
1157 let env, ty2 = raw_expr in_cond env e2 in
1158 let env, ty = binop in_cond p env bop (fst e1) ty1 (fst e2) ty2 in
1159 Typing_hooks.dispatch_binop_hook p bop ty1 ty2;
1160 env, ty
1161 | Pipe ((_, id), e1, e2) ->
1162 let env, ty = expr env e1 in
1163 (** id is the ID of the $$ that is implicitly declared by the pipe.
1164 * Set the local type for the $$ in the RHS. *)
1165 let env = Env.set_local env id ty in
1166 let env, ty2 = expr env e2 in
1168 * Return ty2 since the type of the pipe expression is the type of the
1169 * RHS.
1171 * Note: env does have the type of this Pipe's $$, but it doesn't
1172 * override the outer one since they have different ID's.
1174 * For example:
1175 * a() |> ( inner1($$) |> inner2($$) ) + $$
1177 * The rightmost $$ refers to the result of a()
1179 env, ty2
1180 | Unop (uop, e) ->
1181 let env, ty = raw_expr in_cond env e in
1182 unop p env uop ty
1183 | Eif (c, e1, e2) -> eif env ~coalesce:false ~in_cond p c e1 e2
1184 | NullCoalesce (e1, e2) -> eif env ~coalesce:true ~in_cond p e1 None e2
1185 | Typename sid ->
1186 begin match Env.get_typedef env (snd sid) with
1187 | Some {td_tparams = tparaml; _} ->
1188 let params = List.map ~f:begin fun (_, (p, x), cstr) ->
1189 Reason.Rwitness p, Tgeneric (x, cstr)
1190 end tparaml in
1191 let tdef = Reason.Rwitness (fst sid), Tapply (sid, params) in
1192 let typename =
1193 Reason.Rwitness p, Tapply((p, SN.Classes.cTypename), [tdef]) in
1194 let env, tparams = List.map_env env tparaml begin fun env _ ->
1195 Env.fresh_unresolved_type env
1196 end in
1197 let ety_env = { (Phase.env_with_self env) with
1198 substs = Subst.make tparaml tparams } in
1199 (* AKENN: do we generate bounds here from the Tgeneric above? *)
1200 Phase.localize ~ety_env env typename
1201 | None ->
1202 (* Should never hit this case since we only construct this AST node
1203 * if in the expression Foo::class, Foo is a type def.
1205 env, (Reason.Rwitness p, Tany)
1207 | Class_const (cid, mid) -> class_const env p (cid, mid)
1208 | Class_get (x, (_, y))
1209 when Env.FakeMembers.get_static env x y <> None ->
1210 let env, local = Env.FakeMembers.make_static p env x y in
1211 let local = p, Lvar (p, local) in
1212 expr env local
1213 | Class_get (cid, mid) ->
1214 TUtils.process_static_find_ref cid mid;
1215 let env, cty = static_class_id p env cid in
1216 let env, cty = Env.expand_type env cty in
1217 let env, ty = class_get ~is_method:false ~is_const:false env cty mid cid in
1218 if Env.FakeMembers.is_static_invalid env cid (snd mid)
1219 then
1220 let fake_name = Env.FakeMembers.make_static_id cid (snd mid) in
1221 let env, ty = Env.lost_info fake_name env ty in
1222 env, ty
1223 else env, ty
1224 | Obj_get (e, (_, Id (_, y)), _)
1225 when Env.FakeMembers.get env e y <> None ->
1226 let env, local = Env.FakeMembers.make p env e y in
1227 let local = p, Lvar (p, local) in
1228 expr env local
1229 | Obj_get (e1, (_, Id m), nullflavor) ->
1230 let nullsafe =
1231 (match nullflavor with
1232 | OG_nullthrows -> None
1233 | OG_nullsafe -> Some p
1234 ) in
1235 let env, ty1 = expr env e1 in
1236 let env, result =
1237 obj_get ~is_method:false ~nullsafe env ty1 (CIexpr e1) m (fun x -> x) in
1238 let has_lost_info = Env.FakeMembers.is_invalid env e1 (snd m) in
1239 if has_lost_info
1240 then
1241 let name = "the member "^snd m in
1242 let env, result = Env.lost_info name env result in
1243 env, result
1244 else env, result
1245 | Obj_get (e1, _, _) ->
1246 let env, _ = expr env e1 in
1247 env, (Reason.Rwitness p, Tany)
1248 | Yield_break ->
1249 env, (Reason.Rwitness p, Tany)
1250 | Yield af ->
1251 let env, key = yield_field_key env af in
1252 let env, value = yield_field_value env af in
1253 let send = Env.fresh_type () in
1254 let rty = match Env.get_fn_kind env with
1255 | Ast.FGenerator ->
1256 Reason.Ryield_gen p,
1257 Tclass ((p, SN.Classes.cGenerator), [key; value; send])
1258 | Ast.FAsyncGenerator ->
1259 Reason.Ryield_asyncgen p,
1260 Tclass ((p, SN.Classes.cAsyncGenerator), [key; value; send])
1261 | Ast.FSync | Ast.FAsync ->
1262 failwith "Parsing should never allow this" in
1263 let env =
1264 Type.sub_type p (Reason.URyield) env (Env.get_return env) rty in
1265 let env = Env.forget_members env p in
1266 env, (Reason.Ryield_send p, Toption send)
1267 | Await e ->
1268 let env, rty = expr env e in
1269 Async.overload_extract_from_awaitable env p rty
1270 | Special_func func -> special_func env p func
1271 | New (c, el, uel) ->
1272 Typing_hooks.dispatch_new_id_hook c env p;
1273 TUtils.process_static_find_ref c (p, SN.Members.__construct);
1274 let check_not_abstract = true in
1275 let env, ty = new_object ~check_not_abstract p env c el uel in
1276 let env = Env.forget_members env p in
1277 env, ExprDepTy.make env c ty
1278 | Cast ((_, Harray (None, None)), _) when Env.is_strict env ->
1279 Errors.array_cast p;
1280 env, (Reason.Rwitness p, Tany)
1281 | Cast (ty, e) ->
1282 let env, _ = expr env e in
1283 Phase.hint_locl env ty
1284 | InstanceOf (e, cid) ->
1285 let env, _ = expr env e in
1286 TUtils.process_class_id cid;
1287 let env, _class = instantiable_cid p env cid in
1288 env, (Reason.Rwitness p, Tprim Tbool)
1289 | Efun (f, _idl) ->
1290 let ft = Decl.fun_decl_in_env env.Env.decl_env f in
1291 (* When creating a closure, the 'this' type will mean the late bound type
1292 * of the current enclosing class
1294 let ety_env =
1295 { (Phase.env_with_self env) with from_class = Some CIstatic } in
1296 let env, ft = Phase.localize_ft ~ety_env env ft in
1297 (* check for recursive function calls *)
1298 let anon = anon_make env p f in
1299 let env, anon_id = Env.add_anonymous env anon in
1300 let env = Errors.try_with_error
1301 (fun () ->
1302 ignore (anon env ft.ft_params); env)
1303 (fun () ->
1304 (* If the anonymous function declaration has errors itself, silence
1305 them in any subsequent usages. *)
1306 let anon env fun_params =
1307 Errors.ignore_ (fun () -> (anon env fun_params)) in
1308 Env.set_anonymous env anon_id anon) in
1309 env, (Reason.Rwitness p, Tanon (ft.ft_arity, anon_id))
1310 | Xml (sid, attrl, el) ->
1311 let cid = CI sid in
1312 let env, obj = expr env (fst sid, New (cid, [], [])) in
1313 let env, attr_ptyl = List.map_env env attrl begin fun env attr ->
1314 (* Typecheck the expressions - this just checks that the expressions are
1315 * valid, not that they match the declared type for the attribute *)
1316 let namepstr, valexpr = attr in
1317 let valp, _ = valexpr in
1318 let env, valty = expr env valexpr in
1319 env, (namepstr, (valp, valty))
1320 end in
1321 let env, _body = List.map_env env el expr in
1322 let env, class_ = class_id_for_new p env cid in
1323 (match class_ with
1324 | None -> env, (Reason.Runknown_class p, Tobject)
1325 | Some (_, class_, _) ->
1326 if TypecheckerOptions.unsafe_xhp (Env.get_options env) then
1327 env, obj
1328 else begin
1329 let env = List.fold_left attr_ptyl ~f:begin fun env attr ->
1330 let namepstr, valpty = attr in
1331 let valp, valty = valpty in
1332 (* We pretend that XHP attributes are stored as member variables,
1333 * prefixed with a colon.
1335 * This converts the member name to an attribute name. *)
1336 let name = ":" ^ (snd namepstr) in
1337 let env, declty =
1338 obj_get ~is_method:false ~nullsafe:None env obj cid
1339 (fst namepstr, name) (fun x -> x) in
1340 let ureason = Reason.URxhp (class_.tc_name, snd namepstr) in
1341 Type.sub_type valp ureason env declty valty
1342 end ~init:env in
1343 env, obj
1346 | Shape fdm ->
1347 let env, fdm = ShapeMap.map_env expr env fdm in
1348 (* allow_inter adds a type-variable *)
1349 let env, fdm = ShapeMap.map_env TUtils.unresolved env fdm in
1350 let env = check_shape_keys_validity env p (ShapeMap.keys fdm) in
1351 (* Fields are fully known, because this shape is constructed
1352 * using shape keyword and we know exactly what fields are set. *)
1353 env, (Reason.Rwitness p, Tshape (FieldsFullyKnown, fdm))
1355 and class_const ?(incl_tc=false) env p (cid, mid) =
1356 TUtils.process_static_find_ref cid mid;
1357 let env, cty = static_class_id p env cid in
1358 let env, cty = Env.expand_type env cty in
1359 let env, const_ty =
1360 class_get ~is_method:false ~is_const:true ~incl_tc env cty mid cid in
1361 match const_ty with
1362 | r, Tabstract (AKgeneric n, _) ->
1363 let () = match cid with
1364 | CIstatic | CIexpr _ -> ();
1365 | _ -> Errors.abstract_const_usage p (Reason.to_pos r) n; ()
1366 in env, const_ty
1367 | _ ->
1368 env, const_ty
1370 (*****************************************************************************)
1371 (* Anonymous functions. *)
1372 (*****************************************************************************)
1374 and anon_bind_param params env (param_name, ty as pname_ty) =
1375 match !params with
1376 | [] ->
1377 (* This code cannot be executed normally, because the arity is wrong
1378 * and it will error later. Bind as many parameters as we can and carry
1379 * on. *)
1381 | param :: paraml ->
1382 params := paraml;
1383 match param.param_hint with
1384 | Some h ->
1385 let env, h = Phase.hint_locl env h in
1386 let pos = Reason.to_pos (fst ty) in
1387 let env = Type.sub_type pos Reason.URparam env h ty in
1388 (* Closures are allowed to have explicit type-hints. When
1389 * that is the case we should check that the argument passed
1390 * is compatible with the type-hint.
1391 * The body of the function should be type-checked with the
1392 * hint and not the type of the argument passed.
1393 * Otherwise it leads to strange results where
1394 * foo(?string $x = null) is called with a string and fails to
1395 * type-check. If $x is a string instead of ?string, null is not
1396 * subtype of string ...
1398 bind_param env (param_name, h) param
1399 | None -> bind_param env pname_ty param
1401 and anon_bind_opt_param env param =
1402 match param.param_expr with
1403 | None ->
1404 let ty = Reason.Rnone, Tany in
1405 bind_param env (None, ty) param
1406 | Some default ->
1407 let env, ty = expr env default in
1408 Typing_sequencing.sequence_check_expr default;
1409 bind_param env (None, ty) param
1411 and anon_check_param env param =
1412 match param.param_hint with
1413 | None -> env
1414 | Some hty ->
1415 let env, hty = Phase.hint_locl env hty in
1416 let env, paramty = Env.get_local env (Local_id.get param.param_name) in
1417 let hint_pos = Reason.to_pos (fst hty) in
1418 let env = Type.sub_type hint_pos Reason.URhint env hty paramty in
1421 and anon_make tenv p f =
1422 let anon_lenv = tenv.Env.lenv in
1423 let is_typing_self = ref false in
1424 let nb = Nast.assert_named_body f.f_body in
1425 fun env tyl ->
1426 if !is_typing_self
1427 then begin
1428 Errors.anonymous_recursive p;
1429 env, (Reason.Rwitness p, Tany)
1431 else begin
1432 is_typing_self := true;
1433 Env.anon anon_lenv env begin fun env ->
1434 let params = ref f.f_params in
1435 let env = List.fold_left ~f:(anon_bind_param params) ~init:env tyl in
1436 let env = List.fold_left ~f:anon_bind_opt_param ~init:env !params in
1437 let env = List.fold_left ~f:anon_check_param ~init:env f.f_params in
1438 let env, hret =
1439 match f.f_ret with
1440 | None -> Env.fresh_unresolved_type env
1441 | Some x ->
1442 let ret = TI.instantiable_hint env x in
1443 (* If a 'this' type appears it needs to be compatible with the
1444 * late static type
1446 let ety_env =
1447 { (Phase.env_with_self env) with
1448 from_class = Some CIstatic } in
1449 Phase.localize ~ety_env env ret in
1450 let env = Env.set_return env hret in
1451 let env = Env.set_fn_kind env f.f_fun_kind in
1452 let env = block env nb.fnb_nast in
1453 let env =
1454 if Nast_terminality.Terminal.block tenv nb.fnb_nast
1455 || nb.fnb_unsafe || !auto_complete
1456 then env
1457 else fun_implicit_return env p hret nb.fnb_nast f.f_fun_kind
1459 is_typing_self := false;
1460 env, hret
1464 (*****************************************************************************)
1465 (* End of anonymous functions. *)
1466 (*****************************************************************************)
1468 and special_func env p func =
1469 let env, ty = (match func with
1470 | Gena e ->
1471 let env, ety = expr env e in
1472 Async.gena env p ety
1473 | Genva el ->
1474 let env, etyl = List.map_env env el expr in
1475 Async.genva env p etyl
1476 | Gen_array_rec e ->
1477 let env, ety = expr env e in
1478 Async.gen_array_rec env p ety
1479 ) in
1480 env, (Reason.Rwitness p, Tclass ((p, SN.Classes.cAwaitable), [ty]))
1482 and requires_consistent_construct = function
1483 | CIstatic -> true
1484 | CIexpr _ -> true
1485 | CIparent -> false
1486 | CIself -> false
1487 | CI _ -> false
1489 and new_object ~check_not_abstract p env c el uel =
1490 let env, class_ = instantiable_cid p env c in
1491 (match class_ with
1492 | None ->
1493 let _ = List.map_env env el expr in
1494 let _ = List.map_env env el expr in
1495 env, (Reason.Runknown_class p, Tobject)
1496 | Some (cname, class_, c_ty) ->
1497 if check_not_abstract && class_.tc_abstract
1498 && not (requires_consistent_construct c) then
1499 uninstantiable_error p c class_.tc_pos class_.tc_name p c_ty;
1500 let env, params = List.map_env env class_.tc_tparams begin fun env _ ->
1501 Env.fresh_unresolved_type env
1502 end in
1503 let env =
1504 if SSet.mem "XHP" class_.tc_extends then env else
1505 let env = call_construct p env class_ params el uel c in
1508 let r_witness = Reason.Rwitness p in
1509 let obj_ty = r_witness, Tclass (cname, params) in
1510 if not (snd class_.tc_construct) then
1511 (match c with
1512 | CIstatic -> Errors.new_inconsistent_construct p cname `static
1513 | CIexpr _ -> Errors.new_inconsistent_construct p cname `classname
1514 | _ -> ());
1515 match c with
1516 | CIstatic ->
1517 env, (r_witness, TUtils.this_of obj_ty)
1518 | CIparent ->
1519 (match (fst class_.tc_construct) with
1520 | Some ce ->
1521 let ety_env = {
1522 type_expansions = [];
1523 substs = SMap.empty;
1524 this_ty = obj_ty;
1525 from_class = None;
1526 } in
1527 let _, ce_type = Phase.localize ~ety_env env ce.ce_type in
1528 ignore (check_abstract_parent_meth SN.Members.__construct p ce_type)
1529 | None -> ());
1530 env, obj_ty
1531 | CI _ | CIself -> env, obj_ty
1532 | CIexpr _ ->
1533 let c_ty = r_witness, snd c_ty in
1534 (* When constructing from a (classname) variable, the variable
1535 * dictates what the constructed object is going to be. This allows
1536 * for generic and dependent types to be correctly carried
1537 * through the 'new $foo()' iff the constructed obj_ty is a
1538 * supertype of the variable-dictated c_ty *)
1539 let env = SubType.sub_type env obj_ty c_ty in
1540 env, c_ty
1543 (* FIXME: we need to separate our instantiability into two parts. Currently,
1544 * all this function is doing is checking if a given type is inhabited --
1545 * that is, whether there are runtime values of type T. However,
1546 * instantiability should be the stricter notion that T has a runtime
1547 * constructor; that is, `new T()` should be valid. In particular, interfaces
1548 * are inhabited, but not instantiable.
1549 * To make this work with classname, we likely need to add something like
1550 * concrete_classname<T>, where T cannot be an interface.
1551 * *)
1552 and instantiable_cid p env cid =
1553 let env, class_id = class_id_for_new p env cid in
1554 (match class_id with
1555 | Some ((pos, name), class_, c_ty) when
1556 class_.tc_kind = Ast.Ctrait || class_.tc_kind = Ast.Cenum ->
1557 (match cid with
1558 | CIexpr _ | CI _ ->
1559 uninstantiable_error p cid class_.tc_pos name pos c_ty;
1560 env, None
1561 | CIstatic | CIparent | CIself -> env, class_id
1563 | Some ((pos, name), class_, c_ty) when
1564 class_.tc_kind = Ast.Cabstract && class_.tc_final ->
1565 uninstantiable_error p cid class_.tc_pos name pos c_ty;
1566 env, None
1567 | None | Some _ -> env, class_id)
1569 and uninstantiable_error reason_pos cid c_tc_pos c_name c_usage_pos c_ty =
1570 let reason_msgl = match cid with
1571 | CIexpr _ ->
1572 let ty_str = "This would be "^Typing_print.error (snd c_ty) in
1573 [(reason_pos, ty_str)]
1574 | _ -> [] in
1575 Errors.uninstantiable_class c_usage_pos c_tc_pos c_name reason_msgl
1577 and exception_ty pos env ty =
1578 let exn_ty = Reason.Rthrow pos, Tclass ((pos, SN.Classes.cException), []) in
1579 Type.sub_type pos (Reason.URthrow) env exn_ty ty
1581 and shape_field_pos = function
1582 | SFlit (p, _) -> p
1583 | SFclass_const ((cls_pos, _), (member_pos, _)) -> Pos.btw cls_pos member_pos
1585 and check_shape_keys_validity env pos keys =
1586 (* If the key is a class constant, get its class name and type. *)
1587 let get_field_info env key =
1588 let key_pos = shape_field_pos key in
1589 (* Empty strings or literals that start with numbers are not
1590 permitted as shape field names. *)
1591 (match key with
1592 | SFlit (_, key_name) ->
1593 if (String.length key_name = 0) then
1594 (Errors.invalid_shape_field_name_empty key_pos)
1595 else if (key_name.[0] >= '0' && key_name.[0] <='9') then
1596 (Errors.invalid_shape_field_name_number key_pos);
1597 env, key_pos, None
1598 | SFclass_const (_, cls as x, y) ->
1599 let env, ty = class_const env pos (CI x, y) in
1600 let env = Typing_enum.check_valid_array_key_type
1601 Errors.invalid_shape_field_type ~allow_any:false
1602 env key_pos ty in
1603 env, key_pos, Some (cls, ty))
1606 let check_field witness_pos witness_info env key =
1607 let env, key_pos, key_info = get_field_info env key in
1608 (match witness_info, key_info with
1609 | Some _, None ->
1610 Errors.invalid_shape_field_literal key_pos witness_pos; env
1611 | None, Some _ ->
1612 Errors.invalid_shape_field_const key_pos witness_pos; env
1613 | None, None -> env
1614 | Some (cls1, ty1), Some (cls2, ty2) ->
1615 if cls1 <> cls2 then
1616 Errors.shape_field_class_mismatch
1617 key_pos witness_pos (strip_ns cls2) (strip_ns cls1);
1618 (* We want to use our own error message here instead of the normal
1619 * unification one. *)
1620 Errors.try_
1621 (fun () -> Unify.iunify env ty1 ty2)
1622 (fun _ ->
1623 Errors.shape_field_type_mismatch
1624 key_pos witness_pos
1625 (Typing_print.error (snd ty2)) (Typing_print.error (snd ty1));
1626 env))
1629 (* Sort the keys by their positions since the error messages will make
1630 * more sense if we take the one that appears first as canonical and if
1631 * they are processed in source order. *)
1632 let cmp_keys x y = Pos.compare (shape_field_pos x) (shape_field_pos y) in
1633 let keys = List.sort cmp_keys keys in
1635 match keys with
1636 | [] -> env
1637 | witness :: rest_keys ->
1638 let env, pos, info = get_field_info env witness in
1639 List.fold_left ~f:(check_field pos info) ~init:env rest_keys
1641 and check_valid_rvalue p env ty =
1642 let env, folded_ty = TUtils.fold_unresolved env ty in
1643 let _deliberately_discarded_env, folded_ety =
1644 Env.expand_type env folded_ty in
1645 match folded_ety with
1646 | r, Tprim Tnoreturn ->
1647 let () = Errors.noreturn_usage p
1648 (Reason.to_string "A noreturn function always throws or exits" r)
1649 in r, Tany
1650 | r, Tprim Tvoid ->
1651 let () = Errors.void_usage p
1652 (Reason.to_string "A void function doesn't return a value" r)
1653 in r, Tany
1654 | _ -> ty
1656 and set_valid_rvalue p env x ty =
1657 let ty = check_valid_rvalue p env ty in
1658 let env = Env.set_local env x ty in
1659 (* We are assigning a new value to the local variable, so we need to
1660 * generate a new expression id
1662 let env = Env.set_local_expr_id env x (Ident.tmp()) in
1663 env, ty
1665 (* Deal with assignment of a value of type ty2 to lvalue e1 *)
1666 and assign p env e1 ty2 =
1667 match e1 with
1668 | (_, Lvar (_, x)) ->
1669 set_valid_rvalue p env x ty2
1670 | (_, Lplaceholder _) ->
1671 let placeholder_ty = Reason.Rplaceholder p, (Tprim Tvoid) in
1672 env, placeholder_ty
1673 | (_, List el) ->
1674 let env, folded_ty2 = TUtils.fold_unresolved env ty2 in
1675 let env, folded_ety2 = Env.expand_type env folded_ty2 in
1676 (match folded_ety2 with
1677 | _, Tclass ((_, x), [elt_type])
1678 when x = SN.Collections.cVector
1679 || x = SN.Collections.cImmVector
1680 || x = SN.Collections.cConstVector ->
1681 let env, _ = List.map_env env el begin fun env e ->
1682 assign (fst e) env e elt_type
1683 end in
1684 env, ty2
1685 | _, Tarraykind (AKvec elt_type) ->
1686 let env, _ = List.map_env env el begin fun env e ->
1687 assign (fst e) env e elt_type
1688 end in
1689 env, ty2
1690 | r, Tarraykind AKany
1691 | r, Tarraykind AKempty
1692 | r, Tany ->
1693 let env, _ = List.map_env env el begin fun env e ->
1694 assign (fst e) env e (r, Tany)
1695 end in
1696 env, ty2
1697 | r, Tclass ((_, coll), [ty1; ty2]) when coll = SN.Collections.cPair ->
1698 (match el with
1699 | [x1; x2] ->
1700 let env, _ = assign p env x1 ty1 in
1701 let env, _ = assign p env x2 ty2 in
1702 env, folded_ety2
1703 | _ ->
1704 Errors.pair_arity p;
1705 env, (r, Tany)
1707 | r, (Ttuple _ | Tarraykind (AKtuple _) as tuple) ->
1708 let p1 = fst e1 in
1709 let p2 = Reason.to_pos r in
1710 let tyl = match tuple with
1711 | Ttuple tyl -> tyl
1712 | Tarraykind (AKtuple fields) -> List.rev (IMap.values fields)
1713 | _ -> Errors.internal_error p2 "Unexpected tuple type"; [] in
1714 let size1 = List.length el in
1715 let size2 = List.length tyl in
1716 if size1 <> size2
1717 then begin
1718 Errors.tuple_arity p2 size2 p1 size1;
1719 env, (r, Tany)
1721 else
1722 let env = List.fold2_exn el tyl ~f:begin fun env lvalue ty2 ->
1723 fst (assign p env lvalue ty2)
1724 end ~init:env in
1725 env, ty2
1726 | _, Tabstract (ak, tyopt) ->
1727 begin match TUtils.get_as_constraints env ak tyopt with
1728 | None -> assign_simple p env e1 ty2
1729 | Some ty -> assign p env e1 ty
1731 | _, (Tmixed | Tarraykind _ | Toption _ | Tprim _
1732 | Tvar _ | Tfun _ | Tanon (_, _)
1733 | Tunresolved _ | Tclass (_, _) | Tobject | Tshape _) ->
1734 assign_simple p env e1 ty2
1736 | _, Class_get _
1737 | _, Obj_get _ ->
1738 let _, locals as lenv = env.Env.lenv in
1739 let no_fakes = Env.empty_fake_members, locals in
1740 (* In this section, we check that the assignment is compatible with
1741 * the real type of a member. Remember that members can change
1742 * type (cf fake_members). But when we assign a value to $this->x,
1743 * we want to make sure that the type assign to $this->x is compatible
1744 * with the actual type hint. In this portion of the code, type-check
1745 * the assignment in an environment without fakes, and therefore
1746 * check that the assignment is compatible with the type of
1747 * the member.
1749 let env, real_type = lvalue { env with Env.lenv = no_fakes } e1 in
1750 let env, exp_real_type = Env.expand_type env real_type in
1751 let env = { env with Env.lenv = lenv } in
1752 let env, ety2 = Env.expand_type env ty2 in
1753 let real_type_list =
1754 match exp_real_type with
1755 | _, Tunresolved tyl -> tyl
1756 | ty -> [ty]
1758 let env = List.fold_left real_type_list ~f:begin fun env real_type ->
1759 Type.sub_type p (Reason.URassign) env real_type ety2
1760 end ~init:env in
1761 (match e1 with
1762 | _, Obj_get ((_, This | _, Lvar _ as obj),
1763 (_, Id (_, member_name)),
1764 _) ->
1765 let env, local = Env.FakeMembers.make p env obj member_name in
1766 let () = (match obj with
1767 | _, This ->
1768 Typing_suggest.save_member member_name env exp_real_type ty2
1769 | _ -> ()
1770 ) in
1771 set_valid_rvalue p env local ty2
1772 | _, Class_get (x, (_, y)) ->
1773 let env, local = Env.FakeMembers.make_static p env x y in
1774 let env, ty3 = set_valid_rvalue p env local ty2 in
1775 (match x with
1776 | CIself
1777 | CIstatic ->
1778 Typing_suggest.save_member y env exp_real_type ty2;
1779 | _ -> ());
1780 env, ty3
1781 | _ -> env, ty2
1783 | _, Array_get ((_, Lvar (_, lvar)) as shape, ((Some _) as e2)) ->
1784 let access_type = Typing_arrays.static_array_access env e2 in
1785 (* In the case of an assignment of the form $x['new_field'] = ...;
1786 * $x could be a shape where the field 'new_field' is not yet defined.
1787 * When that is the case we want to add the field to its type.
1789 let env, shape_ty = expr env shape in
1790 let env, shape_ty = Typing_arrays.update_array_type_on_lvar_assignment
1791 p access_type env shape_ty in
1792 let env, _ = set_valid_rvalue p env lvar shape_ty in
1793 (* We still need to call assign_simple in order to bind the freshly
1794 * created variable in added shape field. Moreover, it's needed because
1795 * shape_ty could be more than just a shape. It could be an unresolved
1796 * type where some elements are shapes and some others are not.
1798 assign_simple p env e1 ty2
1799 | _, This ->
1800 Errors.this_lvalue p;
1801 env, (Reason.Rwitness p, Tany)
1802 | pref, Unop (Ast.Uref, e1') ->
1803 (* references can be "lvalues" in foreach bindings *)
1804 if Env.is_strict env then
1805 Errors.reference_expr pref;
1806 assign p env e1' ty2
1807 | _ ->
1808 assign_simple p env e1 ty2
1810 and assign_simple pos env e1 ty2 =
1811 let env, ty1 = lvalue env e1 in
1813 let ty2 = check_valid_rvalue pos env ty2 in
1815 let env, ty2 = TUtils.unresolved env ty2 in
1816 let env = Type.sub_type pos (Reason.URassign) env ty1 ty2 in
1817 env, ty2
1819 and array_field_value env = function
1820 | Nast.AFvalue x
1821 | Nast.AFkvalue (_, x) ->
1822 let env, ty = expr env x in
1823 Typing_env.unbind env ty
1825 and yield_field_value env x = array_field_value env x
1827 and array_field_key env = function
1828 | Nast.AFvalue (p, _) ->
1829 env, (Reason.Rwitness p, Tprim Tint)
1830 | Nast.AFkvalue (x, _) ->
1831 let env, ty = expr env x in
1832 Typing_env.unbind env ty
1834 and yield_field_key env = function
1835 | Nast.AFvalue (p, _) ->
1836 env, (match Env.get_fn_kind env with
1837 | Ast.FSync
1838 | Ast.FAsync ->
1839 Errors.internal_error p "yield found in non-generator";
1840 Reason.Rnone, Tany
1841 | Ast.FGenerator ->
1842 (Reason.Rwitness p, Tprim Tint)
1843 | Ast.FAsyncGenerator ->
1844 (Reason.Ryield_asyncnull p, Toption (Env.fresh_type ())))
1845 | Nast.AFkvalue (x, _) ->
1846 expr env x
1848 and akshape_field env = function
1849 | Nast.AFkvalue (k, v) ->
1850 let env, tk = expr env k in
1851 let env, tk = Typing_env.unbind env tk in
1852 let env, tk = TUtils.unresolved env tk in
1853 let env, tv = expr env v in
1854 let env, tv = Typing_env.unbind env tv in
1855 let env, tv = TUtils.unresolved env tv in
1856 let field_name = match TUtils.shape_field_name env Pos.none (snd k) with
1857 | Some field_name -> field_name
1858 | None -> assert false in (* Typing_arrays.is_shape_like_array
1859 * should have prevented this *)
1860 env, (field_name, (tk, tv))
1861 | Nast.AFvalue _ -> assert false (* Typing_arrays.is_shape_like_array
1862 * should have prevented this *)
1863 and aktuple_field env = function
1864 | Nast.AFvalue v ->
1865 let env, tv = expr env v in
1866 let env, tv = Typing_env.unbind env tv in
1867 TUtils.unresolved env tv
1868 | Nast.AFkvalue _ -> assert false (* check_consistent_fields
1869 * should have prevented this *)
1870 and check_parent_construct pos env el uel env_parent =
1871 let check_not_abstract = false in
1872 let env, env_parent = Phase.localize_with_self env env_parent in
1873 let env, parent = new_object ~check_not_abstract pos env CIparent el uel in
1874 let env, _ = Type.unify pos (Reason.URnone) env env_parent parent in
1875 env, (Reason.Rwitness pos, Tprim Tvoid)
1877 and call_parent_construct pos env el uel =
1878 let parent = Env.get_parent env in
1879 match parent with
1880 | _, Tapply _ ->
1881 check_parent_construct pos env el uel parent
1882 | _, (Tany | Tmixed | Tarray (_, _) | Tgeneric (_, _) | Toption _ | Tprim _
1883 | Tfun _ | Ttuple _ | Tshape _ | Taccess (_, _) | Tthis
1884 ) -> (* continue here *)
1885 let default = env, (Reason.Rnone, Tany) in
1886 match Env.get_self env with
1887 | _, Tclass ((_, self), _) ->
1888 (match Env.get_class env self with
1889 | Some ({tc_kind = Ast.Ctrait; _}
1890 as trait) ->
1891 (match trait_most_concrete_req_class trait env with
1892 | None -> Errors.parent_in_trait pos; default
1893 | Some (_, parent_ty) ->
1894 check_parent_construct pos env el uel parent_ty
1896 | Some self_tc ->
1897 if not self_tc.tc_members_fully_known
1898 then () (* Don't know the hierarchy, assume it's correct *)
1899 else Errors.undefined_parent pos;
1900 default
1901 | None -> assert false)
1902 | _, (Tany | Tmixed | Tarraykind _ | Toption _
1903 | Tprim _ | Tfun _ | Ttuple _ | Tshape _ | Tvar _
1904 | Tabstract (_, _) | Tanon (_, _) | Tunresolved _ | Tobject
1905 ) ->
1906 Errors.parent_outside_class pos; default
1908 (* parent::method() in a class definition invokes the specific parent
1909 * version of the method ... it better be callable *)
1910 and check_abstract_parent_meth mname pos fty =
1911 if is_abstract_ft fty then Errors.parent_abstract_call mname pos (Reason.to_pos (fst fty)) ;
1914 and is_abstract_ft fty = match fty with
1915 | _r, Tfun { ft_abstract = true; _ } -> true
1916 | _r, (Tany | Tmixed | Tarraykind _ | Toption _ | Tprim _
1917 | Tvar _ | Tfun _ | Tclass (_, _) | Tabstract (_, _) | Ttuple _
1918 | Tanon _ | Tunresolved _ | Tobject | Tshape _
1920 -> false
1922 (* Depending on the kind of expression we are dealing with
1923 * The typing of call is different.
1925 and dispatch_call p env call_type (fpos, fun_expr as e) el uel =
1926 match fun_expr with
1927 | Id (_, pseudo_func) when pseudo_func = SN.SpecialFunctions.echo ->
1928 let env, _ = List.map_env env el expr in
1929 env, (Reason.Rwitness p, Tprim Tvoid)
1930 | Id (_, pseudo_func)
1931 when
1932 pseudo_func = SN.PseudoFunctions.isset
1933 || pseudo_func = SN.PseudoFunctions.empty ->
1934 let env, _ = List.map_env env el expr in
1935 if uel <> [] then
1936 Errors.unpacking_disallowed_builtin_function p pseudo_func;
1937 if Env.is_strict env then
1938 Errors.isset_empty_in_strict p pseudo_func;
1939 env, (Reason.Rwitness p, Tprim Tbool)
1940 | Id (_, pseudo_func) when pseudo_func = SN.PseudoFunctions.unset ->
1941 let env, _ = List.map_env env el expr in
1942 if uel <> [] then
1943 Errors.unpacking_disallowed_builtin_function p pseudo_func;
1944 let env = if Env.is_strict env then
1945 (match el, uel with
1946 | [(_, Array_get (ea, Some _))], [] ->
1947 let env, ty = expr env ea in
1948 if List.exists ~f:(fun super -> SubType.is_sub_type env super ty) [
1949 (Reason.Rnone, (Tclass ((Pos.none, SN.Collections.cDict),
1950 [(Reason.Rnone, Tany); (Reason.Rnone, Tany)])));
1951 (Reason.Rnone, Tarraykind AKany)
1952 ] then env
1953 else begin
1954 let env, (r, ety) = Env.expand_type env ty in
1955 Errors.unset_nonidx_in_strict
1957 (Reason.to_string ("This is " ^ Typing_print.error ety) r);
1960 | _ -> Errors.unset_nonidx_in_strict p []; env)
1961 else env in
1962 (match el with
1963 | [(p, Obj_get (_, _, OG_nullsafe))] ->
1964 begin
1965 Errors.nullsafe_property_write_context p;
1966 env, (Reason.Rwitness p, Tany)
1967 end;
1968 | _ -> env, (Reason.Rwitness p, Tprim Tvoid))
1969 | Id (cp, get_called_class) when
1970 get_called_class = SN.StdlibFunctions.get_called_class
1971 && el = [] && uel = [] ->
1972 (* get_called_class fetches the late-bound class *)
1973 if Env.is_outside_class env then Errors.static_outside_class p;
1974 class_const env p (CIstatic, (cp, SN.Members.mClass))
1975 | Id ((_, array_filter) as id)
1976 when array_filter = SN.StdlibFunctions.array_filter && el <> [] && uel = [] ->
1977 (* dispatch the call to typecheck the arguments *)
1978 let env, fty = fun_type_of_id env id in
1979 let env, fty = Env.expand_type env fty in
1980 let env, res = call p env fty el uel in
1981 (* but ignore the result and overwrite it with custom return type *)
1982 let x = List.hd_exn el in
1983 let env, ty = expr env x in
1984 let explain_array_filter (r, t) =
1985 (Reason.Rarray_filter (p, r), t) in
1986 let get_value_type env tv =
1987 let env, tv = if List.length el > 1 then env, tv else non_null env tv in
1988 env, explain_array_filter tv in
1989 let rec get_array_filter_return_type env ty =
1990 let env, ety = Env.expand_type env ty in
1991 (match ety with
1992 | (_, Tarraykind (AKany | AKempty)) as array_type ->
1993 env, array_type
1994 | (_, Tarraykind (AKtuple _)) ->
1995 let env, ty = Typing_arrays.downcast_aktypes env ty in
1996 get_array_filter_return_type env ty
1997 | (r, Tarraykind (AKvec tv)) ->
1998 let env, tv = get_value_type env tv in
1999 env, (r, Tarraykind (AKvec tv))
2000 | (r, Tunresolved x) ->
2001 let env, x = List.map_env env x get_array_filter_return_type in
2002 env, (r, Tunresolved x)
2003 | (r, Tany) ->
2004 env, (r, Tany)
2005 | (r, _) ->
2006 let tk, tv = Env.fresh_type(), Env.fresh_type() in
2007 Errors.try_
2008 (fun () ->
2009 let keyed_container = (
2010 Reason.Rnone,
2011 Tclass (
2012 (Pos.none, SN.Collections.cKeyedContainer), [tk; tv]
2014 ) in
2015 let env = SubType.sub_type env keyed_container ety in
2016 let env, tv = get_value_type env tv in
2017 env, (r, Tarraykind (AKmap (
2018 (explain_array_filter tk),
2021 (fun _ -> Errors.try_
2022 (fun () ->
2023 let container = (
2024 Reason.Rnone,
2025 Tclass (
2026 (Pos.none, SN.Collections.cContainer), [tv]
2028 ) in
2029 let env = SubType.sub_type env container ety in
2030 let env, tv = get_value_type env tv in
2031 env, (r, Tarraykind (AKmap (
2032 (explain_array_filter (r, Tprim Tarraykey)),
2033 tv))))
2034 (fun _ -> env, res)))
2035 in get_array_filter_return_type env ty
2036 | Id (p, type_structure)
2037 when type_structure = SN.StdlibFunctions.type_structure
2038 && (List.length el = 2) && uel = [] ->
2039 (match el with
2040 | [e1; e2] ->
2041 (match e2 with
2042 | p, Nast.String cst ->
2043 (* find the class constant implicitly defined by the typeconst *)
2044 let cid = (match e1 with
2045 | _, Class_const (cid, (_, x))
2046 | _, Class_get (cid, (_, x)) when x = SN.Members.mClass -> cid
2047 | _ -> Nast.CIexpr e1) in
2048 class_const ~incl_tc:true env p (cid, cst)
2049 | _ ->
2050 Errors.illegal_type_structure p "second argument is not a string";
2051 env, (Reason.Rnone, Tany))
2052 | _ -> assert false)
2053 | Id ((_, array_map) as x)
2054 when array_map = SN.StdlibFunctions.array_map && el <> [] && uel = [] ->
2055 let env, fty = fun_type_of_id env x in
2056 let env, fty = Env.expand_type env fty in
2057 let env, fty = match fty, el with
2058 | ((r_fty, Tfun fty), _::args) when args <> [] ->
2059 let arity = List.length args in
2061 Builds a function with signature:
2063 function<T1, ..., Tn, Tr>(
2064 (function(T1, ..., Tn):Tr),
2065 Container<T1>,
2066 ...,
2067 Container<Tn>,
2068 ): R
2070 where R is constructed by build_output_container applied to Tr
2072 let build_function build_output_container =
2073 let vars = List.map args (fun _ -> Env.fresh_type()) in
2074 let tr = Env.fresh_type() in
2075 let f = (None, (
2076 r_fty,
2077 Tfun {
2078 ft_pos = fty.ft_pos;
2079 ft_deprecated = None;
2080 ft_abstract = false;
2081 ft_arity = Fstandard (arity, arity); ft_tparams = [];
2082 ft_params = List.map vars (fun x -> (None, x));
2083 ft_ret = tr;
2085 )) in
2086 let containers = List.map vars (fun var ->
2087 (None,
2088 (r_fty,
2089 Tclass ((fty.ft_pos, SN.Collections.cContainer), [var])
2092 ) in
2093 (r_fty, Tfun {fty with
2094 ft_arity = Fstandard (arity+1, arity+1);
2095 ft_params = f::containers;
2096 ft_ret = build_output_container tr;
2097 }) in
2100 Takes a Container type and returns a function that can "pack" a type
2101 into an array of appropriate shape, preserving the key type, i.e.:
2102 array -> f, where f R = array
2103 array<X> -> f, where f R = array<R>
2104 array<X, Y> -> f, where f R = array<X, R>
2105 Vector<X> -> f where f R = array<R>
2106 KeyedContainer<X, Y> -> f, where f R = array<X, R>
2107 Container<X> -> f, where f R = array<arraykey, R>
2108 X -> f, where f R = Y
2110 let rec build_output_container
2111 (env:Env.env) (x:locl ty) : (Env.env * (locl ty -> locl ty)) =
2112 let env, x = Env.expand_type env x in (match x with
2113 | (_, Tarraykind (AKany | AKempty)) as array_type ->
2114 env, (fun _ -> array_type)
2115 | (_, Tarraykind (AKtuple _ )) ->
2116 let env, x = Typing_arrays.downcast_aktypes env x in
2117 build_output_container env x
2118 | (r, Tarraykind AKvec _) ->
2119 env, (fun tr -> (r, Tarraykind (AKvec(tr))) )
2120 | ((_, Tany) as any) ->
2121 env, (fun _ -> any)
2122 | (r, Tunresolved x) ->
2123 let env, x = List.map_env env x build_output_container in
2124 env, (fun tr -> (r, Tunresolved (List.map x (fun f -> f tr))))
2125 | (r, _) ->
2126 let tk, tv = Env.fresh_type(), Env.fresh_type() in
2127 let try_vector env =
2128 let vector = (
2129 r_fty,
2130 Tclass (
2131 (fty.ft_pos, SN.Collections.cConstVector), [tv]
2133 ) in
2134 let env = SubType.sub_type env vector x in
2135 env, (fun tr -> (r, Tarraykind (
2136 AKvec(tr)
2137 ))) in
2138 let try_keyed_container env =
2139 let keyed_container = (
2140 r_fty,
2141 Tclass (
2142 (fty.ft_pos, SN.Collections.cKeyedContainer), [tk; tv]
2144 ) in
2145 let env = SubType.sub_type env keyed_container x in
2146 env, (fun tr -> (r, Tarraykind (AKmap (
2149 )))) in
2150 let try_container env =
2151 let container = (
2152 r_fty,
2153 Tclass (
2154 (fty.ft_pos, SN.Collections.cContainer), [tv]
2156 ) in
2157 let env = SubType.sub_type env container x in
2158 env, (fun tr -> (r, Tarraykind (AKmap (
2159 (r, Tprim Tarraykey),
2160 tr)))) in
2161 Errors.try_
2162 (fun () ->
2163 try_vector env)
2164 (fun _ -> Errors.try_
2165 (fun () ->
2166 try_keyed_container env)
2167 (fun _ -> Errors.try_
2168 (fun () ->
2169 try_container env)
2170 (fun _ -> env, (fun _ -> (Reason.Rwitness p, Tany)))))) in
2172 Single argument calls preserve the key type, multi argument
2173 calls always return an array<Tr>
2175 (match args with
2176 | [x] ->
2177 let env, x = expr env x in
2178 let env, output_container = build_output_container env x in
2179 env, build_function output_container
2180 | _ ->
2181 env, build_function (fun tr ->
2182 (r_fty, Tarraykind (AKvec(tr)))))
2183 | _ -> env, fty in
2184 call p env fty el []
2185 | Id ((_, idx) as id) when idx = SN.FB.idx ->
2186 (* Directly call get_fun so that we can muck with the type before
2187 * instantiation -- much easier to work in terms of Tgeneric Tk/Tv than
2188 * trying to figure out which Tvar is which. *)
2189 (match Env.get_fun env (snd id) with
2190 | Some fty ->
2191 let param1, (name2, (r2, _)), (name3, (r3, _)) =
2192 match fty.ft_params with
2193 | [param1; param2; param3] -> param1, param2, param3
2194 | _ -> assert false in
2195 let params, ret = match List.length el with
2196 | 2 ->
2197 let param2 = (name2, (r2, Toption (r2, Tgeneric ("Tk", [])))) in
2198 let rret = fst fty.ft_ret in
2199 let ret = (rret, Toption (rret, Tgeneric ("Tv", []))) in
2200 [param1; param2], ret
2201 | 3 ->
2202 let param2 = (name2, (r2, Tgeneric ("Tk", []))) in
2203 let param3 = (name3, (r3, Tgeneric ("Tv", []))) in
2204 let ret = (fst fty.ft_ret, Tgeneric ("Tv", [])) in
2205 [param1; param2; param3], ret
2206 | _ -> fty.ft_params, fty.ft_ret in
2207 let fty = { fty with ft_params = params; ft_ret = ret } in
2208 let ety_env = Phase.env_with_self env in
2209 let env, fty = Phase.localize_ft ~ety_env env fty in
2210 let tfun = Reason.Rwitness fty.ft_pos, Tfun fty in
2211 call p env tfun el []
2212 | None -> unbound_name env id)
2213 | Class_const (CI(_, shapes) as class_id, ((_, idx) as method_id))
2214 when shapes = SN.Shapes.cShapes && idx = SN.Shapes.idx ->
2215 overload_function p env class_id method_id el uel
2216 begin fun env fty res el -> match el with
2217 | [shape; field] ->
2218 let env, shape_ty = expr env shape in
2219 Typing_shapes.idx env fty shape_ty field None
2220 | [shape; field; default] ->
2221 let env, shape_ty = expr env shape in
2222 let env, default_ty = expr env default in
2223 Typing_shapes.idx env fty shape_ty field
2224 (Some ((fst default), default_ty))
2225 | _ -> env, res
2227 | Class_const (CI(_, shapes) as class_id, ((_, key_exists) as method_id))
2228 when shapes = SN.Shapes.cShapes && key_exists = SN.Shapes.keyExists ->
2229 overload_function p env class_id method_id el uel
2230 begin fun env fty res el -> match el with
2231 | [shape; field] ->
2232 let env, shape_ty = expr env shape in
2233 (* try acessing the field, to verify existence, but ignore
2234 * the returned type and keep the one coming from function
2235 * return type hint *)
2236 let env, _ = Typing_shapes.idx env fty shape_ty field None in
2237 env, res
2238 | _ -> env, res
2240 | Class_const (CI(_, shapes) as class_id, ((_, remove_key) as method_id))
2241 when shapes = SN.Shapes.cShapes && remove_key = SN.Shapes.removeKey ->
2242 overload_function p env class_id method_id el uel
2243 begin fun env _ res el -> match el with
2244 | [shape; field] -> begin match shape with
2245 | (_, Lvar (_, lvar)) ->
2246 let env, shape_ty = expr env shape in
2247 let env, shape_ty =
2248 Typing_shapes.remove_key p env shape_ty field in
2249 let env, _ = set_valid_rvalue p env lvar shape_ty in
2250 env, res
2251 | _ ->
2252 Errors.invalid_shape_remove_key (fst shape);
2253 env, res
2255 | _ -> env, res
2257 | Class_const (CI(_, shapes) as class_id, ((_, to_array) as method_id))
2258 when shapes = SN.Shapes.cShapes && to_array = SN.Shapes.toArray ->
2259 overload_function p env class_id method_id el uel
2260 begin fun env _ res el -> match el with
2261 | [shape] ->
2262 let env, shape_ty = expr env shape in
2263 Typing_shapes.to_array env shape_ty res
2264 | _ -> env, res
2266 | Class_const (CIparent, (_, construct))
2267 when construct = SN.Members.__construct ->
2268 Typing_hooks.dispatch_parent_construct_hook env p;
2269 call_parent_construct p env el uel
2270 | Class_const (CIparent, m) ->
2271 let env, ty1 = static_class_id p env CIparent in
2272 if Env.is_static env
2273 then begin
2274 (* in static context, you can only call parent::foo() on static
2275 * methods *)
2276 let env, fty = class_get ~is_method:true ~is_const:false env ty1 m CIparent in
2277 let env, fty = Env.expand_type env fty in
2278 let fty = check_abstract_parent_meth (snd m) p fty in
2279 call p env fty el uel
2281 else begin
2282 (* in instance context, you can call parent:foo() on static
2283 * methods as well as instance methods *)
2284 (match class_contains_smethod env ty1 m with
2285 | None ->
2286 (* parent::nonStaticFunc() is really weird. It's calling a method
2287 * defined on the parent class, but $this is still the child class.
2288 * We can deal with this by hijacking the continuation that
2289 * calculates the SN.Typehints.this type *)
2290 let this_ty = ExprDepTy.make env CIstatic
2291 (Reason.Rwitness fpos, TUtils.this_of (Env.get_self env)) in
2292 let k_lhs _ = this_ty in
2293 let env, method_, _ =
2294 obj_get_ ~is_method:true ~nullsafe:None env ty1 CIparent m
2295 begin fun (env, fty, _) ->
2296 let env, fty = Env.expand_type env fty in
2297 let fty = check_abstract_parent_meth (snd m) p fty in
2298 let env, method_ = call p env fty el uel in
2299 env, method_, None
2301 k_lhs
2303 env, method_
2304 | Some _ ->
2305 let env, fty = class_get ~is_method:true ~is_const:false env ty1 m CIparent in
2306 let env, fty = Env.expand_type env fty in
2307 let fty = check_abstract_parent_meth (snd m) p fty in
2308 call p env fty el uel
2311 | Class_const(e1, m) ->
2312 TUtils.process_static_find_ref e1 m;
2313 let env, ty1 = static_class_id p env e1 in
2314 let env, fty = class_get ~is_method:true ~is_const:false env ty1 m e1 in
2315 let env, fty = Env.expand_type env fty in
2316 let () = match e1 with
2317 | CIself when is_abstract_ft fty ->
2318 (match Env.get_self env with
2319 | _, Tclass ((_, self), _) ->
2320 (* at runtime, self:: in a trait is a call to whatever
2321 * self:: is in the context of the non-trait "use"-ing
2322 * the trait's code *)
2323 (match Env.get_class env self with
2324 | Some { tc_kind = Ast.Ctrait; _ } -> ()
2325 | _ -> Errors.self_abstract_call (snd m) p (Reason.to_pos (fst fty))
2327 | _ -> ())
2328 | CI c when is_abstract_ft fty ->
2329 Errors.classname_abstract_call (snd c) (snd m) p (Reason.to_pos (fst fty))
2330 | _ -> () in
2331 call p env fty el uel
2332 | Obj_get(e1, (_, Id m), nullflavor) ->
2333 let is_method = call_type = Cnormal in
2334 let env, ty1 = expr env e1 in
2335 let nullsafe =
2336 (match nullflavor with
2337 | OG_nullthrows -> None
2338 | OG_nullsafe -> Some p
2339 ) in
2340 let fn = (fun (env, fty, _) ->
2341 let env, fty = Env.expand_type env fty in
2342 let env, method_ = call p env fty el uel in
2343 env, method_, None) in
2344 obj_get ~is_method ~nullsafe env ty1 (CIexpr e1) m fn
2345 | Fun_id x
2346 | Id x ->
2347 Typing_hooks.dispatch_id_hook x env;
2348 let env, fty = fun_type_of_id env x in
2349 let env, fty = Env.expand_type env fty in
2350 call p env fty el uel
2351 | _ ->
2352 let env, fty = expr env e in
2353 let env, fty = Env.expand_type env fty in
2354 call p env fty el uel
2356 and fun_type_of_id env x =
2357 Typing_hooks.dispatch_fun_id_hook x;
2358 let env, fty =
2359 match Env.get_fun env (snd x) with
2360 | None -> unbound_name env x
2361 | Some fty ->
2362 let ety_env = Phase.env_with_self env in
2363 let env, fty = Phase.localize_ft ~ety_env env fty in
2364 env, (Reason.Rwitness fty.ft_pos, Tfun fty)
2366 env, fty
2368 (*****************************************************************************)
2369 (* Function type-checking expressions accessing an array (example: $x[...]).
2370 * The parameter is_lvalue is true when the expression is on the left hand
2371 * side of an assignment (example: $x[...] = 0).
2373 (*****************************************************************************)
2374 and array_get is_lvalue p env ty1 ety1 e2 ty2 =
2375 (* This is a little weird -- we enforce the right arity when you use certain
2376 * collections, even in partial mode (where normally completely omitting the
2377 * type parameter list is admitted). Basically the "omit type parameter"
2378 * hole was for compatibility with certain interfaces like ArrayAccess, not
2379 * for collections! But it's hard to go back on now, so since we've always
2380 * errored (with an inscrutable error message) when you try to actually use
2381 * a collection with omitted type parameters, we can continue to error and
2382 * give a more useful error message. *)
2383 let arity_error (_, name) =
2384 Errors.array_get_arity p name (Reason.to_pos (fst ty1))
2386 match snd ety1 with
2387 | Tunresolved tyl ->
2388 let env, tyl = List.map_env env tyl begin fun env ty1 ->
2389 let env, ety1 = Env.expand_type env ty1 in
2390 array_get is_lvalue p env ty1 ety1 e2 ty2
2391 end in
2392 env, (fst ety1, Tunresolved tyl)
2393 | Tarraykind (AKvec ty) ->
2394 let ty1 = Reason.Ridx (fst e2, fst ety1), Tprim Tint in
2395 let env = Type.sub_type p Reason.index_array env ty1 ty2 in
2396 env, ty
2397 | Tclass ((_, cn) as id, argl)
2398 when cn = SN.Collections.cVector ->
2399 let ty = match argl with
2400 | [ty] -> ty
2401 | _ -> arity_error id; Reason.Rwitness p, Tany in
2402 let ty1 = Reason.Ridx_vector (fst e2), Tprim Tint in
2403 let env = Type.sub_type p (Reason.index_class cn) env ty1 ty2 in
2404 env, ty
2405 | Tclass ((_, cn) as id, argl)
2406 when cn = SN.Collections.cMap
2407 || cn = SN.Collections.cStableMap
2408 || cn = SN.Collections.cDict ->
2409 let (k, v) = match argl with
2410 | [k; v] -> (k, v)
2411 | _ ->
2412 arity_error id;
2413 let any = (Reason.Rwitness p, Tany) in
2414 any, any
2416 let env, ty2 = TUtils.unresolved env ty2 in
2417 let env, _ = Type.unify p (Reason.index_class cn) env k ty2 in
2418 env, v
2419 (* Certain container/collection types are intended to be immutable/const,
2420 * thus they should never appear as a lvalue when indexing i.e.
2422 * $x[0] = 100; // ERROR
2423 * $x[0]; // OK
2425 | Tclass ((_, cn) as id, argl)
2426 when cn = SN.Collections.cConstMap
2427 || cn = SN.Collections.cImmMap
2428 || cn = SN.Collections.cIndexish
2429 || cn = SN.Collections.cKeyedContainer ->
2430 if is_lvalue then
2431 error_const_mutation env p ety1
2432 else
2433 let (k, v) = match argl with
2434 | [k; v] -> (k, v)
2435 | _ ->
2436 arity_error id;
2437 let any = (Reason.Rwitness p, Tany) in
2438 any, any
2440 let env = Type.sub_type p (Reason.index_class cn) env k ty2 in
2441 env, v
2442 | Tclass ((_, cn) as id, argl)
2443 when not is_lvalue &&
2444 (cn = SN.Collections.cConstVector || cn = SN.Collections.cImmVector) ->
2445 let ty = match argl with
2446 | [ty] -> ty
2447 | _ -> arity_error id; Reason.Rwitness p, Tany in
2448 let ty1 = Reason.Ridx (fst e2, fst ety1), Tprim Tint in
2449 let env, _ = Type.unify p (Reason.index_class cn) env ty2 ty1 in
2450 env, ty
2451 | Tclass ((_, cn), _)
2452 when is_lvalue &&
2453 (cn = SN.Collections.cConstVector || cn = SN.Collections.cImmVector) ->
2454 error_const_mutation env p ety1
2455 | Tarraykind (AKmap (k, v)) ->
2456 let env, ty2 = TUtils.unresolved env ty2 in
2457 let env, _ = Type.unify p Reason.index_array env k ty2 in
2458 env, v
2459 | Tarraykind ((AKshape _ | AKtuple _) as akind) ->
2460 let key = Typing_arrays.static_array_access env (Some e2) in
2461 let env, result = match key, akind with
2462 | Typing_arrays.AKtuple_index index, AKtuple fields ->
2463 begin match IMap.get index fields with
2464 | Some ty ->
2465 let ty1 = Reason.Ridx (fst e2, fst ety1), Tprim Tint in
2466 let env = Type.sub_type p Reason.index_array env ty1 ty2 in
2467 env, Some ty
2468 | None -> env, None
2470 | Typing_arrays.AKshape_key field_name, AKshape fdm ->
2471 begin match Nast.ShapeMap.get field_name fdm with
2472 | Some (k, v) ->
2473 let env, ty2 = TUtils.unresolved env ty2 in
2474 let env, _ = Type.unify p Reason.index_array env k ty2 in
2475 env, Some v
2476 | None -> env, None
2478 | _ -> env, None in
2479 begin match result with
2480 | Some ty -> env, ty
2481 | None ->
2482 (* Key is dynamic, or static and not in the array - treat it as
2483 regular map or vec like array *)
2484 let env, ty1 = Typing_arrays.downcast_aktypes env ty1 in
2485 let env, ety1 = Env.expand_type env ty1 in
2486 array_get is_lvalue p env ty1 ety1 e2 ty2
2488 | Tany | Tarraykind (AKany | AKempty)-> env, (Reason.Rnone, Tany)
2489 | Tprim Tstring ->
2490 let ty = Reason.Rwitness p, Tprim Tstring in
2491 let env, ty = Type.unify p Reason.URnone env ty1 ty in
2492 let int = Reason.Ridx (fst e2, fst ety1), Tprim Tint in
2493 let env, _ = Type.unify p Reason.index_array env ty2 int in
2494 env, ty
2495 | Ttuple tyl ->
2496 (match e2 with
2497 | p, Int n ->
2498 (try
2499 let idx = int_of_string (snd n) in
2500 let nth = List.nth_exn tyl idx in
2501 env, nth
2502 with _ ->
2503 Errors.typing_error p (Reason.string_of_ureason Reason.index_tuple);
2504 env, (Reason.Rwitness p, Tany)
2506 | p, _ ->
2507 Errors.typing_error p (Reason.string_of_ureason Reason.URtuple_access);
2508 env, (Reason.Rwitness p, Tany)
2510 | Tclass ((_, cn) as id, argl) when cn = SN.Collections.cPair ->
2511 let (ty1, ty2) = match argl with
2512 | [ty1; ty2] -> (ty1, ty2)
2513 | _ ->
2514 arity_error id;
2515 let any = (Reason.Rwitness p, Tany) in
2516 any, any
2518 (match e2 with
2519 | p, Int n ->
2520 (try
2521 let idx = int_of_string (snd n) in
2522 let nth = List.nth_exn [ty1; ty2] idx in
2523 env, nth
2524 with _ ->
2525 Errors.typing_error p @@
2526 Reason.string_of_ureason (Reason.index_class cn);
2527 env, (Reason.Rwitness p, Tany)
2529 | p, _ ->
2530 Errors.typing_error p (Reason.string_of_ureason Reason.URpair_access);
2531 env, (Reason.Rwitness p, Tany)
2533 | Tshape (_, fdm) ->
2534 let p, e2' = e2 in
2535 (match TUtils.shape_field_name env p e2' with
2536 | None ->
2537 (* there was already an error in shape_field name,
2538 don't report another one for a missing field *)
2539 env, (Reason.Rwitness p, Tany)
2540 | Some field -> (match ShapeMap.get field fdm with
2541 | None ->
2542 Errors.undefined_field
2543 p (TUtils.get_printable_shape_field_name field);
2544 env, (Reason.Rwitness p, Tany)
2545 | Some ty -> env, ty)
2547 | Toption _ ->
2548 Errors.null_container p
2549 (Reason.to_string
2550 "This is what makes me believe it can be null"
2551 (fst ety1)
2553 env, (Reason.Rwitness p, Tany)
2554 | Tobject ->
2555 if Env.is_strict env
2556 then error_array env p ety1
2557 else env, (Reason.Rnone, Tany)
2558 | Tabstract (AKnewtype (ts, [ty]), Some (r, Tshape (fk, fields)))
2559 when ts = SN.FB.cTypeStructure ->
2560 let env, fields = TS.transform_shapemap env ty fields in
2561 let ty = r, Tshape (fk, fields) in
2562 array_get is_lvalue p env ty ty e2 ty2
2563 | Tabstract (ak, tyopt) ->
2564 begin match TUtils.get_as_constraints env ak tyopt with
2565 | None -> error_array env p ety1
2566 | Some ty ->
2567 let env, ety = Env.expand_type env ty in
2568 Errors.try_
2569 (fun () -> array_get is_lvalue p env ty ety e2 ty2)
2570 (fun _ -> error_array env p ety1)
2572 | Tmixed | Tprim _ | Tvar _ | Tfun _
2573 | Tclass (_, _) | Tanon (_, _) ->
2574 error_array env p ety1
2576 and array_append is_lvalue p env ty1 =
2577 let env, ty1 = TUtils.fold_unresolved env ty1 in
2578 let env, ety1 = Env.expand_type env ty1 in
2579 match snd ety1 with
2580 | Tany | Tarraykind (AKany | AKempty) -> env, (Reason.Rnone, Tany)
2581 | Tclass ((_, n), [ty])
2582 when n = SN.Collections.cVector || n = SN.Collections.cSet ->
2583 env, ty
2584 | Tclass ((_, n), [])
2585 when n = SN.Collections.cVector || n = SN.Collections.cSet ->
2586 (* Handle the case where "Vector" or "Set" was used as a typehint
2587 without type parameters *)
2588 env, (Reason.Rnone, Tany)
2589 | Tclass ((_, n), [tkey; tvalue]) when n = SN.Collections.cMap ->
2590 (* You can append a pair to a map *)
2591 env, (Reason.Rmap_append p, Tclass ((p, SN.Collections.cPair), [tkey; tvalue]))
2592 | Tclass ((_, n), []) when n = SN.Collections.cMap ->
2593 (* Handle the case where "Map" was used as a typehint without
2594 type parameters *)
2595 env, (Reason.Rmap_append p, Tclass ((p, SN.Collections.cPair), []))
2596 | Tarraykind (AKvec ty) ->
2597 env, ty
2598 | Tobject ->
2599 if Env.is_strict env
2600 then error_array_append env p ety1
2601 else env, (Reason.Rnone, Tany)
2602 | Tabstract (ak, tyopt) ->
2603 begin match TUtils.get_as_constraints env ak tyopt with
2604 | None -> error_array_append env p ety1
2605 | Some ty ->
2606 Errors.try_
2607 (fun () -> array_append is_lvalue p env ty)
2608 (fun _ -> error_array_append env p ety1)
2610 | Tmixed | Tarraykind _ | Toption _ | Tprim _
2611 | Tvar _ | Tfun _ | Tclass (_, _) | Ttuple _
2612 | Tanon (_, _) | Tunresolved _ | Tshape _ ->
2613 error_array_append env p ety1
2615 and error_array env p (r, ty) =
2616 Errors.array_access p (Reason.to_pos r) (Typing_print.error ty);
2617 env, (Reason.Rwitness p, Tany)
2619 and error_array_append env p (r, ty) =
2620 Errors.array_append p (Reason.to_pos r) (Typing_print.error ty);
2621 env, (Reason.Rwitness p, Tany)
2623 and error_const_mutation env p (r, ty) =
2624 Errors.const_mutation p (Reason.to_pos r) (Typing_print.error ty);
2625 env, (Reason.Rwitness p, Tany)
2628 * Checks if a class (given by cty) contains a given static method.
2630 * We could refactor this + class_get
2632 and class_contains_smethod env cty (_pos, mid) =
2633 let lookup_member c =
2634 let class_ = Env.get_class env c in
2635 (match class_ with
2636 | None -> None
2637 | Some class_ ->
2638 Env.get_static_member true env class_ mid
2639 ) in
2640 match cty with
2641 | _, Tabstract (ak, tyopt) ->
2642 begin match TUtils.get_as_constraints env ak tyopt with
2643 | Some (_, Tclass ((_, c), _)) -> lookup_member c
2644 | _ -> None
2646 | _, Tclass ((_, c), _) -> lookup_member c
2647 | _, (Tany | Tmixed | Tarraykind _ | Toption _ | Tprim _
2648 | Tvar _ | Tfun _ | Ttuple _ | Tanon (_, _)
2649 | Tunresolved _ | Tobject | Tshape _)-> None
2651 and class_get ~is_method ~is_const ?(incl_tc=false) env cty (p, mid) cid =
2652 let env, this_ty =
2653 if is_method then
2654 this_for_method env cid cty
2655 else
2656 env, cty in
2657 let ety_env = {
2658 type_expansions = [];
2659 this_ty = this_ty;
2660 substs = SMap.empty;
2661 from_class = Some cid;
2662 } in
2663 class_get_ ~is_method ~is_const ~ety_env ~incl_tc env cid cty (p, mid)
2665 and class_get_ ~is_method ~is_const ~ety_env ?(incl_tc=false) env cid cty
2666 (p, mid) =
2667 let env, cty = Env.expand_type env cty in
2668 match cty with
2669 | _, Tany -> env, (Reason.Rnone, Tany)
2670 | _, Tunresolved tyl ->
2671 let env, tyl = List.map_env env tyl begin fun env ty ->
2672 class_get_ ~is_method ~is_const ~ety_env ~incl_tc env cid ty (p, mid)
2673 end in
2674 let env, method_ = TUtils.in_var env (fst cty, Tunresolved tyl) in
2675 env, method_
2676 | _, Tabstract (ak, tyopt) ->
2677 begin match TUtils.get_as_constraints env ak tyopt with
2678 | Some cty ->
2679 class_get_ ~is_method ~is_const ~ety_env ~incl_tc env cid cty (p, mid)
2680 | None ->
2681 env, (Reason.Rnone, Tany)
2683 | _, Tclass ((_, c), paraml) ->
2684 let class_ = Env.get_class env c in
2685 (match class_ with
2686 | None -> env, (Reason.Rnone, Tany)
2687 | Some class_ ->
2688 if !Typing_defs.accumulate_method_calls then
2689 Typing_defs.accumulate_method_calls_result :=
2690 (p, (class_.tc_name^"::"^mid)) ::
2691 !Typing_defs.accumulate_method_calls_result;
2692 Typing_hooks.dispatch_smethod_hook
2693 class_ (p, mid) env ety_env.from_class ~is_method ~is_const;
2694 let ety_env =
2695 { ety_env with
2696 substs = Subst.make class_.tc_tparams paraml } in
2697 if is_const then begin
2698 let const =
2699 if incl_tc then Env.get_const env class_ mid else
2700 match Env.get_typeconst env class_ mid with
2701 | Some _ ->
2702 Errors.illegal_typeconst_direct_access p;
2703 None
2704 | None ->
2705 Env.get_const env class_ mid
2707 match const with
2708 | None ->
2709 smember_not_found p ~is_const ~is_method class_ mid;
2710 env, (Reason.Rnone, Tany)
2711 | Some { cc_type; _ } ->
2712 let env, cc_type = Phase.localize ~ety_env env cc_type in
2713 env, cc_type
2714 end else begin
2715 let smethod = Env.get_static_member is_method env class_ mid in
2716 match smethod with
2717 | None ->
2718 (match Env.get_static_member is_method env class_ SN.Members.__callStatic with
2719 | None ->
2720 smember_not_found p ~is_const ~is_method class_ mid;
2721 env, (Reason.Rnone, Tany)
2722 | Some {ce_visibility = vis; ce_type = (r, Tfun ft); _} ->
2723 let p_vis = Reason.to_pos r in
2724 TVis.check_class_access p env (p_vis, vis) cid class_;
2725 let env, ft = Phase.localize_ft ~ety_env env ft in
2726 let ft = { ft with
2727 ft_arity = Fellipsis 0;
2728 ft_tparams = []; ft_params = [];
2729 } in
2730 env, (r, Tfun ft)
2731 | _ -> assert false)
2732 | Some { ce_visibility = vis; ce_type = method_; _ } ->
2733 let p_vis = Reason.to_pos (fst method_) in
2734 TVis.check_class_access p env (p_vis, vis) cid class_;
2735 let env, method_ =
2736 Phase.localize ~ety_env env method_ in
2737 env, method_
2740 | _, (Tmixed | Tarraykind _ | Toption _
2741 | Tprim _ | Tvar _ | Tfun _ | Ttuple _ | Tanon (_, _) | Tobject
2742 | Tshape _) ->
2743 (* should never happen; static_class_id takes care of these *)
2744 env, (Reason.Rnone, Tany)
2746 and smember_not_found pos ~is_const ~is_method class_ member_name =
2747 let kind =
2748 if is_const then `class_constant
2749 else if is_method then `static_method
2750 else `class_variable in
2751 let error hint =
2752 let cid = (class_.tc_pos, class_.tc_name) in
2753 Errors.smember_not_found kind pos cid member_name hint
2755 match Env.suggest_static_member is_method class_ member_name with
2756 | None ->
2757 (match Env.suggest_member is_method class_ member_name with
2758 | None when not class_.tc_members_fully_known ->
2759 (* no error in this case ... the member might be present
2760 * in one of the parents of class_ that the typing cannot see *)
2762 | None ->
2763 error `no_hint
2764 | Some (pos2, v) ->
2765 error (`closest (pos2, v))
2767 | Some (pos2, v) ->
2768 error (`did_you_mean (pos2, v))
2770 and member_not_found pos ~is_method class_ member_name r =
2771 let kind = if is_method then `method_ else `member in
2772 let cid = class_.tc_pos, class_.tc_name in
2773 let reason = Reason.to_string
2774 ("This is why I think it is an object of type "^strip_ns class_.tc_name) r
2776 let error hint =
2777 Errors.member_not_found kind pos cid member_name hint reason in
2778 match Env.suggest_member is_method class_ member_name with
2779 | None ->
2780 (match Env.suggest_static_member is_method class_ member_name with
2781 | None when not class_.tc_members_fully_known ->
2782 (* no error in this case ... the member might be present
2783 * in one of the parents of class_ that the typing cannot see *)
2785 | None ->
2786 error `no_hint
2787 | Some (def_pos, v) ->
2788 error (`closest (def_pos, v))
2790 | Some (def_pos, v) ->
2791 error (`did_you_mean (def_pos, v))
2793 (* The type of the object member is passed into the continuation k. This is
2794 * useful for typing nullsafed method calls. Consider `$x?->f()`: obj_get will
2795 * pass `k` the type of f, and `k` will typecheck the method call and return
2796 * the method's return type. obj_get then wraps that type in a Toption. *)
2797 and obj_get ~is_method ~nullsafe env ty1 cid id k =
2798 let env =
2799 match nullsafe with
2800 | Some p when not (type_could_be_null env ty1) ->
2801 let env, (r, _) = Env.expand_type env ty1 in
2802 Errors.nullsafe_not_needed p
2803 (Reason.to_string
2804 "This is what makes me believe it cannot be null" r);
2806 | _ -> env in
2807 let env, method_, _ =
2808 obj_get_with_visibility ~is_method ~nullsafe env ty1 cid id k in
2809 env, method_
2811 and obj_get_with_visibility ~is_method ~nullsafe env ty1
2812 cid id k =
2813 obj_get_ ~is_method ~nullsafe env ty1 cid id k (fun ty -> ty)
2815 (* k_lhs takes the type of the object receiver *)
2816 and obj_get_ ~is_method ~nullsafe env ty1 cid (p, s as id)
2817 k k_lhs =
2818 let env, ety1 = Env.expand_type env ty1 in
2819 let default_case () =
2820 k begin
2821 match snd ety1 with
2822 | Tclass (x, paraml) ->
2823 let class_ = Env.get_class env (snd x) in
2824 (match class_ with
2825 | None ->
2826 env, (Reason.Rnone, Tany), None
2827 | Some class_ when not is_method
2828 && not (Env.is_strict env)
2829 && class_.tc_name = SN.Classes.cStdClass ->
2830 env, (Reason.Rnone, Tany), None
2831 | Some class_ ->
2832 let paraml =
2833 if List.length paraml = 0
2834 then List.map class_.tc_tparams (fun _ -> Reason.Rwitness p, Tany)
2835 else paraml
2837 let member_ = Env.get_member is_method env class_ s in
2838 if !Typing_defs.accumulate_method_calls then
2839 Typing_defs.accumulate_method_calls_result :=
2840 (p, (class_.tc_name^"::"^s)) ::
2841 !Typing_defs.accumulate_method_calls_result;
2842 Typing_hooks.dispatch_cmethod_hook
2843 class_ (p, s) env None ~is_method;
2844 (match member_ with
2845 | None when not is_method ->
2846 if not (SN.Members.is_special_xhp_attribute s)
2847 then member_not_found p ~is_method class_ s (fst ety1);
2848 env, (Reason.Rnone, Tany), None
2849 | None ->
2850 (match Env.get_member is_method env class_ SN.Members.__call with
2851 | None ->
2852 member_not_found p ~is_method class_ s (fst ety1);
2853 env, (Reason.Rnone, Tany), None
2854 | Some {ce_visibility = vis; ce_type = (r, Tfun ft); _} ->
2855 let mem_pos = Reason.to_pos r in
2856 TVis.check_obj_access p env (mem_pos, vis);
2857 (* the return type of __call can depend on the
2858 * class params or be this *)
2859 let this_ty = k_lhs ety1 in
2860 let ety_env = {
2861 type_expansions = [];
2862 this_ty = this_ty;
2863 substs = Subst.make class_.tc_tparams paraml;
2864 from_class = Some cid;
2865 } in
2866 let env, ft = Phase.localize_ft ~ety_env env ft in
2868 (* we change the params of the underlying
2869 * declaration to act as a variadic function
2870 * ... this transform cannot be done when
2871 * processing the declaration of call because
2872 * direct calls to $inst->__call are also
2873 * valid. *)
2874 let ft = {ft with
2875 ft_arity = Fellipsis 0;
2876 ft_tparams = []; ft_params = [];
2877 } in
2878 let member_ = (r, Tfun ft) in
2879 env, member_, Some (mem_pos, vis)
2880 | _ -> assert false
2882 | Some ({ce_visibility = vis; ce_type = member_; _ } as member_ce) ->
2883 let mem_pos = Reason.to_pos (fst member_) in
2884 TVis.check_obj_access p env (mem_pos, vis);
2885 let member_ = Typing_enum.member_type env member_ce in
2886 let this_ty = k_lhs ety1 in
2887 let ety_env = {
2888 type_expansions = [];
2889 this_ty = this_ty;
2890 substs = Subst.make class_.tc_tparams paraml;
2891 from_class = Some cid;
2892 } in
2893 let env, member_ = Phase.localize ~ety_env env member_ in
2894 env, member_, Some (mem_pos, vis)
2897 | Tobject
2898 | Tany -> env, (fst ety1, Tany), None
2899 | (Tmixed | Tarraykind _ | Tprim _ | Toption _
2900 | Tvar _ | Tabstract (_, _) | Ttuple _ | Tanon (_, _)
2901 | Tfun _ | Tunresolved _ | Tshape _) as ty ->
2902 Errors.non_object_member
2903 s p (Typing_print.error ty) (Reason.to_pos (fst ety1));
2904 env, (fst ety1, Tany), None
2905 end in
2906 match ety1 with
2907 | _, Tunresolved tyl ->
2908 let (env, vis), tyl = List.map_env (env, None) tyl
2909 begin fun (env, vis) ty ->
2910 let env, ty, vis' =
2911 obj_get_ ~is_method ~nullsafe env ty cid id k k_lhs in
2912 (* There is one special case where we need to expose the
2913 * visibility outside of obj_get (checkout inst_meth special
2914 * function).
2915 * We keep a witness of the "most restrictive" visibility
2916 * we encountered (position + visibility), to be able to
2917 * special case inst_meth.
2919 let vis = TVis.min_vis_opt vis vis' in
2920 (env, vis), ty
2921 end in
2922 let env, method_ = TUtils.in_var env (fst ety1, Tunresolved (tyl)) in
2923 env, method_, vis
2924 | p', (Tabstract (ak, tyopt)) ->
2925 begin match TUtils.get_as_constraints env ak tyopt with
2926 | Some ty ->
2927 (* We probably don't want to rewrap new types for the 'this' closure *)
2928 (* TODO AKENN: we shouldn't refine constraints by changing
2929 * the type like this *)
2930 let k_lhs' ty = match ak with
2931 | AKnewtype (_, _) -> k_lhs ty
2932 | _ -> k_lhs (p', Tabstract (ak, Some ty)) in
2933 obj_get_ ~is_method ~nullsafe env ty cid id k k_lhs'
2934 | None -> default_case ()
2936 | _, Toption ty -> begin match nullsafe with
2937 | Some p1 ->
2938 let k' (env, fty, x) = begin
2939 let env, method_, x = k (env, fty, x) in
2940 let env, method_ = non_null env method_ in
2941 env, (Reason.Rnullsafe_op p1, Toption method_), x
2942 end in
2943 obj_get_ ~is_method ~nullsafe env ty cid id k' k_lhs
2944 | None ->
2945 Errors.null_member s p
2946 (Reason.to_string
2947 "This is what makes me believe it can be null"
2948 (fst ety1)
2950 k (env, (fst ety1, Tany), None)
2952 | _, (Tany | Tmixed | Tarraykind _ | Tprim _ | Tvar _
2953 | Tfun _ | Tclass (_, _) | Ttuple _ | Tanon (_, _)
2954 | Tobject | Tshape _) -> default_case ()
2956 (* Return true if the type ty1 contains the null value *)
2957 and type_could_be_null env ty1 =
2958 let env, ety1 = Env.expand_type env ty1 in
2959 match (snd ety1) with
2960 | Tabstract (ak, tyopt) ->
2961 begin match TUtils.get_as_constraints env ak tyopt with
2962 | None -> false
2963 | Some ty -> type_could_be_null env ty
2965 | Toption _ | Tunresolved _ | Tmixed | Tany -> true
2966 | Tarraykind _ | Tprim _ | Tvar _ | Tfun _
2967 | Tclass (_, _) | Ttuple _ | Tanon (_, _) | Tobject
2968 | Tshape _ -> false
2970 and class_id_for_new p env cid =
2971 let env, ty = static_class_id p env cid in
2972 (* Instantiation on an abstract class (e.g. from classname<T>) is via the
2973 * base type (to check constructor args), but the actual type `ty` must be
2974 * preserved. *)
2975 match TUtils.get_base_type env ty with
2976 | _, Tclass (sid, _) ->
2977 let class_ = Env.get_class env (snd sid) in
2978 env, (match class_ with
2979 | None -> None
2980 | Some class_ -> Some (sid, class_, ty)
2982 | _, (Tany | Tmixed | Tarraykind _ | Toption _ | Tprim _
2983 | Tvar _ | Tfun _ | Tabstract (_, _) | Ttuple _ | Tanon (_, _)
2984 | Tunresolved _ | Tobject | Tshape _) -> env, None
2986 (* To be a valid trait declaration, all of its 'require extends' must
2987 * match; since there's no multiple inheritance, it follows that all of
2988 * the 'require extends' must belong to the same inheritance hierarchy
2989 * and one of them should be the child of all the others *)
2990 and trait_most_concrete_req_class trait env =
2991 List.fold_left trait.tc_req_ancestors ~f:begin fun acc (_p, ty) ->
2992 let _r, (_p, name), _paraml = TUtils.unwrap_class_type ty in
2993 let keep = match acc with
2994 | Some (c, _ty) -> SMap.mem name c.tc_ancestors
2995 | None -> false
2997 if keep then acc
2998 else
2999 let class_ = Env.get_class env name in
3000 (match class_ with
3001 | None
3002 | Some { tc_kind = Ast.Cinterface; _ } -> acc
3003 | Some { tc_kind = Ast.Ctrait; _ } ->
3004 (* this is an error case for which the nastCheck spits out
3005 * an error, but does *not* currently remove the offending
3006 * 'require extends' or 'require implements' *)
3008 | Some c -> Some (c, ty)
3010 end ~init:None
3012 (* When invoking a method the class_id is used to determine what class we
3013 * lookup the method in, but the type of 'this' will be the late bound type.
3014 * For example:
3016 * class C {
3017 * public static function get(): this { return new static(); }
3019 * public static function alias(): this { return self::get(); }
3022 * In C::alias, when we invoke self::get(), 'self' is resolved to the class
3023 * in the lexical scope (C), so call C::get. However the method is executed in
3024 * the current context, so static inside C::get will be resolved to the late
3025 * bound type (get_called_class() within C::alias).
3027 * This means when determining the type of this, CIparent and CIself should be
3028 * changed to CIstatic. For the other cases of C::get() or $c::get(), we only
3029 * look at the left hand side of the '::' and use the type type associated
3030 * with it.
3032 * Thus C::get() will return a type C, while $c::get() will return the same
3033 * type as $c.
3035 and this_for_method env cid default_ty = match cid with
3036 | CIparent | CIself | CIstatic ->
3037 let p = Reason.to_pos (fst default_ty) in
3038 let env, ty = static_class_id p env CIstatic in
3039 env, ExprDepTy.make env CIstatic ty
3040 | _ ->
3041 env, default_ty
3043 and static_class_id p env = function
3044 | CIparent ->
3045 (match Env.get_self env with
3046 | _, Tclass ((_, self), _) ->
3047 (match Env.get_class env self with
3048 | Some (
3049 {tc_kind = Ast.Ctrait; _}
3050 as trait) ->
3051 (match trait_most_concrete_req_class trait env with
3052 | None ->
3053 Errors.parent_in_trait p;
3054 env, (Reason.Rwitness p, Tany)
3055 | Some (_, parent_ty) ->
3056 (* inside a trait, parent is SN.Typehints.this, but with the
3057 * type of the most concrete class that the trait has
3058 * "require extend"-ed *)
3059 let r = Reason.Rwitness p in
3060 let env, parent_ty = Phase.localize_with_self env parent_ty in
3061 env, (r, TUtils.this_of parent_ty)
3063 | _ ->
3064 let parent = Env.get_parent env in
3065 let parent_defined = snd parent <> Tany in
3066 if not parent_defined
3067 then Errors.parent_undefined p;
3068 let r = Reason.Rwitness p in
3069 let env, parent = Phase.localize_with_self env parent in
3070 (* parent is still technically the same object. *)
3071 env, (r, TUtils.this_of (r, snd parent))
3073 | _, (Tany | Tmixed | Tarraykind _ | Toption _ | Tprim _
3074 | Tfun _ | Ttuple _ | Tshape _ | Tvar _
3075 | Tanon (_, _) | Tunresolved _ | Tabstract (_, _) | Tobject
3076 ) ->
3077 let parent = Env.get_parent env in
3078 let parent_defined = snd parent <> Tany in
3079 if not parent_defined
3080 then Errors.parent_undefined p;
3081 let r = Reason.Rwitness p in
3082 let env, parent = Phase.localize_with_self env parent in
3083 (* parent is still technically the same object. *)
3084 env, (r, TUtils.this_of (r, snd parent))
3086 | CIstatic ->
3087 env, (Reason.Rwitness p, TUtils.this_of (Env.get_self env))
3088 | CIself ->
3089 env, (Reason.Rwitness p, snd (Env.get_self env))
3090 | CI c ->
3091 let class_ = Env.get_class env (snd c) in
3092 (match class_ with
3093 | None -> env, (Reason.Rnone, Tany) (* Tobject *)
3094 | Some class_ ->
3095 let env, params = List.map_env env class_.tc_tparams begin fun env _ ->
3096 Env.fresh_unresolved_type env
3097 end in
3098 env, (Reason.Rwitness (fst c), Tclass (c, params))
3100 | CIexpr (p, _ as e) ->
3101 let env, ty = expr env e in
3102 let rec resolve_ety ty =
3103 let env, ty = TUtils.fold_unresolved env ty in
3104 let _, ty = Env.expand_type env ty in
3105 match TUtils.get_base_type env ty with
3106 | _, Tabstract (AKnewtype (classname, [the_cls]), _) when
3107 classname = SN.Classes.cClassname -> resolve_ety the_cls
3108 | _, Tabstract (AKgeneric _, _)
3109 | _, Tclass _ -> ty
3110 | r, Tunresolved tyl -> r, Tunresolved (List.map tyl resolve_ety)
3111 | _, Tvar _ as ty -> resolve_ety ty
3112 | _, (Tany | Tprim Tstring | Tabstract (_, None) | Tmixed | Tobject)
3113 when not (Env.is_strict env) ->
3114 Reason.Rnone, Tany
3115 | _, (Tany | Tmixed | Tarraykind _ | Toption _
3116 | Tprim _ | Tfun _ | Ttuple _
3117 | Tabstract ((AKenum _ | AKdependent _ | AKnewtype _), _)
3118 | Tanon (_, _) | Tobject | Tshape _ as ty
3119 ) ->
3120 Errors.expected_class ~suffix:(", but got "^Typing_print.error ty) p;
3121 Reason.Rnone, Tany
3122 in env, resolve_ety ty
3124 and call_construct p env class_ params el uel cid =
3125 let cstr = Env.get_construct env class_ in
3126 let mode = Env.get_mode env in
3127 Typing_hooks.dispatch_constructor_hook class_ env p;
3128 match (fst cstr) with
3129 | None ->
3130 if el <> [] &&
3131 (mode = FileInfo.Mstrict || mode = FileInfo.Mpartial) &&
3132 class_.tc_members_fully_known
3133 then Errors.constructor_no_args p;
3134 fst (List.map_env env el expr)
3135 | Some { ce_visibility = vis; ce_type = m; _ } ->
3136 TVis.check_obj_access p env (Reason.to_pos (fst m), vis);
3137 let cid = if cid = CIparent then CIstatic else cid in
3138 let env, cid_ty = static_class_id p env cid in
3139 let ety_env = {
3140 type_expansions = [];
3141 this_ty = cid_ty;
3142 substs = Subst.make class_.tc_tparams params;
3143 from_class = Some cid;
3144 } in
3145 let env, m = Phase.localize ~ety_env env m in
3146 fst (call p env m el uel)
3148 and check_arity ?(check_min=true) pos pos_def (arity:int) exp_arity =
3149 let exp_min = (Typing_defs.arity_min exp_arity) in
3150 if check_min && arity < exp_min then
3151 Errors.typing_too_few_args pos pos_def;
3152 match exp_arity with
3153 | Fstandard (_, exp_max) ->
3154 if (arity > exp_max)
3155 then Errors.typing_too_many_args pos pos_def;
3156 | Fvariadic _ | Fellipsis _ -> ()
3158 and check_deprecated p { ft_pos; ft_deprecated; _ } =
3159 match ft_deprecated with
3160 | Some s -> Errors.deprecated_use p ft_pos s
3161 | None -> ()
3163 (* The variadic capture argument is an array listing the passed
3164 * variable arguments for the purposes of the function body; callsites
3165 * should not unify with it *)
3166 and variadic_param env ft =
3167 match ft.ft_arity with
3168 | Fvariadic (_, p_ty) -> env, Some p_ty
3169 | Fellipsis _ | Fstandard _ -> env, None
3171 and call pos env fty el uel =
3172 let env, ty = call_ pos env fty el uel in
3173 (* We need to solve the constraints after every single function call.
3174 * The type-checker is control-flow sensitive, the same value could
3175 * have different type depending on the branch that we are in.
3176 * When this is the case, a call could violate one of the constraints
3177 * in a branch. *)
3178 let env = fold_fun_list env env.Env.todo in
3179 env, ty
3181 (* Enforces that e is unpackable. If e is a tuple, appends its unpacked types
3182 * into the e_tyl returned.
3184 and unpack_expr env e_tyl e =
3185 let env, ety = expr env e in
3186 (match ety with
3187 | _, Ttuple tyl ->
3188 (* Todo: Check that tuples are allowed - that is, disallow a tuple
3189 * unpacking after an array unpacking.
3191 let unpacked_e_tyl = List.map tyl (fun ty -> e, ty) in
3192 env, e_tyl @ unpacked_e_tyl, true
3193 | _ ->
3194 let pos = fst e in
3195 let unpack_r = Reason.Runpack_param pos in
3196 let container_ty = (unpack_r, Tclass ((pos, SN.Collections.cContainer),
3197 [unpack_r, Tany])) in
3198 let env = Type.sub_type pos Reason.URparam env container_ty ety in
3199 env, e_tyl, false
3202 (* Unpacks uel. If tuples are found, unpacked types are appended to the
3203 * e_tyl returned.
3205 and unpack_exprl env e_tyl uel =
3206 List.fold_left uel ~init:(env, e_tyl, false)
3207 ~f: begin fun (env, e_tyl, unpacked_tuple) e ->
3208 let env, e_tyl, is_tuple = unpack_expr env e_tyl e in
3209 (env, e_tyl, is_tuple || unpacked_tuple)
3212 and call_ pos env fty el uel =
3213 let env, efty = Env.expand_type env fty in
3214 (match efty with
3215 | _, (Tany | Tunresolved []) ->
3216 let el = el @ uel in
3217 let env, _ = List.map_env env el begin fun env elt ->
3218 let env, arg_ty = expr env elt in
3219 let arg_ty = check_valid_rvalue pos env arg_ty in
3220 env, arg_ty
3221 end in
3222 Typing_hooks.dispatch_fun_call_hooks [] (List.map (el @ uel) fst) env;
3223 env, (Reason.Rnone, Tany)
3224 | r, Tunresolved tyl ->
3225 let env, retl = List.map_env env tyl begin fun env ty ->
3226 call pos env ty el uel
3227 end in
3228 TUtils.in_var env (r, Tunresolved retl)
3229 | r2, Tfun ft ->
3230 (* Typing of format string functions. It is dependent on the arguments (el)
3231 * so it cannot be done earlier.
3233 let env, ft = Typing_exts.retype_magic_func env ft el in
3234 check_deprecated pos ft;
3235 let pos_def = Reason.to_pos r2 in
3236 let env, var_param = variadic_param env ft in
3237 let env, e_tyl = List.map_env env el begin fun env e ->
3238 let env, ty = expr env e in
3239 env, (e, ty)
3240 end in
3241 let env, e_tyl, unpacked_tuple = unpack_exprl env e_tyl uel in
3242 let arity = if unpacked_tuple
3243 then List.length e_tyl
3244 (* Each array unpacked corresponds with at least 1 param. *)
3245 else List.length el + List.length uel in
3246 (* If we unpacked an array, we don't check arity exactly. Since each
3247 * unpacked array consumes 1 or many parameters, it is nonsensical to say
3248 * that not enough args were passed in (so we don't do the min check).
3250 let () = check_arity ~check_min:(uel = [] || unpacked_tuple)
3251 pos pos_def arity ft.ft_arity in
3252 let todos = ref [] in
3253 let env = wfold_left_default (call_param todos) (env, var_param)
3254 ft.ft_params e_tyl in
3255 let env = fold_fun_list env !todos in
3256 Typing_hooks.dispatch_fun_call_hooks
3257 ft.ft_params (List.map (el @ uel) fst) env;
3258 env, ft.ft_ret
3259 | r2, Tanon (arity, id) when uel = [] ->
3260 let env, tyl = List.map_env env el expr in
3261 let anon = Env.get_anonymous env id in
3262 let fpos = Reason.to_pos r2 in
3263 (match anon with
3264 | None ->
3265 Errors.anonymous_recursive_call pos;
3266 env, (Reason.Rnone, Tany)
3267 | Some anon ->
3268 let () = check_arity pos fpos (List.length tyl) arity in
3269 let tyl = List.map tyl (fun x -> None, x) in
3270 anon env tyl)
3271 | _, Tarraykind _ when not (Env.is_strict env) ->
3272 (* Relaxing call_user_func to work with an array in partial mode *)
3273 env, (Reason.Rnone, Tany)
3274 | _, ty ->
3275 bad_call pos ty;
3276 env, (Reason.Rnone, Tany)
3279 and call_param todos env (name, x) ((pos, _ as e), arg_ty) =
3280 (match name with
3281 | None -> ()
3282 | Some name -> Typing_suggest.save_param name env x arg_ty
3284 let arg_ty = check_valid_rvalue pos env arg_ty in
3286 (* When checking params the type 'x' may be expression dependent. Since
3287 * we store the expression id in the local env for Lvar, we want to apply
3288 * it in this case.
3290 let dep_ty = match snd e with
3291 | Lvar _ -> ExprDepTy.make env (CIexpr e) arg_ty
3292 | _ -> arg_ty in
3293 (* We solve for Tanon types after all the other params because we want to
3294 * typecheck the lambda bodies with as much type information as possible. For
3295 * example, in array_map(fn, x), we might be able to use the type of x to
3296 * infer the type of fn, but if we call sub_type on fn first, we end up
3297 * typechecking its body without the benefit of knowing its full type. If
3298 * fn is typehinted but not x, we could use fn to infer the type of x, but
3299 * in practice the reverse situation is more likely. This rearrangement is
3300 * particularly useful since higher-order functions usually put fn before x.
3302 match arg_ty with
3303 | _, Tanon _ ->
3304 todos := (fun env ->
3305 Type.sub_type pos Reason.URparam env x arg_ty) :: !todos;
3307 | _, (Tany | Tmixed | Tarraykind _ | Toption _ | Tprim _
3308 | Tvar _ | Tfun _ | Tabstract (_, _) | Tclass (_, _) | Ttuple _
3309 | Tunresolved _ | Tobject | Tshape _) ->
3310 Type.sub_type pos Reason.URparam env x dep_ty
3312 and bad_call p ty =
3313 Errors.bad_call p (Typing_print.error ty)
3315 and unop p env uop ty =
3316 match uop with
3317 | Ast.Unot ->
3318 Async.enforce_not_awaitable env p ty;
3319 (* !$x (logical not) works with any type, so we just return Tbool *)
3320 env, (Reason.Rlogic_ret p, Tprim Tbool)
3321 | Ast.Utild ->
3322 (* ~$x (bitwise not) only works with int *)
3323 Type.unify p Reason.URnone env (Reason.Rarith p, Tprim Tint) ty
3324 | Ast.Uincr
3325 | Ast.Upincr
3326 | Ast.Updecr
3327 | Ast.Udecr
3328 | Ast.Uplus
3329 | Ast.Uminus ->
3330 (* math operators work with int or floats, so we call sub_type *)
3331 let env = Type.sub_type p Reason.URnone env (Reason.Rarith p, Tprim Tnum) ty in
3332 env, ty
3333 | Ast.Uref ->
3334 (* We basically just ignore references in non-strict files *)
3335 if Env.is_strict env then
3336 Errors.reference_expr p;
3337 env, ty
3339 and binop in_cond p env bop p1 ty1 p2 ty2 =
3340 let expand_num_type env p ty =
3341 let env, ty = TUtils.fold_unresolved env ty in
3342 let env = Type.sub_type p Reason.URnone env (Reason.Rarith p, Tprim Tnum) ty in
3343 let env, ety = Env.expand_type env ty in
3344 (env, ety) in
3345 match bop with
3346 | Ast.Plus ->
3347 let env, ty1 = TUtils.fold_unresolved env ty1 in
3348 let env, ty2 = TUtils.fold_unresolved env ty2 in
3349 let env, ety1 = Env.expand_type env ty1 in
3350 let env, ety2 = Env.expand_type env ty2 in
3351 (match ety1, ety2 with
3352 (* For array<V1>+array<V2> and array<K1,V1>+array<K2,V2>, allow
3353 * the addition to produce a supertype. (We could also handle
3354 * when they have mismatching annotations, but we get better error
3355 * messages if we just let those get unified in the next case. *)
3356 | (_, Tarraykind (AKmap _ as ak)), (_, Tarraykind (AKmap _))
3357 | (_, Tarraykind (AKvec _ as ak)), (_, Tarraykind (AKvec _)) ->
3358 let env, a_sup = Env.fresh_unresolved_type env in
3359 let env, b_sup = Env.fresh_unresolved_type env in
3360 let res_ty = Reason.Rarray_plus_ret p, Tarraykind (
3361 match ak with
3362 | AKvec _ -> AKvec a_sup
3363 | AKmap _ -> AKmap (a_sup, b_sup)
3364 | _ -> assert false
3365 ) in
3366 let env = Type.sub_type p1 Reason.URnone env res_ty ety1 in
3367 let env = Type.sub_type p2 Reason.URnone env res_ty ety2 in
3368 env, res_ty
3369 | (_, Tarraykind _), (_, Tarraykind (AKshape _)) ->
3370 let env, ty2 = Typing_arrays.downcast_aktypes env ty2 in
3371 binop in_cond p env bop p1 ty1 p2 ty2
3372 | (_, Tarraykind (AKshape _)), (_, Tarraykind _) ->
3373 let env, ty1 = Typing_arrays.downcast_aktypes env ty1 in
3374 binop in_cond p env bop p1 ty1 p2 ty2
3375 | (_, Tarraykind _), (_, Tarraykind _)
3376 | (_, Tany), (_, Tarraykind _)
3377 | (_, Tarraykind _), (_, Tany) ->
3378 let env, ty = Type.unify p Reason.URnone env ty1 ty2 in
3379 env, ty
3380 | (_, (Tany | Tmixed | Tarraykind _ | Toption _
3381 | Tprim _ | Tvar _ | Tfun _ | Tabstract (_, _) | Tclass (_, _)
3382 | Ttuple _ | Tanon (_, _) | Tunresolved _ | Tobject | Tshape _
3384 ), _ -> binop in_cond p env Ast.Minus p1 ty1 p2 ty2
3386 | Ast.Minus | Ast.Star ->
3387 let env, ty1 = TUtils.fold_unresolved env ty1 in
3388 let env, ty2 = TUtils.fold_unresolved env ty2 in
3389 let env = Type.sub_type p1 Reason.URnone env
3390 (Reason.Rarith p1, Tprim Tnum) ty1 in
3391 let env = Type.sub_type p2 Reason.URnone env
3392 (Reason.Rarith p2, Tprim Tnum) ty2 in
3393 let env, ety1 = Env.expand_type env ty1 in
3394 let env, ety2 = Env.expand_type env ty2 in
3395 (match ety1, ety2 with
3396 | (r, Tprim Tfloat), _ | _, (r, Tprim Tfloat) ->
3397 (* if either side is a float then float: 1.0 - 1 -> float *)
3398 env, (r, Tprim Tfloat)
3399 | (r, Tprim Tnum), _ | _, (r, Tprim Tnum) ->
3400 (* if either side is a num, then num: (3 / x) - 1 -> num *)
3401 env, (r, Tprim Tnum)
3402 | (_, Tprim Tint), (_, Tprim Tint) ->
3403 (* Both sides are integers, then integer: 1 - 1 -> int *)
3404 env, (Reason.Rarith_ret p, Tprim Tint)
3405 | (_, (Tany | Tmixed | Tarraykind _ | Toption _
3406 | Tprim _ | Tvar _ | Tfun _ | Tabstract (_, _) | Tclass (_, _)
3407 | Ttuple _ | Tanon (_, _) | Tunresolved _ | Tobject | Tshape _
3409 ), _->
3410 (* Either side is unknown, unknown *)
3411 (* TODO um, what? This seems very wrong, particularly where "newtype
3412 * as" is concerned.
3413 * This also causes issues with primitive constraints on generics.
3414 * See test/typecheck/generic_primitive_invariant.php as an example *)
3415 env, ety1)
3416 | Ast.Slash ->
3417 let env, ety1 = expand_num_type env p1 ty1 in
3418 let env, ety2 = expand_num_type env p2 ty2 in
3419 (match ety1, ety2 with
3420 | (r, Tprim Tfloat), _ | _, (r, Tprim Tfloat) -> env, (r, Tprim Tfloat)
3421 | (_, (Tany | Tmixed | Tarraykind _ | Toption _
3422 | Tprim _ | Tvar _ | Tfun _ | Tabstract (_, _) | Tclass (_, _)
3423 | Ttuple _ | Tanon (_, _) | Tunresolved _ | Tobject | Tshape _
3425 ), _ -> env, (Reason.Rret_div p, Tprim Tnum)
3427 | Ast.Starstar ->
3428 let env, ety1 = expand_num_type env p1 ty1 in
3429 let env, ety2 = expand_num_type env p2 ty2 in
3430 (match ety1, ety2 with
3431 | (r, Tprim Tfloat), _ | _, (r, Tprim Tfloat) -> env, (r, Tprim Tfloat)
3432 | (_, (Tany | Tmixed | Tarraykind _ | Toption _
3433 | Tprim _ | Tvar _ | Tfun _ | Tabstract (_, _) | Tclass (_, _)
3434 | Ttuple _ | Tanon (_, _) | Tunresolved _ | Tobject | Tshape _
3436 ), _ -> env, (Reason.Rarith_ret p, Tprim Tnum)
3438 | Ast.Percent ->
3439 let env = Type.sub_type p Reason.URnone env (Reason.Rarith p1, Tprim Tint) ty1 in
3440 let env = Type.sub_type p Reason.URnone env (Reason.Rarith p1, Tprim Tint) ty2 in
3441 env, (Reason.Rarith_ret p, Tprim Tint)
3442 | Ast.Xor ->
3443 let env, ty1 = TUtils.fold_unresolved env ty1 in
3444 let env, ty2 = TUtils.fold_unresolved env ty2 in
3445 let env, ety1 = Env.expand_type env ty1 in
3446 let env, ety2 = Env.expand_type env ty2 in
3447 (match ety1, ety2 with
3448 | (_, Tprim Tbool), _ | _, (_, Tprim Tbool) ->
3449 let env, _ = Type.unify p Reason.URnone env ty1 (Reason.Rlogic_ret p1, Tprim Tbool) in
3450 let env, _ = Type.unify p Reason.URnone env ty2 (Reason.Rlogic_ret p1, Tprim Tbool) in
3451 env, (Reason.Rlogic_ret p, Tprim Tbool)
3452 | (_, (Tany | Tmixed | Tarraykind _ | Toption _
3453 | Tprim _ | Tvar _ | Tfun _ | Tabstract (_, _) | Tclass (_, _)
3454 | Ttuple _ | Tanon (_, _) | Tunresolved _ | Tobject | Tshape _
3456 ), _ ->
3457 let env, _ = Type.unify p Reason.URnone env ty1 (Reason.Rarith p1, Tprim Tint) in
3458 let env, _ = Type.unify p Reason.URnone env ty2 (Reason.Rarith p1, Tprim Tint) in
3459 env, (Reason.Rarith_ret p, Tprim Tint)
3461 | Ast.Eqeq | Ast.Diff ->
3462 env, (Reason.Rcomp p, Tprim Tbool)
3463 | Ast.EQeqeq | Ast.Diff2 ->
3464 if not in_cond
3465 then Typing_equality_check.assert_nontrivial p bop env ty1 ty2;
3466 env, (Reason.Rcomp p, Tprim Tbool)
3467 | Ast.Lt | Ast.Lte | Ast.Gt | Ast.Gte ->
3468 let ty_num = (Reason.Rcomp p, Tprim Nast.Tnum) in
3469 let ty_string = (Reason.Rcomp p, Tprim Nast.Tstring) in
3470 let ty_datetime =
3471 (Reason.Rcomp p, Tclass ((p, SN.Classes.cDateTime), [])) in
3472 let both_sub ty =
3473 SubType.is_sub_type env ty ty1 && SubType.is_sub_type env ty ty2 in
3474 if both_sub ty_num || both_sub ty_string || both_sub ty_datetime
3475 then env, (Reason.Rcomp p, Tprim Tbool)
3476 else
3477 (* TODO this is questionable; PHP's semantics for conversions with "<"
3478 * are pretty crazy and we may want to just disallow this? *)
3479 let env, _ = Type.unify p Reason.URnone env ty1 ty2 in
3480 env, (Reason.Rcomp p, Tprim Tbool)
3481 | Ast.Dot ->
3482 let env = SubType.sub_string p1 env ty1 in
3483 let env = SubType.sub_string p2 env ty2 in
3484 env, (Reason.Rconcat_ret p, Tprim Tstring)
3485 | Ast.AMpamp
3486 | Ast.BArbar ->
3487 env, (Reason.Rlogic_ret p, Tprim Tbool)
3488 | Ast.Amp | Ast.Bar | Ast.Ltlt | Ast.Gtgt ->
3489 let env = Type.sub_type p Reason.URnone env (Reason.Rbitwise p1, Tprim Tint) ty1 in
3490 let env = Type.sub_type p Reason.URnone env (Reason.Rbitwise p2, Tprim Tint) ty2 in
3491 env, (Reason.Rbitwise_ret p, Tprim Tint)
3492 | Ast.Eq _ ->
3493 assert false
3495 and non_null env ty =
3496 let env, ty = Env.expand_type env ty in
3497 match ty with
3498 | _, Toption ty ->
3499 let env, ty = Env.expand_type env ty in
3500 (* When "??T" appears in the typing environment due to implicit
3501 * typing, the recursion here ensures that it's treated as
3502 * isomorphic to "?T"; that is, all nulls are created equal.
3504 non_null env ty
3505 | r, Tunresolved tyl ->
3506 let env, tyl = List.map_env env tyl non_null in
3507 (* We need to flatten the unresolved types, otherwise we could
3508 * end up with "Tunresolved[Tunresolved _]" which is not supposed
3509 * to happen.
3511 let tyl = List.fold_right tyl ~f:begin fun ty tyl ->
3512 match ty with
3513 | _, Tunresolved l -> l @ tyl
3514 | x -> x :: tyl
3515 end ~init:[] in
3516 env, (r, Tunresolved tyl)
3518 | r, Tabstract (ak, tyopt) ->
3519 begin match TUtils.get_as_constraints env ak tyopt with
3520 | Some ty -> let env, ty = non_null env ty in
3521 env, (r, Tabstract (ak, Some ty))
3522 | None -> env, ty
3524 | _, (Tany | Tmixed | Tarraykind _ | Tprim _ | Tvar _
3525 | Tclass (_, _) | Ttuple _ | Tanon (_, _) | Tfun _
3526 | Tobject | Tshape _) ->
3527 env, ty
3529 and condition_var_non_null env = function
3530 | _, Lvar (_, x)
3531 | _, Dollardollar (_, x) ->
3532 let env, x_ty = Env.get_local env x in
3533 let env, x_ty = Env.expand_type env x_ty in
3534 let env, x_ty = non_null env x_ty in
3535 Env.set_local env x x_ty
3536 | p, Class_get (cname, (_, member_name)) as e ->
3537 let env, ty = expr env e in
3538 let env, local = Env.FakeMembers.make_static p env cname member_name in
3539 let env = Env.set_local env local ty in
3540 let local = p, Lvar (p, local) in
3541 condition_var_non_null env local
3542 | p, Obj_get ((_, This | _, Lvar _ as obj),
3543 (_, Id (_, member_name)),
3544 _) as e ->
3545 let env, ty = expr env e in
3546 let env, local = Env.FakeMembers.make p env obj member_name in
3547 let env = Env.set_local env local ty in
3548 let local = p, Lvar (p, local) in
3549 condition_var_non_null env local
3550 | _ -> env
3552 and condition_isset env = function
3553 | _, Array_get (x, _) -> condition_isset env x
3554 | v -> condition_var_non_null env v
3557 * Build an environment for the true or false branch of
3558 * conditional statements.
3560 and condition env tparamet =
3561 let expr = raw_expr ~in_cond:true in function
3562 | _, Expr_list [] -> env
3563 | _, Expr_list [x] ->
3564 let env, _ = expr env x in
3565 condition env tparamet x
3566 | r, Expr_list (x::xs) ->
3567 let env, _ = expr env x in
3568 condition env tparamet (r, Expr_list xs)
3569 | _, Call (Cnormal, (_, Id (_, func)), [param], [])
3570 when SN.PseudoFunctions.isset = func && tparamet &&
3571 not (Env.is_strict env) ->
3572 condition_isset env param
3573 | _, Call (Cnormal, (_, Id (_, func)), [e], [])
3574 when not tparamet && SN.StdlibFunctions.is_null = func ->
3575 condition_var_non_null env e
3576 | r, Binop ((Ast.Eqeq | Ast.EQeqeq as bop),
3577 (_, Null), e)
3578 | r, Binop ((Ast.Eqeq | Ast.EQeqeq as bop),
3579 e, (_, Null)) when not tparamet ->
3580 let env, x_ty = expr env e in
3581 let env, x_ty = Env.expand_type env x_ty in
3582 let env =
3583 if bop == Ast.Eqeq then check_null_wtf env r x_ty else env in
3584 condition_var_non_null env e
3585 | (p, (Lvar _ | Obj_get _ | Class_get _) as e) ->
3586 let env, ty = expr env e in
3587 let env, ety = Env.expand_type env ty in
3588 (match ety with
3589 | _, Tarraykind (AKany | AKempty)
3590 | _, Tprim Tbool -> env
3591 | _, (Tany | Tmixed | Tarraykind _ | Toption _
3592 | Tprim _ | Tvar _ | Tfun _ | Tabstract (_, _) | Tclass (_, _)
3593 | Ttuple _ | Tanon (_, _) | Tunresolved _ | Tobject | Tshape _
3594 ) ->
3595 condition env (not tparamet) (p, Binop (Ast.Eqeq, e, (p, Null))))
3596 | r, Binop (Ast.Eq None, var, e) when tparamet ->
3597 let env, e_ty = expr env e in
3598 let env, e_ty = Env.expand_type env e_ty in
3599 let env = check_null_wtf env r e_ty in
3600 condition_var_non_null env var
3601 | p1, Binop (Ast.Eq None, (_, (Lvar _ | Obj_get _) as lv), (p2, _)) ->
3602 let env, _ = expr env (p1, Binop (Ast.Eq None, lv, (p2, Null))) in
3603 condition env tparamet lv
3604 | p, Binop ((Ast.Diff | Ast.Diff2 as op), e1, e2) ->
3605 let op = if op = Ast.Diff then Ast.Eqeq else Ast.EQeqeq in
3606 condition env (not tparamet) (p, Binop (op, e1, e2))
3607 | _, Binop (Ast.AMpamp, e1, e2) when tparamet ->
3608 let env = condition env true e1 in
3609 let env = condition env true e2 in
3611 | _, Binop (Ast.BArbar, e1, e2) when not tparamet ->
3612 let env = condition env false e1 in
3613 let env = condition env false e2 in
3615 | _, Call (Cnormal, (_, Id (_, f)), [lv], [])
3616 when tparamet && f = SN.StdlibFunctions.is_array ->
3617 is_array env lv
3618 | _, Call (Cnormal, (_, Id (_, f)), [lv], [])
3619 when tparamet && f = SN.StdlibFunctions.is_int ->
3620 is_type env lv Tint
3621 | _, Call (Cnormal, (_, Id (_, f)), [lv], [])
3622 when tparamet && f = SN.StdlibFunctions.is_bool ->
3623 is_type env lv Tbool
3624 | _, Call (Cnormal, (_, Id (_, f)), [lv], [])
3625 when tparamet && f = SN.StdlibFunctions.is_float ->
3626 is_type env lv Tfloat
3627 | _, Call (Cnormal, (_, Id (_, f)), [lv], [])
3628 when tparamet && f = SN.StdlibFunctions.is_string ->
3629 is_type env lv Tstring
3630 | _, Call (Cnormal, (_, Id (_, f)), [lv], [])
3631 when tparamet && f = SN.StdlibFunctions.is_resource ->
3632 is_type env lv Tresource
3633 | _, Unop (Ast.Unot, e) ->
3634 condition env (not tparamet) e
3635 | p, InstanceOf (ivar, cid) when tparamet && is_instance_var ivar ->
3636 let env, x_ty = expr env ivar in
3637 let env, x_ty = Env.expand_type env x_ty in
3638 let env, (ivar_pos, x) = get_instance_var env ivar in
3639 let env = Env.set_local env x x_ty in
3640 (* XXX the position p here is not really correct... it's the position
3641 * of the instanceof expression, not the class id. But we don't store
3642 * position data for the latter. *)
3643 let env, obj_ty = static_class_id p env cid in
3644 let rec resolve_obj env obj_ty =
3645 (* Expand so that we don't modify x *)
3646 let env, obj_ty = Env.expand_type env obj_ty in
3647 match obj_ty with
3648 | _, Tabstract (AKgeneric _, _) ->
3649 env, obj_ty
3650 | _, Tabstract (AKdependent (`this, []), Some (_, Tclass _)) ->
3651 let obj_ty =
3652 (* Technically instanceof static is not strong enough to prove
3653 * that a type is exactly the same as the late bound type.
3654 * For now we allow this lie to exist. To solve
3655 * this we either need to create a new type that means
3656 * subtype of static or provide a way of specifying exactly
3657 * the late bound type i.e. $x::class === static::class
3659 if cid = CIstatic then
3660 ExprDepTy.make env CIstatic obj_ty
3661 else
3662 obj_ty in
3663 env, obj_ty
3664 | _, Tabstract ((AKdependent _ | AKnewtype _), Some ty) ->
3665 resolve_obj env ty
3666 | _, Tclass ((_, cid as _c), _) ->
3667 let class_ = Env.get_class env cid in
3668 (match class_ with
3669 | None -> env, (Reason.Rwitness ivar_pos, Tobject)
3670 | Some _class ->
3671 if SubType.is_sub_type env obj_ty x_ty
3672 then
3673 (* If the right side of the `instanceof` object is
3674 * a super type of what we already knew. In this case,
3675 * since we already have a more specialized object, we
3676 * don't touch the original object. Check out the unit
3677 * test srecko.php if this is unclear.
3679 * Note that if x_ty is Tany, no amount of subtype
3680 * checking will be able to specify it
3681 * further. This is arguably desirable to maintain
3682 * the invariant that removing annotations gets rid
3683 * of typing errors in partial mode (See also
3684 * t3216948). *)
3685 env, x_ty
3686 else env, obj_ty
3688 | r, Tunresolved tyl ->
3689 let env, tyl = List.map_env env tyl resolve_obj in
3690 env, (r, Tunresolved tyl)
3691 | _, (Tany | Tmixed | Tarraykind _ | Tprim _ | Tvar _ | Tfun _
3692 | Tabstract ((AKenum _ | AKnewtype _ | AKdependent _), _)
3693 | Ttuple _ | Tanon (_, _) | Toption _ | Tobject | Tshape _) ->
3694 env, (Reason.Rwitness ivar_pos, Tobject)
3696 let env, x_ty = resolve_obj env obj_ty in
3697 Env.set_local env x x_ty
3698 | _, Binop ((Ast.Eqeq | Ast.EQeqeq), e, (_, Null))
3699 | _, Binop ((Ast.Eqeq | Ast.EQeqeq), (_, Null), e) ->
3700 let env, _ = expr env e in
3702 | e ->
3703 let env, _ = expr env e in
3706 and is_instance_var = function
3707 | _, (Lvar _ | This) -> true
3708 | _, Obj_get ((_, This), (_, Id _), _) -> true
3709 | _, Obj_get ((_, Lvar _), (_, Id _), _) -> true
3710 | _, Class_get (_, _) -> true
3711 | _ -> false
3713 and get_instance_var env = function
3714 | p, Class_get (cname, (_, member_name)) ->
3715 let env, local = Env.FakeMembers.make_static p env cname member_name in
3716 env, (p, local)
3717 | p, Obj_get ((_, This | _, Lvar _ as obj), (_, Id (_, member_name)), _) ->
3718 let env, local = Env.FakeMembers.make p env obj member_name in
3719 env, (p, local)
3720 | _, Lvar (p, x) -> env, (p, x)
3721 | p, This -> env, (p, this)
3722 | _ -> failwith "Should only be called when is_instance_var is true"
3724 and check_null_wtf env p ty =
3725 if not (Env.is_strict env) then env else
3726 let env, ty = TUtils.fold_unresolved env ty in
3727 let env, ety = Env.expand_type env ty in
3728 match ety with
3729 | _, Toption ty ->
3730 let env, ty = Env.expand_type env ty in
3731 (match ty with
3732 | _, Tmixed
3733 | _, Tany ->
3734 Errors.sketchy_null_check p
3735 | _, Tprim _ ->
3736 Errors.sketchy_null_check_primitive p
3737 | _, (Tarraykind _ | Toption _ | Tvar _ | Tfun _
3738 | Tabstract (_, _) | Tclass (_, _) | Ttuple _ | Tanon (_, _)
3739 | Tunresolved _ | Tobject | Tshape _ ) -> ());
3741 | _, (Tany | Tmixed | Tarraykind _ | Tprim _ | Tvar _
3742 | Tfun _ | Tabstract (_, _) | Tclass (_, _) | Ttuple _ | Tanon (_, _)
3743 | Tunresolved _ | Tobject | Tshape _ ) -> env
3745 and is_type env e tprim =
3746 match e with
3747 | p, Class_get (cname, (_, member_name)) ->
3748 let env, local = Env.FakeMembers.make_static p env cname member_name in
3749 Env.set_local env local (Reason.Rwitness p, Tprim tprim)
3750 | p, Obj_get ((_, This | _, Lvar _ as obj), (_, Id (_, member_name)), _) ->
3751 let env, local = Env.FakeMembers.make p env obj member_name in
3752 Env.set_local env local (Reason.Rwitness p, Tprim tprim)
3753 | _, Lvar (p, x) ->
3754 Env.set_local env x (Reason.Rwitness p, Tprim tprim)
3755 | _ -> env
3757 and is_array env = function
3758 | p, Class_get (cname, (_, member_name)) ->
3759 let env, local = Env.FakeMembers.make_static p env cname member_name in
3760 Env.set_local env local (Reason.Rwitness p, Tarraykind AKany)
3761 | p, Obj_get ((_, This | _, Lvar _ as obj), (_, Id (_, member_name)), _) ->
3762 let env, local = Env.FakeMembers.make p env obj member_name in
3763 Env.set_local env local (Reason.Rwitness p, Tarraykind AKany)
3764 | _, Lvar (p, x) ->
3765 Env.set_local env x (Reason.Rwitness p, Tarraykind AKany)
3766 | _ -> env
3768 and string2 env idl =
3769 List.fold_left idl ~init:env ~f:begin fun env x ->
3770 let env, ty = expr env x in
3771 let p = fst x in
3772 let env = SubType.sub_string p env ty in
3776 (* If the current class inherits from classes that take type arguments, we need
3777 * to check that the arguments provided are consistent with the constraints on
3778 * the type parameters. *)
3779 and check_implements_tparaml (env: Env.env) ht =
3780 let _r, (p, c), paraml = TUtils.unwrap_class_type ht in
3781 let class_ = Env.get_class_dep env c in
3782 match class_ with
3783 | None ->
3784 (* The class lives in PHP land *)
3786 | Some class_ ->
3787 let size1 = List.length class_.tc_tparams in
3788 let size2 = List.length paraml in
3789 if size1 <> size2 then Errors.class_arity p class_.tc_pos c size1;
3790 let subst = Inst.make_subst class_.tc_tparams paraml in
3791 iter2_shortest begin fun (_, (p, _), cstrl) ty ->
3792 List.iter cstrl begin fun (ck, cstr) ->
3793 (* Constraint might contain uses of generic type parameters *)
3794 let cstr = Inst.instantiate subst cstr in
3795 match ck with
3796 | Ast.Constraint_as ->
3797 ignore (Type.sub_type_decl p Reason.URnone env cstr ty)
3798 | Ast.Constraint_super ->
3799 ignore (Type.sub_type_decl p Reason.URnone env ty cstr)
3801 end class_.tc_tparams paraml
3803 (* In order to type-check a class, we need to know what "parent"
3804 * refers to. Sometimes people write "parent::", when that happens,
3805 * we need to know the type of parent.
3807 and class_def_parent env class_def class_type =
3808 match class_def.c_extends with
3809 | (_, Happly ((_, x), _) as parent_ty) :: _ ->
3810 let parent_type = Env.get_class_dep env x in
3811 (match parent_type with
3812 | Some parent_type -> check_parent class_def class_type parent_type
3813 | None -> ());
3814 let parent_ty = Decl_hint.hint env.Env.decl_env parent_ty in
3815 env, Some x, parent_ty
3816 (* The only case where we have more than one parent class is when
3817 * dealing with interfaces and interfaces cannot use parent.
3819 | _ :: _
3820 | _ -> env, None, (Reason.Rnone, Tany)
3822 and check_parent class_def class_type parent_type =
3823 let position = fst class_def.c_name in
3824 (* Are all the parents in Hack? Do we know all their methods?
3825 * If so, let's check that the abstract methods have been implemented.
3827 if class_type.tc_members_fully_known
3828 then check_parent_abstract position parent_type class_type;
3829 if parent_type.tc_final
3830 then Errors.extend_final position parent_type.tc_pos parent_type.tc_name
3831 else ()
3833 and check_parent_abstract position parent_type class_type =
3834 let is_final = class_type.tc_final in
3835 if parent_type.tc_kind = Ast.Cabstract &&
3836 (class_type.tc_kind <> Ast.Cabstract || is_final)
3837 then begin
3838 check_extend_abstract_meth ~is_final position class_type.tc_methods;
3839 check_extend_abstract_meth ~is_final position class_type.tc_smethods;
3840 check_extend_abstract_const ~is_final position class_type.tc_consts;
3841 check_extend_abstract_typeconst
3842 ~is_final position class_type.tc_typeconsts;
3843 end else ()
3845 and class_def tcopt c =
3846 let filename = Pos.filename (fst c.Nast.c_name) in
3847 let dep = Dep.Class (snd c.c_name) in
3848 let env = Env.empty tcopt filename (Some dep) in
3849 let c = TNBody.class_meth_bodies tcopt c in
3850 if not !auto_complete then begin
3851 NastCheck.class_ env c;
3852 NastInitCheck.class_ env c;
3853 end;
3854 let tc = Env.get_class env (snd c.c_name) in
3855 match tc with
3856 | None ->
3857 (* This can happen if there was an error during the declaration
3858 * of the class. *)
3860 | Some tc ->
3861 Typing_requirements.check_class env tc;
3862 class_def_ env c tc
3864 (* Given a class definition construct a type consisting of the
3865 * class instantiated at its generic parameters.
3866 * TODO AKENN: remove the need for embedded constraints here. *)
3867 and get_self_from_c env c =
3868 let tparams = List.map (fst c.c_tparams) begin fun (_, (p, s), cstr) ->
3869 let cstr = List.map cstr
3870 begin fun (ck, h) -> (ck, Decl_hint.hint env.Env.decl_env h) end in
3871 Reason.Rwitness p, Tgeneric (s, cstr)
3872 end in
3873 let ret = Reason.Rwitness (fst c.c_name), Tapply (c.c_name, tparams) in
3876 and class_def_ env c tc =
3877 Typing_hooks.dispatch_enter_class_def_hook c tc;
3878 let env = Env.set_mode env c.c_mode in
3879 let pc, _ = c.c_name in
3880 let impl = List.map
3881 (c.c_extends @ c.c_implements @ c.c_uses)
3882 (Decl_hint.hint env.Env.decl_env) in
3883 TI.check_tparams_instantiable env (fst c.c_tparams);
3884 let env = Phase.localize_generic_parameters_with_bounds env (fst c.c_tparams)
3885 ~ety_env:(Phase.env_with_self env) in
3886 Typing_variance.class_ (Env.get_options env) (snd c.c_name) tc impl;
3887 List.iter impl (check_implements_tparaml env);
3889 (* Set up self identifier and type *)
3890 let env = Env.set_self_id env (snd c.c_name) in
3891 let self = get_self_from_c env c in
3892 (* For enums, localize makes self:: into an abstract type, which we don't
3893 * want *)
3894 let env, self = match c.c_kind with
3895 | Ast.Cenum -> env, (fst self, Tclass (c.c_name, []))
3896 | Ast.Cinterface | Ast.Cabstract | Ast.Ctrait
3897 | Ast.Cnormal -> Phase.localize_with_self env self in
3898 let env = Env.set_self env self in
3900 let env, parent_id, parent = class_def_parent env c tc in
3901 let is_final = tc.tc_final in
3902 if (tc.tc_kind = Ast.Cnormal || is_final) && tc.tc_members_fully_known
3903 then begin
3904 check_extend_abstract_meth ~is_final pc tc.tc_methods;
3905 check_extend_abstract_meth ~is_final pc tc.tc_smethods;
3906 check_extend_abstract_const ~is_final pc tc.tc_consts;
3907 check_extend_abstract_typeconst ~is_final pc tc.tc_typeconsts;
3908 end;
3909 let env = Env.set_parent env parent in
3910 let env = match parent_id with
3911 | None -> env
3912 | Some parent_id -> Env.set_parent_id env parent_id in
3913 if tc.tc_final then begin
3914 match c.c_kind with
3915 | Ast.Cinterface -> Errors.interface_final (fst c.c_name)
3916 | Ast.Cabstract -> ()
3917 | Ast.Ctrait -> Errors.trait_final (fst c.c_name)
3918 | Ast.Cenum ->
3919 Errors.internal_error pc "The parser should not parse final on enums"
3920 | Ast.Cnormal -> ()
3921 end;
3922 List.iter impl (class_implements_type env c);
3923 List.iter c.c_vars (class_var_def env ~is_static:false c);
3924 List.iter c.c_methods (method_def env);
3925 List.iter c.c_typeconsts (typeconst_def env);
3926 let const_types = List.map c.c_consts (class_const_def env) in
3927 let env = Typing_enum.enum_class_check env tc c.c_consts const_types in
3928 class_constr_def env c;
3929 let env = Env.set_static env in
3930 List.iter c.c_static_vars (class_var_def env ~is_static:true c);
3931 List.iter c.c_static_methods (method_def env);
3932 Typing_hooks.dispatch_exit_class_def_hook c tc
3934 and check_extend_abstract_meth ~is_final p smap =
3935 SMap.iter begin fun x ce ->
3936 match ce.ce_type with
3937 | r, Tfun { ft_abstract = true; _ } ->
3938 Errors.implement_abstract ~is_final p (Reason.to_pos r) "method" x
3939 | _, (Tany | Tmixed | Tarray (_, _) | Tgeneric (_,_) | Toption _ | Tprim _
3940 | Tfun _ | Tapply (_, _) | Ttuple _ | Tshape _ | Taccess (_, _)
3941 | Tthis
3942 ) -> ()
3943 end smap
3945 (* Type constants must be bound to a concrete type for non-abstract classes.
3947 and check_extend_abstract_typeconst ~is_final p smap =
3948 SMap.iter begin fun x tc ->
3949 if tc.ttc_type = None then
3950 Errors.implement_abstract ~is_final p (fst tc.ttc_name) "type constant" x
3951 end smap
3953 and check_extend_abstract_const ~is_final p smap =
3954 SMap.iter begin fun x cc ->
3955 match cc.cc_type with
3956 | r, Tgeneric _ when not cc.cc_synthesized ->
3957 Errors.implement_abstract ~is_final p (Reason.to_pos r) "constant" x
3958 | _, (Tany | Tmixed | Tarray (_, _) | Toption _ | Tprim _ | Tfun _
3959 | Tapply (_, _) | Ttuple _ | Tshape _ | Taccess (_, _) | Tthis
3960 | Tgeneric _) -> ()
3961 end smap
3963 and typeconst_def env {
3964 c_tconst_name = (pos, _);
3965 c_tconst_constraint;
3966 c_tconst_type;
3968 let env, cstr = opt Phase.hint_locl env c_tconst_constraint in
3969 let env, ty = opt Phase.hint_locl env c_tconst_type in
3970 ignore(
3971 Option.map2 cstr ty ~f:(Type.sub_type pos Reason.URtypeconst_cstr env)
3974 and class_const_def env (h, id, e) =
3975 let env, ty =
3976 match h with
3977 | None -> env, Env.fresh_type()
3978 | Some h ->
3979 Phase.hint_locl env h
3981 match e with
3982 | Some e ->
3983 let env, ty' = expr env e in
3984 ignore (Type.sub_type (fst id) Reason.URhint env ty ty');
3986 | None -> ty
3988 and class_constr_def env c =
3989 match c.c_constructor with
3990 | None -> ()
3991 | Some m ->
3992 method_def env m
3994 and class_implements_type env c1 ctype2 =
3995 let env, params =
3996 List.map_env env (fst c1.c_tparams) begin fun env (_, (p, s), param) ->
3997 let param = List.map param begin fun (ck, h) ->
3998 let ty = Decl_hint.hint env.Env.decl_env h in
3999 (ck, ty) end in
4000 env, (Reason.Rwitness p, Tgeneric (s, param))
4001 end in
4002 let r = Reason.Rwitness (fst c1.c_name) in
4003 let ctype1 = r, Tapply (c1.c_name, params) in
4004 Typing_extends.check_implements env ctype2 ctype1;
4007 and class_var_def env ~is_static c cv =
4008 let env, ty =
4009 match cv.cv_expr with
4010 | None -> env, Env.fresh_type()
4011 | Some e -> expr env e in
4012 match cv.cv_type with
4013 | None when Env.is_strict env ->
4014 Errors.add_a_typehint (fst cv.cv_id)
4015 | None ->
4016 let pos, name = cv.cv_id in
4017 let name = if is_static then "$"^name else name in
4018 let var_type = Reason.Rwitness pos, Tany in
4019 (match cv.cv_expr with
4020 | None ->
4021 Typing_suggest.uninitialized_member (snd c.c_name) name env var_type ty;
4023 | Some _ ->
4024 Typing_suggest.save_member name env var_type ty;
4027 | Some (p, _ as cty) ->
4028 let env =
4029 (* If this is an XHP attribute and we're in strict mode,
4030 relax to partial mode to allow the use of the "array"
4031 annotation without specifying type parameters. Until
4032 recently HHVM did not allow "array" with type parameters
4033 in XHP attribute declarations, so this is a temporary
4034 hack to support existing code for now. *)
4035 (* Task #5815945: Get rid of this Hack *)
4036 if cv.cv_is_xhp && (Env.is_strict env)
4037 then Env.set_mode env FileInfo.Mpartial
4038 else env in
4039 let cty = TI.instantiable_hint env cty in
4040 let env, cty = Phase.localize_with_self env cty in
4041 let _ = Type.sub_type p Reason.URhint env cty ty in
4044 and method_def env m =
4045 (* reset the expression dependent display ids for each method body *)
4046 Reason.expr_display_id_map := IMap.empty;
4047 Typing_hooks.dispatch_enter_method_def_hook m;
4048 let env = { env with Env.lenv = Env.empty_local } in
4049 let env = Phase.localize_generic_parameters_with_bounds env m.m_tparams
4050 ~ety_env:({ (Phase.env_with_self env) with from_class = Some CIstatic; }) in
4051 TI.check_tparams_instantiable env m.m_tparams;
4052 let env = Env.set_local env this (Env.get_self env) in
4053 let env, ret = match m.m_ret with
4054 | None -> env, (Reason.Rwitness (fst m.m_name), Tany)
4055 | Some ret ->
4056 let ret = TI.instantiable_hint env ret in
4057 (* If a 'this' type appears it needs to be compatiable with the
4058 * late static type
4060 let ety_env =
4061 { (Phase.env_with_self env) with
4062 from_class = Some CIstatic } in
4063 Phase.localize ~ety_env env ret in
4064 let m_params = match m.m_variadic with
4065 | FVvariadicArg param -> param :: m.m_params
4066 | _ -> m.m_params
4068 TI.check_params_instantiable env m_params;
4069 let env, params = List.map_env env m_params make_param_local_ty in
4070 if Env.is_strict env then begin
4071 List.iter2_exn ~f:(check_param env) m_params params;
4072 end;
4073 if Attributes.mem SN.UserAttributes.uaMemoize m.m_user_attributes then
4074 List.iter2_exn ~f:(check_memoizable env) m_params params;
4075 let env = List.fold2_exn ~f:bind_param ~init:env params m_params in
4076 let nb = Nast.assert_named_body m.m_body in
4077 let env = fun_ ~abstract:m.m_abstract env ret (fst m.m_name) nb m.m_fun_kind in
4078 let env =
4079 List.fold_left (Env.get_todo env) ~f:(fun env f -> f env) ~init:env in
4080 match m.m_ret with
4081 | None when Env.is_strict env && snd m.m_name <> SN.Members.__destruct ->
4082 (* if we are in strict mode, the only case where we don't want to enforce
4083 * a return type is when the method is a destructor
4085 suggest_return env (fst m.m_name) ret
4086 | None
4087 | Some _ -> ();
4088 Typing_hooks.dispatch_exit_method_def_hook m
4090 and typedef_def typedef =
4091 let tid = (snd typedef.t_name) in
4092 let filename = Pos.filename (fst typedef.t_kind) in
4093 let dep = Typing_deps.Dep.Class tid in
4094 let env =
4095 Typing_env.empty TypecheckerOptions.permissive filename (Some dep) in
4096 (* Mode for typedefs themselves doesn't really matter right now, but
4097 * they can expand hints, so make it loose so that the typedef doesn't
4098 * fail. (The hint will get re-checked with the proper mode anyways.)
4099 * Ideally the typedef would carry the right mode with it, but it's a
4100 * slightly larger change than I want to deal with right now. *)
4101 let env = Typing_env.set_mode env FileInfo.Mdecl in
4102 let env = Phase.localize_generic_parameters_with_bounds env typedef.t_tparams
4103 ~ety_env:(Phase.env_with_self env) in
4104 NastCheck.typedef env typedef;
4105 let {
4106 t_name = t_pos, _;
4107 t_tparams = _;
4108 t_constraint = tcstr;
4109 t_kind = hint;
4110 t_user_attributes = _;
4111 t_vis = _;
4112 t_mode = _;
4113 } = typedef in
4114 let ty = TI.instantiable_hint env hint in
4115 let env, ty = Phase.localize_with_self env ty in
4116 begin match tcstr with
4117 | Some tcstr ->
4118 let cstr = TI.instantiable_hint env tcstr in
4119 let env, cstr = Phase.localize_with_self env cstr in
4120 ignore @@ Typing_ops.sub_type t_pos Reason.URnewtype_cstr env cstr ty
4121 | _ -> ()
4122 end;
4123 match hint with
4124 | pos, Hshape fdm ->
4125 ignore (check_shape_keys_validity env pos (ShapeMap.keys fdm))
4126 | _ -> ()
4128 (* Calls the method of a class, but allows the f callback to override the
4129 * return value type *)
4130 and overload_function p env class_id method_id el uel f =
4131 let env, ty = static_class_id p env class_id in
4132 let env, fty =
4133 class_get ~is_method:true ~is_const:false env ty method_id class_id in
4134 (* call the function as declared to validate arity and input types,
4135 but ignore the result and overwrite with custom one *)
4136 let (env, res), has_error = Errors.try_with_error
4137 (fun () -> call p env fty el uel, false)
4138 (fun () -> (env, (Reason.Rwitness p, Tany)), true) in
4139 (* if there are errors already stop here - going forward would
4140 * report them twice *)
4141 if has_error then env, res
4142 else f env fty res el
4144 and update_array_type p env e1 e2 valkind =
4145 let access_type = Typing_arrays.static_array_access env e2 in
4146 let type_mapper =
4147 Typing_arrays.update_array_type p access_type in
4148 match valkind with
4149 | `lvalue | `lvalue_subexpr ->
4150 let env, ty1 =
4151 raw_expr ~valkind:`lvalue_subexpr ~in_cond:false env e1 in
4152 let env, ty1 = type_mapper env ty1 in
4153 begin match e1 with
4154 | (_, Lvar (_, x)) ->
4155 (* type_mapper has updated the type in ty1 typevars, but we
4156 need to update the local variable type too *)
4157 set_valid_rvalue p env x ty1
4158 | _ -> env, ty1
4160 | _ ->
4161 expr env e1