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