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