Fix parent constructor call position in parent_construct_hook.
[hiphop-php.git] / hphp / hack / src / typing / typing.ml
blob769fb7884e91fdba8f44e9f5f7f53128273aab6c
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 Decl_defs
19 open Nast
20 open Typing_defs
21 open Utils
23 module TUtils = Typing_utils
24 module Reason = Typing_reason
25 module Inst = Decl_instantiate
26 module Type = Typing_ops
27 module Env = Typing_env
28 module LEnv = Typing_lenv
29 module Dep = Typing_deps.Dep
30 module Async = Typing_async
31 module SubType = Typing_subtype
32 module Unify = Typing_unify
33 module TGen = Typing_generic
34 module SN = Naming_special_names
35 module TAccess = Typing_taccess
36 module TI = Typing_instantiability
37 module TVis = Typing_visibility
38 module TNBody = Typing_naming_body
39 module TS = Typing_structure
40 module T = Tast
41 module Phase = Typing_phase
42 module Subst = Decl_subst
43 module ExprDepTy = Typing_dependent_type.ExprDepTy
44 module Conts = Typing_continuations
46 (*****************************************************************************)
47 (* Debugging *)
48 (*****************************************************************************)
50 (* A guess as to the last position we were typechecking, for use in debugging,
51 * such as figuring out what a runaway hh_server thread is doing. Updated
52 * only best-effort -- it's an approximation to point debugging in the right
53 * direction, nothing more. *)
54 let debug_last_pos = ref Pos.none
55 let debug_print_last_pos _ = print_endline (Pos.string (Pos.to_absolute
56 !debug_last_pos))
58 (****************************************************************************)
59 (* Hooks *)
60 (****************************************************************************)
62 let expr_hook = ref None
64 let with_expr_hook hook f = with_context
65 ~enter: (fun () -> expr_hook := Some hook)
66 ~exit: (fun () -> expr_hook := None)
67 ~do_: f
69 (*****************************************************************************)
70 (* Helpers *)
71 (*****************************************************************************)
73 let suggest env p ty =
74 let ty = Typing_expand.fully_expand env ty in
75 (match Typing_print.suggest ty with
76 | "..." -> Errors.expecting_type_hint p
77 | ty -> Errors.expecting_type_hint_suggest p ty
80 let suggest_return env p ty =
81 let ty = Typing_expand.fully_expand env ty in
82 (match Typing_print.suggest ty with
83 | "..." -> Errors.expecting_return_type_hint p
84 | ty -> Errors.expecting_return_type_hint_suggest p ty
87 let err_witness p = Reason.Rwitness p, Terr
88 let err_none = Reason.Rnone, Terr
90 let expr_error env r =
91 env, T.make_implicitly_typed_expr Pos.none T.Any, (r, Terr)
93 let expr_any env r =
94 env, T.make_implicitly_typed_expr Pos.none T.Any, (r, Tany)
96 let compare_field_kinds x y =
97 match x, y with
98 | Nast.AFvalue (p1, _), Nast.AFkvalue ((p2, _), _)
99 | Nast.AFkvalue ((p2, _), _), Nast.AFvalue (p1, _) ->
100 Errors.field_kinds p1 p2;
101 false
102 | _ ->
103 true
105 let check_consistent_fields x l =
106 List.for_all l (compare_field_kinds x)
108 let unbound_name env (pos, name) =
109 match Env.get_mode env with
110 | FileInfo.Mstrict ->
111 (Errors.unbound_name_typing pos name;
112 expr_error env Reason.Rnone)
114 | FileInfo.Mdecl | FileInfo.Mpartial | FileInfo.Mphp ->
115 expr_any env Reason.Rnone
117 (* Try running function on each concrete supertype in turn. Return all
118 * successful results
120 let try_over_concrete_supertypes env ty f =
121 let env, tyl = TUtils.get_concrete_supertypes env ty in
122 (* If there is just a single result then don't swallow errors *)
123 match tyl with
124 | [ty] -> [f env ty]
125 | _ ->
126 let rec iter_over_types env resl tyl =
127 match tyl with
128 [] -> resl
129 | ty::tyl ->
130 Errors.try_
131 (fun () -> iter_over_types env (f env ty::resl) tyl)
132 (fun _ -> iter_over_types env resl tyl) in
133 iter_over_types env [] tyl
135 (*****************************************************************************)
136 (* Handling function/method arguments *)
137 (*****************************************************************************)
139 let rec wfold_left_default f (env, def1) l1 l2 =
140 match l1, def1, l2 with
141 | _, _, [] -> env
142 | [], None, _ -> env
143 | [], Some d1, x2 :: rl2 ->
144 let env = f env d1 x2 in
145 wfold_left_default f (env, def1) [] rl2
146 | x1 :: rl1, _, x2 :: rl2 ->
147 let env = f env x1 x2 in
148 wfold_left_default f (env, def1) rl1 rl2
150 let rec check_memoizable env param ty =
151 let env, ty = Env.expand_type env ty in
152 let p = param.param_pos in
153 match ty with
154 | _, (Tprim (Tarraykey | Tbool | Tint | Tfloat | Tstring | Tnum)
155 | Tmixed | Tany | Terr) ->
157 | _, Tprim (Tvoid | Tresource | Tnoreturn) ->
158 let ty_str = Typing_print.error (snd ty) in
159 let msgl = Reason.to_string ("This is "^ty_str) (fst ty) in
160 Errors.invalid_memoized_param p msgl
161 | _, Toption ty ->
162 check_memoizable env param ty
163 | _, Tshape (_, fdm) ->
164 ShapeMap.iter begin fun name _ ->
165 match ShapeMap.get name fdm with
166 | Some { sft_ty; _ } -> check_memoizable env param sft_ty
167 | None ->
168 let ty_str = Typing_print.error (snd ty) in
169 let msgl = Reason.to_string ("This is "^ty_str) (fst ty) in
170 Errors.invalid_memoized_param p msgl;
171 end fdm
172 | _, Ttuple tyl ->
173 List.iter tyl begin fun ty ->
174 check_memoizable env param ty
176 | _, Tabstract (AKenum _, _) ->
178 | _, Tabstract (AKnewtype (_, _), _) ->
179 let env, t', _ =
180 let ety_env = Phase.env_with_self env in
181 Typing_tdef.force_expand_typedef ~ety_env env ty in
182 check_memoizable env param t'
183 (* Just accept all generic types for now. Stricter checks to come later. *)
184 | _, Tabstract (AKgeneric _, _) ->
186 (* For parameter type 'this::TID' defined by 'type const TID as Bar' checks
187 * Bar recursively.
189 | _, Tabstract (AKdependent _, Some ty) ->
190 check_memoizable env param ty
191 (* Allow unconstrined dependent type `abstract type const TID` just as we
192 * allow unconstrained generics. *)
193 | _, Tabstract (AKdependent _, None) ->
195 (* Handling Tunresolved case here for completeness, even though it
196 * shouldn't be possible to have an unresolved type when checking
197 * the method declaration. No corresponding test case for this.
199 | _, Tunresolved tyl ->
200 List.iter tyl begin fun ty ->
201 check_memoizable env param ty
203 (* Allow untyped arrays. *)
204 | _, Tarraykind AKany
205 | _, Tarraykind AKempty ->
207 | _, Tarraykind (AKvarray ty | AKvec ty | AKdarray(_, ty) | AKmap(_, ty)) ->
208 check_memoizable env param ty
209 | _, Tarraykind (AKshape fdm) ->
210 ShapeMap.iter begin fun _ (_, tv) ->
211 check_memoizable env param tv
212 end fdm
213 | _, Tarraykind (AKtuple fields) ->
214 IMap.iter begin fun _ tv ->
215 check_memoizable env param tv
216 end fields
217 | _, Tclass (_, _) ->
218 let type_param = Env.fresh_type() in
219 let container_type =
220 Reason.none,
221 Tclass ((Pos.none, SN.Collections.cContainer), [type_param]) in
222 let env, is_container =
223 Errors.try_
224 (fun () ->
225 SubType.sub_type env ty container_type, true)
226 (fun _ -> env, false) in
227 if is_container then
228 check_memoizable env param type_param
229 else
230 let r, _ = ty in
231 let memoizable_type =
232 r, Tclass ((Pos.none, SN.Classes.cIMemoizeParam), []) in
233 if SubType.is_sub_type env ty memoizable_type
234 then ()
235 else
236 let ty_str = Typing_print.error (snd ty) in
237 let msgl = Reason.to_string ("This is "^ty_str) (fst ty) in
238 Errors.invalid_memoized_param p msgl;
239 | _, Tfun _
240 | _, Tvar _
241 | _, Tanon (_, _)
242 | _, Tobject ->
243 let ty_str = Typing_print.error (snd ty) in
244 let msgl = Reason.to_string ("This is "^ty_str) (fst ty) in
245 Errors.invalid_memoized_param p msgl
247 (* This function is used to determine the type of an argument.
248 * When we want to type-check the body of a function, we need to
249 * introduce the type of the arguments of the function in the environment
250 * Let's take an example, we want to check the code of foo:
252 * function foo(int $x): int {
253 * // CALL TO make_param_type on (int $x)
254 * // Now we know that the type of $x is int
256 * return $x; // in the environment $x is an int, the code is correct
259 * When we localize, we want to resolve to "static" or "$this" depending on
260 * the context. Even though we are passing in CIstatic, resolve_with_class_id
261 * is smart enough to know what to do. Why do this? Consider the following
263 * abstract class C {
264 * abstract const type T;
266 * private this::T $val;
268 * final public function __construct(this::T $x) {
269 * $this->val = $x;
272 * public static function create(this::T $x): this {
273 * return new static($x);
277 * class D extends C { const type T = int; }
279 * In __construct() we want to be able to assign $x to $this->val. The type of
280 * $this->val will expand to '$this::T', so we need $x to also be '$this::T'.
281 * We can do this soundly because when we construct a new class such as,
282 * 'new D(0)' we can determine the late static bound type (D) and resolve
283 * 'this::T' to 'D::T' which is int.
285 * A similar line of reasoning is applied for the static method create.
287 let make_param_local_ty env param =
288 let ety_env =
289 { (Phase.env_with_self env) with from_class = Some CIstatic; } in
290 let env, ty =
291 match param.param_hint with
292 | None ->
293 (* if the type is missing, use an unbound type variable *)
294 let _r, ty = Env.fresh_type () in
295 let r = Reason.Rwitness param.param_pos in
296 env, (r, ty)
297 | Some x ->
298 let ty = Decl_hint.hint env.Env.decl_env x in
299 Phase.localize ~ety_env env ty
301 let ty = match ty with
302 | _, t when param.param_is_variadic ->
303 (* when checking the body of a function with a variadic
304 * argument, "f(C ...$args)", $args is an array<C> *)
305 let r = Reason.Rvar_param param.param_pos in
306 let arr_values = r, t in
307 r, Tarraykind (AKvec arr_values)
308 | x -> x
310 Typing_hooks.dispatch_infer_ty_hook ty param.param_pos env;
311 env, ty
313 (* Given a localized parameter type and parameter information, infer
314 * a type for the parameter default expression (if present) and check that
315 * it is a subtype of the parameter type. Set the type of the parameter in
316 * the locals environment *)
317 let rec bind_param env (ty1, param) =
318 let env, param_te, ty2 =
319 match param.param_expr with
320 | None ->
321 env, None, (Reason.none, Tany)
322 | Some e ->
323 let env, te, ty = expr env e in
324 Typing_sequencing.sequence_check_expr e;
325 env, Some te, ty
327 Typing_suggest.save_param (param.param_name) env ty1 ty2;
328 let env = Type.sub_type param.param_pos Reason.URhint env ty2 ty1 in
329 let tparam = {
330 T.param_hint = param.param_hint;
331 T.param_is_reference = param.param_is_reference;
332 T.param_is_variadic = param.param_is_variadic;
333 T.param_pos = param.param_pos;
334 T.param_name = param.param_name;
335 T.param_expr = param_te;
336 } in
337 Env.set_local env (Local_id.get param.param_name) ty1, tparam
339 (* In strict mode, we force you to give a type declaration on a parameter *)
340 (* But the type checker is nice: it makes a suggestion :-) *)
341 and check_param env param ty =
342 match param.param_hint with
343 | None -> suggest env param.param_pos ty
344 | Some _ -> ()
346 (*****************************************************************************)
347 (* Now we are actually checking stuff! *)
348 (*****************************************************************************)
349 and fun_def tcopt f =
350 (* reset the expression dependent display ids for each function body *)
351 Reason.expr_display_id_map := IMap.empty;
352 Typing_hooks.dispatch_enter_fun_def_hook f;
353 let nb = TNBody.func_body tcopt f in
354 let dep = Typing_deps.Dep.Fun (snd f.f_name) in
355 let env = Env.empty tcopt (Pos.filename (fst f.f_name)) (Some dep) in
356 NastCheck.fun_ env f nb;
357 (* Fresh type environment is actually unnecessary, but I prefer to
358 * have a guarantee that we are using a clean typing environment. *)
359 let tfun_def = Env.fresh_tenv env (
360 fun env ->
361 let env = Env.set_mode env f.f_mode in
362 let env, constraints =
363 Phase.localize_generic_parameters_with_bounds env f.f_tparams
364 ~ety_env:(Phase.env_with_self env) in
365 let env = add_constraints (fst f.f_name) env constraints in
367 let env, hret =
368 match f.f_ret with
369 | None -> env, (Reason.Rwitness (fst f.f_name), Tany)
370 | Some ret ->
371 let ty = TI.instantiable_hint env ret in
372 Phase.localize_with_self env ty
374 (* TODO TAST: convert default expression in param *)
375 let f_params = match f.f_variadic with
376 | FVvariadicArg param -> param :: f.f_params
377 | _ -> f.f_params
379 TI.check_params_instantiable env f_params;
380 TI.check_tparams_instantiable env f.f_tparams;
381 let env, param_tys = List.map_env env f_params make_param_local_ty in
382 let env, tparams = List.map_env env (List.zip_exn param_tys f_params)
383 bind_param in
384 let env, tb = fun_ env hret (fst f.f_name) nb f.f_fun_kind in
385 let env = fold_fun_list env env.Env.todo in
386 if Env.is_strict env then begin
387 List.iter2_exn f_params param_tys (check_param env);
388 match f.f_ret with
389 | None -> suggest_return env (fst f.f_name) hret
390 | Some _ -> ()
391 end;
393 T.f_mode = f.f_mode;
394 T.f_ret = f.f_ret;
395 T.f_name = f.f_name;
396 T.f_tparams = f.f_tparams;
397 T.f_variadic = T.FVnonVariadic (* TAST: get this right *);
398 T.f_params = tparams;
399 T.f_fun_kind = f.f_fun_kind;
400 T.f_user_attributes = List.map f.f_user_attributes (user_attribute env);
401 T.f_body = T.NamedBody {
402 T.fnb_nast = tb;
403 T.fnb_unsafe = false (* TAST get this right *)
406 ) in
407 Typing_hooks.dispatch_exit_fun_def_hook f;
408 tfun_def
410 (*****************************************************************************)
411 (* function used to type closures, functions and methods *)
412 (*****************************************************************************)
414 and fun_ ?(abstract=false) env hret pos named_body f_kind =
415 Env.with_return env begin fun env ->
416 debug_last_pos := pos;
417 let env = Env.set_return env hret in
418 let env = Env.set_fn_kind env f_kind in
419 let env, tb = block env named_body.fnb_nast in
420 Typing_sequencing.sequence_check_block named_body.fnb_nast;
421 let ret = Env.get_return env in
422 let env =
423 if Nast_terminality.Terminal.block env named_body.fnb_nast ||
424 abstract ||
425 named_body.fnb_unsafe ||
426 !auto_complete
427 then env
428 else fun_implicit_return env pos ret named_body.fnb_nast f_kind in
429 debug_last_pos := Pos.none;
430 env, tb
433 and fun_implicit_return env pos ret _b = function
434 | Ast.FGenerator | Ast.FAsyncGenerator -> env
435 | Ast.FSync ->
436 (* A function without a terminal block has an implicit return; the
437 * "void" type *)
438 let rty = Reason.Rno_return pos, Tprim Nast.Tvoid in
439 Typing_suggest.save_return env ret rty;
440 Type.sub_type pos Reason.URreturn env rty ret
441 | Ast.FAsync ->
442 (* An async function without a terminal block has an implicit return;
443 * the Awaitable<void> type *)
444 let r = Reason.Rno_return_async pos in
445 let rty = r, Tclass ((pos, SN.Classes.cAwaitable), [r, Tprim Nast.Tvoid]) in
446 Typing_suggest.save_return env ret rty;
447 Type.sub_type pos Reason.URreturn env rty ret
449 and block env stl =
450 List.map_env env stl stmt
452 and stmt env = function
453 | Fallthrough ->
454 env, T.Fallthrough
455 | Noop ->
456 env, T.Noop
457 | Expr e ->
458 let env, te, ty = expr env e in
459 (* NB: this check does belong here and not in expr, even though it only
460 * applies to expressions -- we actually want to perform the check on
461 * statements that are expressions, e.g., "foo();" we want to check, but
462 * "return foo();" we do not even though the expression "foo()" is a
463 * subexpression of the statement "return foo();". *)
464 (match snd e with
465 | Nast.Binop (Ast.Eq _, _, _) -> ()
466 | _ -> Async.enforce_not_awaitable env (fst e) ty);
467 env, T.Expr te
468 | If (e, b1, b2) ->
469 let env, te, _ = expr env e in
470 (* We stash away the locals environment because condition updates it
471 * locally for checking b1. For example, we might have condition
472 * $x === null, or $x instanceof C, which changes the type of $x in
473 * lenv *)
474 let parent_lenv = env.Env.lenv in
475 let env = condition env true e in
476 let env, tb2 = block env b1 in
477 let lenv1 = env.Env.lenv in
478 let env = { env with Env.lenv = parent_lenv } in
479 let env = condition env false e in
480 let env, tb1 = block env b2 in
481 let lenv2 = env.Env.lenv in
482 let terminal1 = Nast_terminality.Terminal.block env b1 in
483 let terminal2 = Nast_terminality.Terminal.block env b2 in
484 let env =
485 if terminal1 && terminal2
486 then
487 let env = LEnv.integrate env parent_lenv lenv1 in
488 let env = LEnv.integrate env env.Env.lenv lenv2 in
489 LEnv.integrate env env.Env.lenv parent_lenv
490 else if terminal1
491 then begin
492 let env = LEnv.integrate env parent_lenv lenv1 in
493 LEnv.integrate env env.Env.lenv lenv2
495 else if terminal2
496 then begin
497 let env = LEnv.integrate env parent_lenv lenv2 in
498 LEnv.integrate env env.Env.lenv lenv1
500 else LEnv.intersect env parent_lenv lenv1 lenv2 in
501 (* TODO TAST: annotate with joined types *)
502 env, T.If(te, tb1, tb2)
503 | Return (p, None) ->
504 let rty = match Env.get_fn_kind env with
505 | Ast.FSync -> (Reason.Rwitness p, Tprim Tvoid)
506 | Ast.FGenerator
507 (* Return type checked against the "yield". *)
508 | Ast.FAsyncGenerator -> (Reason.Rnone, Tany)
509 | Ast.FAsync -> (Reason.Rwitness p,
510 Tclass ((p, SN.Classes.cAwaitable),
511 [(Reason.Rwitness p, Tprim Tvoid)])) in
512 let expected_return = Env.get_return env in
513 Typing_suggest.save_return env expected_return rty;
514 let env = Type.sub_type p Reason.URreturn env rty expected_return in
515 env, T.Return (p, None)
516 | Return (p, Some e) ->
517 let pos = fst e in
518 let env, te, rty = expr env e in
519 let rty = match Env.get_fn_kind env with
520 | Ast.FSync -> rty
521 | Ast.FGenerator
522 (* Is an error, but caught in NastCheck. *)
523 | Ast.FAsyncGenerator -> (Reason.Rnone, Terr)
524 | Ast.FAsync ->
525 (Reason.Rwitness p), Tclass ((p, SN.Classes.cAwaitable), [rty]) in
526 let expected_return = Env.get_return env in
527 (match snd (Env.expand_type env expected_return) with
528 | r, Tprim Tvoid ->
529 (* Yell about returning a value from a void function. This catches
530 * more issues than just unifying with void would do -- in particular
531 * just unifying allows you to return a Tany from a void function,
532 * which is clearly wrong. Note this check is best-effort; if the
533 * function returns a generic type which later ends up being Tvoid
534 * then there's not much we can do here. *)
535 Errors.return_in_void p (Reason.to_pos r);
536 env, T.Return(p, Some te)
537 | _, Tunresolved _ ->
538 (* we allow return types to grow for anonymous functions *)
539 let env, rty = TUtils.unresolved env rty in
540 let env = Type.sub_type pos Reason.URreturn env rty expected_return in
541 env, T.Return(p, Some te)
542 | _, (Terr | Tany | Tmixed | Tarraykind _ | Toption _ | Tprim _
543 | Tvar _ | Tfun _ | Tabstract (_, _) | Tclass (_, _) | Ttuple _
544 | Tanon (_, _) | Tobject | Tshape _) ->
545 Typing_suggest.save_return env expected_return rty;
546 let env = Type.sub_type pos Reason.URreturn env rty expected_return in
547 env, T.Return(p, Some te)
549 | Do (b, e) as st ->
550 (* NOTE: leaks scope as currently implemented; this matches
551 the behavior in naming (cf. `do_stmt` in naming/naming.ml).
553 let parent_lenv = env.Env.lenv in
554 let env = Env.freeze_local_env env in
555 let env, _ = block env b in
556 let env, te, _ = expr env e in
557 let after_block = env.Env.lenv in
558 let alias_depth =
559 if env.Env.in_loop then 1 else Typing_alias.get_depth st in
560 let env, tb = Env.in_loop env begin
561 iter_n_acc alias_depth begin fun env ->
562 let env = condition env true e in
563 let env, tb = block env b in
564 env, tb
565 end end in
566 let env =
567 if Nast.Visitor.HasContinue.block b
568 then LEnv.fully_integrate env parent_lenv
569 else
570 let env = LEnv.integrate env parent_lenv env.Env.lenv in
571 let env = { env with Env.lenv = after_block } in
572 env in
573 let env = condition env false e in
574 env, T.Do(tb, te)
575 | While (e, b) as st ->
576 let env, te, _ = expr env e in
577 let parent_lenv = env.Env.lenv in
578 let env = Env.freeze_local_env env in
579 let alias_depth =
580 if env.Env.in_loop then 1 else Typing_alias.get_depth st in
581 let env, tb = Env.in_loop env begin
582 iter_n_acc alias_depth begin fun env ->
583 let env = condition env true e in
584 (* TODO TAST: avoid repeated generation of block *)
585 let env, tb = block env b in
586 env, tb
588 end in
589 let env = LEnv.fully_integrate env parent_lenv in
590 let env = condition env false e in
591 env, T.While (te, tb)
592 | For (e1, e2, e3, b) as st ->
593 (* For loops leak their initalizer, but nothing that's defined in the
594 body
596 let (env, te1, _) = expr env e1 in (* initializer *)
597 let (env, te2, _) = expr env e2 in
598 let parent_lenv = env.Env.lenv in
599 let env = Env.freeze_local_env env in
600 let alias_depth =
601 if env.Env.in_loop then 1 else Typing_alias.get_depth st in
602 let env, (tb, te3) = Env.in_loop env begin
603 iter_n_acc alias_depth begin fun env ->
604 let env = condition env true e2 in (* iteration 0 *)
605 let env, tb = block env b in
606 let (env, te3, _) = expr env e3 in
607 env, (tb, te3)
609 end in
610 let env = LEnv.fully_integrate env parent_lenv in
611 let env = condition env false e2 in
612 env, T.For(te1, te2, te3, tb)
613 | Switch (e, cl) ->
614 let cl = List.map ~f:drop_dead_code_after_break cl in
615 Nast_terminality.SafeCase.check (fst e) env cl;
616 let env, te, ty = expr env e in
617 Async.enforce_not_awaitable env (fst e) ty;
618 let env = check_exhaustiveness env (fst e) ty cl in
619 let parent_lenv = env.Env.lenv in
620 let env, cl, tcl = case_list parent_lenv ty env cl in
621 let env = LEnv.intersect_list env parent_lenv cl in
622 env, T.Switch(te, tcl)
623 | Foreach (e1, e2, b) as st ->
624 let env, te1, ty1 = expr env e1 in
625 let parent_lenv = env.Env.lenv in
626 let env = Env.freeze_local_env env in
627 let env, ty2 = as_expr env (fst e1) e2 in
628 let env = Type.sub_type (fst e1) Reason.URforeach env ty1 ty2 in
629 let alias_depth =
630 if env.Env.in_loop then 1 else Typing_alias.get_depth st in
631 let env, (te2, tb) = Env.in_loop env begin
632 iter_n_acc alias_depth begin fun env ->
633 let env, te2 = bind_as_expr env ty2 e2 in
634 let env, tb = block env b in
635 env, (te2, tb)
637 end in
638 let env = LEnv.fully_integrate env parent_lenv in
639 env, T.Foreach (te1, te2, tb)
640 | Try (tb, cl, fb) ->
641 let env, ttb, tcl = try_catch env tb cl in
642 let env, tfb = block env fb in
643 env, T.Try (ttb, tcl, tfb)
644 | Static_var el ->
645 let env = List.fold_left el ~f:begin fun env e ->
646 match e with
647 | _, Binop (Ast.Eq _, (_, Lvar (p, x)), _) ->
648 Env.add_todo env (TGen.no_generic p x)
649 | _ -> env
650 end ~init:env in
651 let env, tel, _ = exprs env el in
652 env, T.Static_var tel
653 | Throw (is_terminal, e) ->
654 let p = fst e in
655 let env, te, ty = expr env e in
656 let env = exception_ty p env ty in
657 env, T.Throw(is_terminal, te)
658 | Continue p ->
659 env, T.Continue p
660 | Break p ->
661 env, T.Break p
663 and check_exhaustiveness env pos ty caselist =
664 check_exhaustiveness_ env pos ty caselist false
666 and check_exhaustiveness_ env pos ty caselist enum_coming_from_unresolved =
667 (* Right now we only do exhaustiveness checking for enums. *)
668 (* This function has a built in hack where if Tunresolved has an enum
669 inside then it tells the enum exhaustiveness checker to
670 not punish for extra default *)
671 let env, (_, ty) = Env.expand_type env ty in
672 match ty with
673 | Tunresolved tyl ->
674 let new_enum = enum_coming_from_unresolved ||
675 (List.length tyl> 1 && List.exists tyl ~f:begin fun cur_ty ->
676 let _, (_, cur_ty) = Env.expand_type env cur_ty in
677 match cur_ty with
678 | Tabstract (AKenum _, _) -> true
679 | _ -> false
680 end) in
681 List.fold_left tyl ~init:env ~f:begin fun env ty ->
682 check_exhaustiveness_ env pos ty caselist new_enum
684 | Tabstract (AKenum id, _) ->
685 let tc = unsafe_opt @@ Env.get_enum env id in
686 Typing_enum.check_enum_exhaustiveness pos tc
687 caselist enum_coming_from_unresolved;
689 | Terr | Tany | Tmixed | Tarraykind _ | Tclass _ | Toption _ | Tprim _
690 | Tvar _ | Tfun _ | Tabstract (_, _) | Ttuple _ | Tanon (_, _)
691 | Tobject | Tshape _ -> env
693 and case_list parent_lenv ty env cl =
694 let env = { env with Env.lenv = parent_lenv } in
695 case_list_ parent_lenv ty env cl
697 and try_catch env tb cl =
698 let parent_lenv = env.Env.lenv in
699 let env = Env.freeze_local_env env in
700 let env, ttb = block env tb in
701 let after_try = env.Env.lenv in
702 let env, term_lenv_l = List.map_env env cl
703 begin fun env (_, _, b as catch_block) ->
704 let env, lenv = catch parent_lenv after_try env catch_block in
705 let term = Nast_terminality.Terminal.block env b in
706 env, (term, lenv)
707 end in
708 let term_lenv_l =
709 (Nast_terminality.Terminal.block env tb, after_try) :: term_lenv_l in
710 let env = LEnv.intersect_list env parent_lenv term_lenv_l in
711 env, ttb, [] (* TODO TAST tcl *)
713 and drop_dead_code_after_break_block = function
714 | [] -> [], false
715 | Break x :: _ -> [Break x], true
716 | x :: rest ->
717 let x', drop =
718 match x with
719 | If (_, [], []) as if_stmt -> if_stmt, false
720 | If (cond, b1, b2) ->
721 let b1, drop1 = if b1 = [] then [], true
722 else drop_dead_code_after_break_block b1 in
723 let b2, drop2 = if b2 = [] then [], true
724 else drop_dead_code_after_break_block b2 in
725 If (cond, b1, b2), drop1 && drop2
726 | x -> x, false
728 if drop then ([x'], true) else begin
729 let rest', drop = drop_dead_code_after_break_block rest in
730 x'::rest', drop
733 and drop_dead_code_after_break = function
734 | Default b -> Default (fst (drop_dead_code_after_break_block b))
735 | Case (e, b) -> Case (e, fst (drop_dead_code_after_break_block b))
737 and case_list_ parent_lenv ty env = function
738 | [] -> env, [], []
739 | Default b :: _ ->
740 (* TODO this is wrong, should continue on to the other cases, but it
741 * doesn't matter in practice since our parser won't parse default
742 * anywhere but in the last position :) Should fix all of this as well
743 * as totality detection for switch. *)
744 let env, tb = block env b in
745 env, [Nast_terminality.Terminal.case env (Default b), env.Env.lenv],
746 [T.Default tb]
747 | Case (e, b) :: rl ->
748 (* TODO - we should consider handling the comparisons the same
749 * way as Binop Ast.EqEq, since case statements work using ==
750 * comparison rules *)
752 (* The way we handle terminal/nonterminal here is not quite right, you
753 * can still break the type system with things like P3131824. *)
754 let ty_num = (Reason.Rnone, Tprim Nast.Tnum) in
755 let ty_arraykey = (Reason.Rnone, Tprim Nast.Tarraykey) in
756 let both_are_sub_types env tprim ty1 ty2 =
757 (SubType.is_sub_type env ty1 tprim) &&
758 (SubType.is_sub_type env ty2 tprim) in
759 if Nast_terminality.Terminal.block env b then
760 let env, te, ty2 = expr env e in
761 let env, _ =
762 if (both_are_sub_types env ty_num ty ty2) ||
763 (both_are_sub_types env ty_arraykey ty ty2)
764 then env, ty
765 else Type.unify (fst e) Reason.URnone env ty ty2 in
766 let env, tb = block env b in
767 let lenv = env.Env.lenv in
768 let env, rl, tcl = case_list parent_lenv ty env rl in
769 env, (Nast_terminality.Terminal.case env (Case (e, b)), lenv) :: rl,
770 T.Case (te, tb)::tcl
771 else
772 let env, _te, ty2 = expr env e in
773 let env, _ =
774 if (both_are_sub_types env ty_num ty ty2) ||
775 (both_are_sub_types env ty_arraykey ty ty2)
776 then env, ty
777 else Type.unify (fst e) Reason.URnone env ty ty2 in
778 (* Since this block is not terminal we will end up falling through to the
779 * next block. This means the lenv will include what our current
780 * environment is, intersected (or integrated?) with the environment
781 * after executing the block. Example:
783 * $x = 0; // $x = int
784 * switch (0) {
785 * case 1:
786 * $x = ''; // $x = string
787 * // FALLTHROUGH
788 * case 2:
789 * $x; // $x = int & string
790 * ...
792 let lenv1 = env.Env.lenv in
793 let env, _ = block env b in
794 (* PERF: If the case is empty or a Noop then we do not need to intersect
795 * the lenv since they will be the same.
797 * This saves the cost of intersecting the lenv for the common pattern of
798 * case 1:
799 * case 2:
800 * case 3:
801 * ...
803 let env = match b with
804 | [] | [Noop] -> env
805 | _ -> LEnv.intersect env parent_lenv lenv1 env.Env.lenv in
806 case_list_ parent_lenv ty env rl
808 and catch parent_lenv after_try env (ety, exn, b) =
809 let env = { env with Env.lenv = after_try } in
810 let env = LEnv.fully_integrate env parent_lenv in
811 let cid = CI (ety, []) in
812 let ety_p = (fst ety) in
813 TUtils.process_class_id cid;
814 let env, _, _ = instantiable_cid ety_p env cid in
815 let env, _te, ety = static_class_id ety_p env cid in
816 let env = exception_ty ety_p env ety in
817 let env = Env.set_local env (snd exn) ety in
818 let env, _tb = block env b in
819 (* Only keep the local bindings if this catch is non-terminal *)
820 env, env.Env.lenv
822 and as_expr env pe =
823 let make_result ty = env, ty in
824 function
825 | As_v _ ->
826 let ty = Env.fresh_type() in
827 let tvector = Tclass ((pe, SN.Collections.cTraversable), [ty]) in
828 make_result (Reason.Rforeach pe, tvector)
829 | As_kv _ ->
830 let ty1 = Env.fresh_type() in
831 let ty2 = Env.fresh_type() in
832 let tmap = Tclass((pe, SN.Collections.cKeyedTraversable), [ty1; ty2]) in
833 make_result (Reason.Rforeach pe, tmap)
834 | Await_as_v _ ->
835 let ty = Env.fresh_type() in
836 let tvector = Tclass ((pe, SN.Classes.cAsyncIterator), [ty]) in
837 make_result (Reason.Rasyncforeach pe, tvector)
838 | Await_as_kv _ ->
839 let ty1 = Env.fresh_type() in
840 let ty2 = Env.fresh_type() in
841 let tmap = Tclass ((pe, SN.Classes.cAsyncKeyedIterator), [ty1; ty2]) in
842 make_result (Reason.Rasyncforeach pe, tmap)
844 and bind_as_expr env ty aexpr =
845 let env, ety = Env.expand_type env ty in
846 let p, ty1, ty2 =
847 match ety with
848 | _, Tclass ((p, _), [ty2]) -> (p, (Reason.Rnone, Tmixed), ty2)
849 | _, Tclass ((p, _), [ty1; ty2]) -> (p, ty1, ty2)
850 | _ -> assert false in
851 match aexpr with
852 | As_v ev ->
853 let env, te, _ = assign p env ev ty2 in
854 env, T.As_v te
855 | Await_as_v (p, ev) ->
856 let env, te, _ = assign p env ev ty2 in
857 env, T.Await_as_v(p, te)
858 | As_kv ((p, Lvar ((_, k) as id)), ev) ->
859 let env, ty1' = set_valid_rvalue p env k ty1 in
860 let env, te, _ = assign p env ev ty2 in
861 env, T.As_kv(T.make_typed_expr p ty1' (T.Lvar id), te)
862 | Await_as_kv (p, (p1, Lvar ((_, k) as id)), ev) ->
863 let env, ty1' = set_valid_rvalue p env k ty1 in
864 let env, te, _ = assign p env ev ty2 in
865 env, T.Await_as_kv(p, T.make_typed_expr p1 ty1' (T.Lvar id), te)
866 | _ -> (* TODO Probably impossible, should check that *)
867 assert false
869 and expr env e =
870 raw_expr ~in_cond:false env e
872 and raw_expr ~in_cond ?valkind:(valkind=`other) env e =
873 debug_last_pos := fst e;
874 let env, te, ty = expr_ ~in_cond ~valkind env e in
875 let () = match !expr_hook with
876 | Some f -> f e (Typing_expand.fully_expand env ty)
877 | None -> () in
878 Typing_hooks.dispatch_infer_ty_hook ty (fst e) env;
879 env, te, ty
881 and lvalue env e =
882 let valkind = `lvalue in
883 expr_ ~in_cond:false ~valkind env e
885 and is_pseudo_function s =
886 s = SN.PseudoFunctions.hh_show ||
887 s = SN.PseudoFunctions.hh_show_env ||
888 s = SN.PseudoFunctions.hh_log_level
890 (* $x ?? 0 is handled similarly to $x ?: 0, except that the latter will also
891 * look for sketchy null checks in the condition. *)
892 (* TODO TAST: type refinement should be made explicit in the typed AST *)
893 and eif env ~coalesce ~in_cond p c e1 e2 =
894 let env, tc, tyc = raw_expr in_cond env c in
895 let parent_lenv = env.Env.lenv in
896 let c = if coalesce then (p, Binop (Ast.Diff2, c, (p, Null))) else c in
897 let env = condition env true c in
898 let env, te1, ty1 = match e1 with
899 | None ->
900 let env, ty = non_null env tyc in
901 env, None, ty
902 | Some e1 ->
903 let env, te1, ty1 = expr env e1 in
904 env, Some te1, ty1
906 let lenv1 = env.Env.lenv in
907 let env = { env with Env.lenv = parent_lenv } in
908 let env = condition env false c in
909 let env, te2, ty2 = expr env e2 in
910 let lenv2 = env.Env.lenv in
911 let fake_members =
912 LEnv.intersect_fake lenv1.Env.fake_members lenv2.Env.fake_members in
913 (* we restore the locals to their parent state so as not to leak the
914 * effects of the `condition` calls above *)
915 let env = { env with Env.lenv =
916 { parent_lenv with Env.fake_members = fake_members } } in
917 (* This is a shortened form of what we do in Typing_lenv.intersect. The
918 * latter takes local environments as arguments, but our types here
919 * aren't assigned to local variables in an environment *)
920 let env, ty1 = TUtils.unresolved env ty1 in
921 let env, ty2 = TUtils.unresolved env ty2 in
922 let env, ty = Unify.unify env ty1 ty2 in
923 env, T.make_typed_expr p ty (T.Eif(tc, te1, te2)), ty
925 and exprs env el =
926 match el with
927 | [] ->
928 env, [], []
930 | e::el ->
931 let env, te, ty = expr env e in
932 let env, tel, tyl = exprs env el in
933 env, te::tel, ty::tyl
935 and expr_
936 ~in_cond
937 ~(valkind: [> `lvalue | `lvalue_subexpr | `other ])
938 env (p, e) =
939 let make_result env te ty =
940 env, T.make_typed_expr p ty te, ty in
943 * Given a list of types, computes their supertype. If any of the types are
944 * unknown (e.g., comes from PHP), the supertype will be Tany.
946 let compute_supertype env tys =
947 let env, supertype = Env.fresh_unresolved_type env in
948 let has_unknown = List.exists tys (fun (_, ty) -> ty = Tany) in
949 let env, tys = List.rev_map_env env tys TUtils.unresolved in
950 let subtype_value env ty =
951 Type.sub_type p Reason.URarray_value env ty supertype in
952 if has_unknown then
953 (* If one of the values comes from PHP land, we have to be conservative
954 * and consider that we don't know what the type of the values are. *)
955 env, (Reason.Rnone, Tany)
956 else
957 let env = List.fold_left tys ~init:env ~f:subtype_value in
958 env, supertype in
961 * Given a 'a list and a method to extract an expr and its ty from a 'a, this
962 * function extracts a list of exprs from the list, and computes the supertype
963 * of all of the expressions' tys.
965 let compute_exprs_and_supertype env l extract_expr_and_ty =
966 let env, exprs_and_tys = List.map_env env l extract_expr_and_ty in
967 let exprs, tys = List.unzip exprs_and_tys in
968 let env, supertype = compute_supertype env tys in
969 env, exprs, supertype in
971 match e with
972 | Any -> expr_error env (Reason.Rwitness p)
973 | Array [] ->
974 make_result env (T.Array []) (Reason.Rwitness p, Tarraykind AKempty)
975 | Array l when Typing_arrays.is_shape_like_array env l ->
976 let env, (tafl, fdm) = List.fold_left_env env l
977 ~init:([], ShapeMap.empty)
978 ~f:begin fun env (tafl,fdm) x ->
979 let env, taf, (key, value) = akshape_field env x in
980 env, (taf::tafl, Nast.ShapeMap.add key value fdm)
981 end in
982 make_result env (T.Array(List.rev tafl))
983 (Reason.Rwitness p, Tarraykind (AKshape fdm))
985 | Array (x :: rl as l) ->
986 let fields_consistent = check_consistent_fields x rl in
987 let is_vec = match x with
988 | Nast.AFvalue _ -> true
989 | Nast.AFkvalue _ -> false in
990 if fields_consistent && is_vec then
991 let env, tel, fields =
992 List.foldi l ~f:begin fun index (env, tel, acc) e ->
993 let env, te, ty = aktuple_field env e in
994 env, te::tel, IMap.add index ty acc
995 end ~init:(env, [], IMap.empty) in
996 make_result env
997 (T.Array (List.map (List.rev tel) (fun e -> T.AFvalue e)))
998 (Reason.Rwitness p, Tarraykind (AKtuple fields))
999 else
1000 let env, value_exprs_and_tys = List.rev_map_env env l array_field_value in
1001 let tvl, value_tys = List.unzip value_exprs_and_tys in
1002 let env, value = compute_supertype env value_tys in
1003 (* TODO TAST: produce a typed expression here *)
1004 if is_vec then
1005 make_result env T.Any
1006 (Reason.Rwitness p, Tarraykind (AKvec value))
1007 else
1008 let env, key_exprs_and_tys = List.rev_map_env env l array_field_key in
1009 let tkl, key_tys = List.unzip key_exprs_and_tys in
1010 let env, key = compute_supertype env key_tys in
1011 make_result env
1012 (T.Array (List.map (List.rev (List.zip_exn tkl tvl))
1013 (fun (tek, tev) -> T.AFkvalue (tek, tev))))
1014 (Reason.Rwitness p, Tarraykind (AKmap (key, value)))
1016 | Darray l ->
1017 let keys, values = List.unzip l in
1019 let env, value_exprs, value_ty =
1020 compute_exprs_and_supertype env values array_value in
1021 let env, key_exprs, key_ty =
1022 compute_exprs_and_supertype env keys array_value in
1024 let field_exprs = List.zip_exn key_exprs value_exprs in
1025 make_result env
1026 (T.Darray field_exprs)
1027 (Reason.Rwitness p, Tarraykind (AKdarray (key_ty, value_ty)))
1029 | Varray values ->
1030 let env, value_exprs, value_ty =
1031 compute_exprs_and_supertype env values array_value in
1032 make_result env
1033 (T.Varray value_exprs)
1034 (Reason.Rwitness p, Tarraykind (AKvarray value_ty))
1036 | ValCollection (kind, el) ->
1037 let env, x = Env.fresh_unresolved_type env in
1038 let env, tel, tyl = exprs env el in
1039 let env, tyl = List.map_env env tyl Typing_env.unbind in
1040 let env, tyl = List.map_env env tyl TUtils.unresolved in
1041 let subtype_val env ty =
1042 Type.sub_type p Reason.URvector env ty x in
1043 let env =
1044 List.fold_left tyl ~init:env ~f:subtype_val in
1045 let tvector = Tclass ((p, vc_kind_to_name kind), [x]) in
1046 let ty = Reason.Rwitness p, tvector in
1047 make_result env (T.ValCollection (kind, tel)) ty
1048 | KeyValCollection (kind, l) ->
1049 let kl, vl = List.unzip l in
1050 let env, tkl, kl = exprs env kl in
1051 let env, kl = List.map_env env kl Typing_env.unbind in
1052 let env, tvl, vl = exprs env vl in
1053 let env, vl = List.map_env env vl Typing_env.unbind in
1054 let env, k = Env.fresh_unresolved_type env in
1055 let env, v = Env.fresh_unresolved_type env in
1056 let env, kl = List.map_env env kl TUtils.unresolved in
1057 let subtype_key env ty = Type.sub_type p Reason.URkey env ty k in
1058 let env =
1059 List.fold_left kl ~init:env ~f:subtype_key in
1060 let subtype_val env ty = Type.sub_type p Reason.URvalue env ty v in
1061 let env, vl = List.map_env env vl TUtils.unresolved in
1062 let env =
1063 List.fold_left vl ~init:env ~f:subtype_val in
1064 let ty = Tclass ((p, kvc_kind_to_name kind), [k; v])
1066 make_result env (T.KeyValCollection (kind, List.zip_exn tkl tvl))
1067 (Reason.Rwitness p, ty)
1068 | Clone e ->
1069 let env, te, ty = expr env e in
1070 make_result env (T.Clone te) ty
1071 | This when Env.is_static env ->
1072 Errors.this_in_static p;
1073 expr_error env (Reason.Rwitness p)
1074 | This when valkind = `lvalue ->
1075 Errors.this_lvalue p;
1076 expr_error env (Reason.Rwitness p)
1077 | This ->
1078 let r, _ = Env.get_self env in
1079 if r = Reason.Rnone
1080 then Errors.this_var_outside_class p;
1081 let env, (_, ty) = Env.get_local env this in
1082 let r = Reason.Rwitness p in
1083 let ty = (r, ty) in
1084 let ty = r, TUtils.this_of ty in
1085 (* '$this' always refers to the late bound static type *)
1086 make_result env T.This (ExprDepTy.make env CIstatic ty)
1087 | Assert (AE_assert e) ->
1088 let env = condition env true e in
1089 make_result env T.Any (Reason.Rwitness p, Tprim Tvoid)
1090 | True ->
1091 make_result env T.True (Reason.Rwitness p, Tprim Tbool)
1092 | False ->
1093 make_result env T.False (Reason.Rwitness p, Tprim Tbool)
1094 (* TODO TAST: consider checking that the integer is in range. Right now
1095 * it's possible for HHVM to fail on well-typed Hack code
1097 | Int s ->
1098 make_result env (T.Int s) (Reason.Rwitness p, Tprim Tint)
1099 | Float s ->
1100 make_result env (T.Float s) (Reason.Rwitness p, Tprim Tfloat)
1101 (* TODO TAST: consider introducing a "null" type, and defining ?t to
1102 * be null | t
1104 | Null ->
1105 let ty = Env.fresh_type() in
1106 make_result env T.Null (Reason.Rwitness p, Toption ty)
1107 | String s ->
1108 make_result env (T.String s) (Reason.Rwitness p, Tprim Tstring)
1109 | String2 idl ->
1110 let env, tel = string2 env idl in
1111 make_result env (T.String2 tel) (Reason.Rwitness p, Tprim Tstring)
1112 | Fun_id x ->
1113 Typing_hooks.dispatch_id_hook x env;
1114 let env, fty = fun_type_of_id env x in
1115 begin match fty with
1116 | _, Tfun fty -> check_deprecated (fst x) fty;
1117 | _ -> ()
1118 end;
1119 make_result env (T.Fun_id x) fty
1120 | Id ((cst_pos, cst_name) as id) ->
1121 Typing_hooks.dispatch_id_hook id env;
1122 Typing_hooks.dispatch_global_const_hook id;
1123 (match Env.get_gconst env cst_name with
1124 | None when Env.is_strict env ->
1125 Errors.unbound_global cst_pos;
1126 expr_error env (Reason.Rwitness cst_pos)
1127 | None ->
1128 make_result env (T.Id id) (Reason.Rnone, Tany)
1129 | Some ty ->
1130 let env, ty =
1131 Phase.localize_with_self env ty in
1132 make_result env (T.Id id) ty
1134 | Method_id (instance, meth) ->
1135 (* Method_id is used when creating a "method pointer" using the magic
1136 * inst_meth function.
1138 * Typing this is pretty simple, we just need to check that instance->meth
1139 * is public+not static and then return its type.
1141 Typing_hooks.dispatch_fun_id_hook (p, "\\"^SN.SpecialFunctions.inst_meth);
1142 let env, te, ty1 = expr env instance in
1143 let env, result, vis =
1144 obj_get_with_visibility ~is_method:true ~nullsafe:None env ty1
1145 (CIexpr instance) meth (fun x -> x) in
1146 let has_lost_info = Env.FakeMembers.is_invalid env instance (snd meth) in
1147 if has_lost_info
1148 then
1149 let name = "the method "^snd meth in
1150 let env, result = Env.lost_info name env result in
1151 make_result env (T.Method_id (te, meth)) result
1152 else
1153 begin
1154 (match result with
1155 | _, Tfun fty -> check_deprecated p fty
1156 | _ -> ());
1157 (match vis with
1158 | Some (method_pos, Vprivate _) ->
1159 Errors.private_inst_meth method_pos p
1160 | Some (method_pos, Vprotected _) ->
1161 Errors.protected_inst_meth method_pos p
1162 | _ -> ()
1164 make_result env (T.Method_id (te, meth)) result
1166 | Method_caller ((pos, class_name) as pos_cname, meth_name) ->
1167 (* meth_caller('X', 'foo') desugars to:
1168 * $x ==> $x->foo()
1170 Typing_hooks.dispatch_fun_id_hook (p, "\\"^SN.SpecialFunctions.meth_caller);
1171 let class_ = Env.get_class env class_name in
1172 (match class_ with
1173 | None -> unbound_name env pos_cname
1174 | Some class_ ->
1175 (* Create a class type for the given object instantiated with unresolved
1176 * types for its type parameters.
1178 let env, tvarl =
1179 List.map_env env class_.tc_tparams TUtils.unresolved_tparam in
1180 let params = List.map class_.tc_tparams begin fun (_, (p, n), _) ->
1181 Reason.Rwitness p, Tgeneric n
1182 end in
1183 let obj_type = Reason.Rwitness p, Tapply (pos_cname, params) in
1184 let ety_env = {
1185 (Phase.env_with_self env) with
1186 substs = Subst.make class_.tc_tparams tvarl;
1187 } in
1188 let env, local_obj_ty = Phase.localize ~ety_env env obj_type in
1189 let env, fty =
1190 obj_get ~is_method:true ~nullsafe:None env local_obj_ty
1191 (CI ((pos, class_name), [])) meth_name (fun x -> x) in
1192 (match fty with
1193 | reason, Tfun fty ->
1194 check_deprecated p fty;
1195 (* We are creating a fake closure:
1196 * function(Class $x, arg_types_of(Class::meth_name))
1197 : return_type_of(Class::meth_name)
1199 let ety_env = {
1200 ety_env with substs = Subst.make class_.tc_tparams tvarl
1201 } in
1202 let env =
1203 Phase.check_tparams_constraints ~ety_env env class_.tc_tparams in
1204 let env, local_obj_ty = Phase.localize ~ety_env env obj_type in
1205 let fty = { fty with
1206 ft_params = (None, local_obj_ty) :: fty.ft_params } in
1207 let fun_arity = match fty.ft_arity with
1208 | Fstandard (min, max) -> Fstandard (min + 1, max + 1)
1209 | Fvariadic (min, x) -> Fvariadic (min + 1, x)
1210 | Fellipsis min -> Fellipsis (min + 1) in
1211 let caller = {
1212 ft_pos = pos;
1213 ft_deprecated = None;
1214 ft_abstract = false;
1215 ft_arity = fun_arity;
1216 ft_tparams = fty.ft_tparams;
1217 ft_where_constraints = fty.ft_where_constraints;
1218 ft_params = fty.ft_params;
1219 ft_ret = fty.ft_ret;
1220 } in
1221 make_result env (T.Method_caller(pos_cname, meth_name))
1222 (reason, Tfun caller)
1223 | _ ->
1224 (* This can happen if the method lives in PHP *)
1225 make_result env (T.Method_caller(pos_cname, meth_name))
1226 (Reason.Rwitness pos, Tany)
1229 | Smethod_id (c, meth) ->
1230 (* Smethod_id is used when creating a "method pointer" using the magic
1231 * class_meth function.
1233 * Typing this is pretty simple, we just need to check that c::meth is
1234 * public+static and then return its type.
1236 Typing_hooks.dispatch_fun_id_hook (p, "\\"^SN.SpecialFunctions.class_meth);
1237 let class_ = Env.get_class env (snd c) in
1238 (match class_ with
1239 | None ->
1240 (* The class given as a static string was not found. *)
1241 unbound_name env c
1242 | Some class_ ->
1243 let smethod = Env.get_static_member true env class_ (snd meth) in
1244 (match smethod with
1245 | None -> (* The static method wasn't found. *)
1246 smember_not_found p ~is_const:false ~is_method:true class_ (snd meth);
1247 expr_error env Reason.Rnone
1248 | Some { ce_type = lazy ty; ce_visibility; _ } ->
1249 let cid = CI (c, []) in
1250 let env, _te, cid_ty = static_class_id (fst c) env cid in
1251 let ety_env = {
1252 type_expansions = [];
1253 substs = SMap.empty;
1254 this_ty = cid_ty;
1255 from_class = Some cid;
1256 } in
1257 let env, smethod_type = Phase.localize ~ety_env env ty in
1258 (match smethod_type with
1259 | _, Tfun fty -> check_deprecated p fty
1260 | _ -> ());
1261 (match smethod_type, ce_visibility with
1262 | (r, (Tfun _ as ty)), Vpublic ->
1263 make_result env (T.Smethod_id(c, meth)) (r, ty)
1264 | (r, Tfun _), Vprivate _ ->
1265 Errors.private_class_meth (Reason.to_pos r) p;
1266 expr_error env r
1267 | (r, Tfun _), Vprotected _ ->
1268 Errors.protected_class_meth (Reason.to_pos r) p;
1269 expr_error env r
1270 | (r, _), _ ->
1271 Errors.internal_error p "We have a method which isn't callable";
1272 expr_error env r
1276 | Lplaceholder p ->
1277 let r = Reason.Rplaceholder p in
1278 let ty = r, Tprim Tvoid in
1279 make_result env (T.Lplaceholder p) ty
1280 | Dollardollar ((_, x) as id) ->
1281 let env, ty =
1282 Env.get_local env x in
1283 make_result env (T.Dollardollar id) ty
1284 | Lvar ((_, x) as id) ->
1285 Typing_hooks.dispatch_lvar_hook id env;
1286 let env, ty = Env.get_local env x in
1287 make_result env (T.Lvar id) ty
1288 | Lvarvar (i, id) ->
1289 Typing_hooks.dispatch_lvar_hook id env;
1290 (** Can't easily track any typing information for variable variable. *)
1291 make_result env (T.Lvarvar (i, id)) (Reason.Rnone, Tany)
1292 | List el ->
1293 let env, tel, tyl = exprs env el in
1294 (* TODO TAST: figure out role of unbind here *)
1295 let env, tyl = List.map_env env tyl Typing_env.unbind in
1296 let ty = Reason.Rwitness p, Ttuple tyl in
1297 make_result env (T.List tel) ty
1298 | Pair (e1, e2) ->
1299 let env, te1, ty1 = expr env e1 in
1300 let env, ty1 = Typing_env.unbind env ty1 in
1301 let env, te2, ty2 = expr env e2 in
1302 let env, ty2 = Typing_env.unbind env ty2 in
1303 let ty =
1304 Reason.Rwitness p, Tclass ((p, SN.Collections.cPair), [ty1; ty2]) in
1305 make_result env (T.Pair (te1, te2)) ty
1306 | Expr_list el ->
1307 let env, tel, tyl = exprs env el in
1308 let ty = Reason.Rwitness p, Ttuple tyl in
1309 make_result env (T.Expr_list tel) ty
1310 | Array_get (e, None) ->
1311 let env, te1, ty1 = update_array_type p env e None valkind in
1312 let env, ty = array_append p env ty1 in
1313 make_result env (T.Array_get(te1, None)) ty
1314 | Array_get (e1, Some e2) ->
1315 let env, te1, ty1 = update_array_type p env e1 (Some e2) valkind in
1316 let env, ty1 = TUtils.fold_unresolved env ty1 in
1317 let env, te2, ty2 = expr env e2 in
1318 let is_lvalue = (valkind == `lvalue) in
1319 let env, ty = array_get is_lvalue p env ty1 e2 ty2 in
1320 make_result env (T.Array_get(te1, Some te2)) ty
1321 | Call (Cnormal, (pos_id, Id ((_, s) as id)), el, [])
1322 when is_pseudo_function s ->
1323 let env, tel, tys = exprs env el in
1324 if s = SN.PseudoFunctions.hh_show
1325 then List.iter tys (Typing_log.hh_show p env)
1326 else
1327 if s = SN.PseudoFunctions.hh_show_env
1328 then Typing_log.hh_show_env p env
1329 else
1330 if s = SN.PseudoFunctions.hh_log_level
1331 then match el with
1332 | [(_, Int (_, level_str))] ->
1333 Typing_log.hh_log_level (int_of_string level_str)
1334 | _ -> ()
1335 else ();
1336 make_result env
1337 (T.Call(
1338 Cnormal,
1339 T.make_implicitly_typed_expr pos_id (T.Id id),
1340 tel,
1341 [])) (Env.fresh_type())
1342 | Call (call_type, e, el, uel) ->
1343 let env, te, result = dispatch_call p env call_type e el uel in
1344 let env = Env.forget_members env p in
1345 env, te, result
1346 (* For example, e1 += e2. This is typed and translated as if
1347 * written e1 = e1 + e2.
1348 * TODO TAST: is this right? e1 will get evaluated more than once
1350 | Binop (Ast.Eq (Some op), e1, e2) ->
1351 let e2 = p, Binop (op, e1, e2) in
1352 raw_expr in_cond env (p, Binop (Ast.Eq None, e1, e2))
1353 | Binop (Ast.Eq None, e1, e2) ->
1354 let env, te2, ty2 = raw_expr in_cond env e2 in
1355 let env, te1, ty = assign p env e1 ty2 in
1356 Typing_hooks.dispatch_assign_hook p ty2 env;
1357 (* If we are assigning a local variable to another local variable then
1358 * the expression ID associated with e2 is transferred to e1
1360 (match e1, e2 with
1361 | (_, Lvar (_, x1)), (_, Lvar (_, x2)) ->
1362 let eid2 = Env.get_local_expr_id env x2 in
1363 let env =
1364 Option.value_map
1365 eid2 ~default:env
1366 ~f:(Env.set_local_expr_id env x1) in
1367 make_result env (T.Binop(Ast.Eq None, te1, te2)) ty
1368 | _ ->
1369 make_result env (T.Binop(Ast.Eq None, te1, te2)) ty
1371 | Binop ((Ast.AMpamp | Ast.BArbar as bop), e1, e2) ->
1372 let c = bop = Ast.AMpamp in
1373 let lenv = env.Env.lenv in
1374 let env, te1, ty1 = expr env e1 in
1375 let env = condition env c e1 in
1376 let env, te2, ty2 = raw_expr in_cond env e2 in
1377 let env = { env with Env.lenv = lenv } in
1378 Typing_hooks.dispatch_binop_hook p bop ty1 ty2;
1379 make_result env (T.Binop(bop, te1, te2))
1380 (Reason.Rlogic_ret p, Tprim Tbool)
1381 | Binop (bop, e, (pe, Null))
1382 | Binop (bop, (pe, Null), e)
1383 when Env.is_strict env && (bop = Ast.EQeqeq || bop = Ast.Diff2) ->
1384 let _, te, ty = raw_expr in_cond env e in
1385 if not in_cond
1386 then Typing_equality_check.assert_nullable p bop env ty;
1387 make_result env (T.Binop(bop, te, T.make_typed_expr pe ty T.Null))
1388 (Reason.Rcomp p, Tprim Tbool)
1389 | Binop (bop, e1, e2) ->
1390 let env, te1, ty1 = raw_expr in_cond env e1 in
1391 let env, te2, ty2 = raw_expr in_cond env e2 in
1392 let env, te3, ty =
1393 binop in_cond p env bop (fst e1) te1 ty1 (fst e2) te2 ty2 in
1394 Typing_hooks.dispatch_binop_hook p bop ty1 ty2;
1395 env, te3, ty
1396 | Pipe ((_, id) as e0, e1, e2) ->
1397 let env, te1, ty = expr env e1 in
1398 (** id is the ID of the $$ that is implicitly declared by the pipe.
1399 * Set the local type for the $$ in the RHS. *)
1400 let env = Env.set_local env id ty in
1401 let env, te2, ty2 = expr env e2 in
1403 * Return ty2 since the type of the pipe expression is the type of the
1404 * RHS.
1406 * Note: env does have the type of this Pipe's $$, but it doesn't
1407 * override the outer one since they have different ID's.
1409 * For example:
1410 * a() |> ( inner1($$) |> inner2($$) ) + $$
1412 * The rightmost $$ refers to the result of a()
1414 make_result env (T.Pipe(e0, te1, te2)) ty2
1415 | Unop (uop, e) ->
1416 let env, te, ty = raw_expr in_cond env e in
1417 unop p env uop te ty
1418 | Eif (c, e1, e2) -> eif env ~coalesce:false ~in_cond p c e1 e2
1419 | NullCoalesce (e1, e2) -> eif env ~coalesce:true ~in_cond p e1 None e2
1420 | Typename sid ->
1421 begin match Env.get_typedef env (snd sid) with
1422 | Some {td_tparams = tparaml; _} ->
1423 (* Typedef type parameters cannot have constraints *)
1424 let params = List.map ~f:begin fun (_, (p, x), _) ->
1425 Reason.Rwitness p, Tgeneric x
1426 end tparaml in
1427 let tdef = Reason.Rwitness (fst sid), Tapply (sid, params) in
1428 let typename =
1429 Reason.Rwitness p, Tapply((p, SN.Classes.cTypename), [tdef]) in
1430 let env, tparams = List.map_env env tparaml begin fun env _ ->
1431 Env.fresh_unresolved_type env
1432 end in
1433 let ety_env = { (Phase.env_with_self env) with
1434 substs = Subst.make tparaml tparams } in
1435 let env = Phase.check_tparams_constraints ~ety_env env tparaml in
1436 let env, ty = Phase.localize ~ety_env env typename in
1437 make_result env (T.Typename sid) ty
1438 | None ->
1439 (* Should never hit this case since we only construct this AST node
1440 * if in the expression Foo::class, Foo is a type def.
1442 expr_error env (Reason.Rwitness p)
1444 | Class_const (cid, mid) -> class_const env p (cid, mid)
1445 | Class_get (x, (_, y))
1446 when Env.FakeMembers.get_static env x y <> None ->
1447 let env, local = Env.FakeMembers.make_static p env x y in
1448 let local = p, Lvar (p, local) in
1449 expr env local
1450 | Class_get (cid, mid) ->
1451 TUtils.process_static_find_ref cid mid;
1452 let env, te, cty = static_class_id p env cid in
1453 let env, ty, _ =
1454 class_get ~is_method:false ~is_const:false env cty mid cid in
1455 if Env.FakeMembers.is_static_invalid env cid (snd mid)
1456 then
1457 let fake_name = Env.FakeMembers.make_static_id cid (snd mid) in
1458 let env, ty = Env.lost_info fake_name env ty in
1459 make_result env (T.Class_get (te, mid)) ty
1460 else
1461 make_result env (T.Class_get (te, mid)) ty
1462 (* Fake member property access. For example:
1463 * if ($x->f !== null) { ...$x->f... }
1465 | Obj_get (e, (_, Id (_, y)), _)
1466 when Env.FakeMembers.get env e y <> None ->
1467 let env, local = Env.FakeMembers.make p env e y in
1468 let local = p, Lvar (p, local) in
1469 expr env local
1470 (* Statically-known instance property access e.g. $x->f *)
1471 | Obj_get (e1, (pm, Id m), nullflavor) ->
1472 let nullsafe =
1473 (match nullflavor with
1474 | OG_nullthrows -> None
1475 | OG_nullsafe -> Some p
1476 ) in
1477 let env, te1, ty1 = expr env e1 in
1478 let env, result =
1479 obj_get ~is_method:false ~nullsafe env ty1 (CIexpr e1) m (fun x -> x) in
1480 let has_lost_info = Env.FakeMembers.is_invalid env e1 (snd m) in
1481 let env, result =
1482 if has_lost_info
1483 then
1484 let name = "the member " ^ snd m in
1485 Env.lost_info name env result
1486 else
1487 env, result
1489 make_result env (T.Obj_get(te1,
1490 T.make_implicitly_typed_expr pm (T.Id m), nullflavor)) result
1491 (* Dynamic instance property access e.g. $x->$f *)
1492 | Obj_get (e1, e2, nullflavor) ->
1493 let env, te1, _ = expr env e1 in
1494 let env, te2, _ = expr env e2 in
1495 let ty = (Reason.Rwitness p, Tany) in
1496 make_result env (T.Obj_get(te1, te2, nullflavor)) ty
1497 | Yield_break ->
1498 make_result env T.Yield_break (Reason.Rwitness p, Tany)
1499 | Yield af ->
1500 let env, (taf, opt_key, value) = array_field env af in
1501 let send = Env.fresh_type () in
1502 let env, key = match af, opt_key with
1503 | Nast.AFvalue (p, _), None ->
1504 let result_ty =
1505 match Env.get_fn_kind env with
1506 | Ast.FSync
1507 | Ast.FAsync ->
1508 Errors.internal_error p "yield found in non-generator";
1509 Reason.Rnone, Tany
1510 | Ast.FGenerator ->
1511 (Reason.Rwitness p, Tprim Tint)
1512 | Ast.FAsyncGenerator ->
1513 (Reason.Ryield_asyncnull p,
1514 Toption (Env.fresh_type ()))
1516 env, result_ty
1517 | _, Some x ->
1518 env, x
1519 | _, _ -> assert false in
1520 let rty = match Env.get_fn_kind env with
1521 | Ast.FGenerator ->
1522 Reason.Ryield_gen p,
1523 Tclass ((p, SN.Classes.cGenerator), [key; value; send])
1524 | Ast.FAsyncGenerator ->
1525 Reason.Ryield_asyncgen p,
1526 Tclass ((p, SN.Classes.cAsyncGenerator), [key; value; send])
1527 | Ast.FSync | Ast.FAsync ->
1528 failwith "Parsing should never allow this" in
1529 let env =
1530 Type.sub_type p (Reason.URyield) env rty (Env.get_return env) in
1531 let env = Env.forget_members env p in
1532 make_result env (T.Yield taf) (Reason.Ryield_send p, Toption send)
1533 | Await e ->
1534 let env, te, rty = expr env e in
1535 let env, ty = Async.overload_extract_from_awaitable env p rty in
1536 make_result env (T.Await te) ty
1537 | Special_func func -> special_func env p func
1538 | New (c, el, uel) ->
1539 Typing_hooks.dispatch_new_id_hook c env p;
1540 TUtils.process_static_find_ref c (p, SN.Members.__construct);
1541 let check_not_abstract = true in
1542 let env, tc, tel, tuel, ty =
1543 new_object ~check_not_abstract p env c el uel in
1544 let env = Env.forget_members env p in
1545 make_result env (T.New(tc, tel, tuel)) (ExprDepTy.make env c ty)
1546 | Cast ((_, Harray (None, None)), _) when Env.is_strict env ->
1547 Errors.array_cast p;
1548 expr_error env (Reason.Rwitness p)
1549 | Cast (hint, e) ->
1550 let env, te, ty2 = expr env e in
1551 Async.enforce_not_awaitable env (fst e) ty2;
1552 let env, ty = Phase.hint_locl env hint in
1553 make_result env (T.Cast (hint, te)) ty
1554 | InstanceOf (e, cid) ->
1555 let env, te, _ = expr env e in
1556 TUtils.process_class_id cid;
1557 let env, te2, _class = instantiable_cid p env cid in
1558 make_result env (T.InstanceOf (te, te2)) (Reason.Rwitness p, Tprim Tbool)
1559 | Efun (f, _idl) ->
1560 let ft = Decl.fun_decl_in_env env.Env.decl_env f in
1561 (* When creating a closure, the 'this' type will mean the late bound type
1562 * of the current enclosing class
1564 let ety_env =
1565 { (Phase.env_with_self env) with from_class = Some CIstatic } in
1566 let env, ft = Phase.localize_ft ~ety_env env ft in
1567 (* check for recursive function calls *)
1568 let anon = anon_make env p f in
1569 let env, anon_id = Env.add_anonymous env anon in
1570 let env = Errors.try_with_error
1571 (fun () ->
1572 ignore (anon env ft.ft_params); env)
1573 (fun () ->
1574 (* If the anonymous function declaration has errors itself, silence
1575 them in any subsequent usages. *)
1576 let anon env fun_params =
1577 Errors.ignore_ (fun () -> (anon env fun_params)) in
1578 Env.set_anonymous env anon_id anon) in
1579 (* TODO TAST: introduce lambda node in Tast.expr *)
1580 make_result env T.Any (Reason.Rwitness p, Tanon (ft.ft_arity, anon_id))
1581 | Xml (sid, attrl, el) ->
1582 let cid = CI (sid, []) in
1583 let env, _te, obj = expr env (fst sid, New (cid, [], [])) in
1584 let env, attr_ptyl = List.map_env env attrl begin fun env attr ->
1585 (* Typecheck the expressions - this just checks that the expressions are
1586 * valid, not that they match the declared type for the attribute *)
1587 let namepstr, valexpr = attr in
1588 let valp, _ = valexpr in
1589 let env, _te, valty = expr env valexpr in
1590 env, (namepstr, (valp, valty))
1591 end in
1592 let env, _tel, _body = exprs env el in
1593 let env, _te, classes = class_id_for_new p env cid in
1594 (match classes with
1595 | [] -> make_result env T.Any (Reason.Runknown_class p, Tobject)
1596 (* OK to ignore rest of list; class_info only used for errors, and
1597 * cid = CI sid cannot produce a union of classes anyhow *)
1598 | (_, class_info, _)::_ ->
1599 let env = List.fold_left attr_ptyl ~f:begin fun env attr ->
1600 let namepstr, valpty = attr in
1601 let valp, valty = valpty in
1602 (* We pretend that XHP attributes are stored as member variables,
1603 * prefixed with a colon.
1605 * This converts the member name to an attribute name. *)
1606 let name = ":" ^ (snd namepstr) in
1607 let env, declty =
1608 obj_get ~is_method:false ~nullsafe:None env obj cid
1609 (fst namepstr, name) (fun x -> x) in
1610 let ureason = Reason.URxhp (class_info.tc_name, snd namepstr) in
1611 Type.sub_type valp ureason env valty declty
1612 end ~init:env in
1613 make_result env T.Any obj
1615 (* TODO TAST: change AST so that order of shape expressions is preserved.
1616 * At present, evaluation order is unspecified in TAST *)
1617 | Shape fdm ->
1618 (* allow_inter adds a type-variable *)
1619 let env, tfdm =
1620 ShapeMap.map_env
1621 (fun env e -> let env, te, ty = expr env e in env, (te,ty))
1622 env fdm in
1623 let env, fdm =
1624 let convert_expr_and_type_to_shape_field_type env (_, ty) =
1625 let env, sft_ty = TUtils.unresolved env ty in
1626 (* An expression evaluation always corresponds to a shape_field_type
1627 with sft_optional = false. *)
1628 env, { sft_optional = false; sft_ty } in
1629 ShapeMap.map_env convert_expr_and_type_to_shape_field_type env tfdm in
1630 let env = check_shape_keys_validity env p (ShapeMap.keys fdm) in
1631 (* Fields are fully known, because this shape is constructed
1632 * using shape keyword and we know exactly what fields are set. *)
1633 make_result env (T.Shape (ShapeMap.map (fun (te,_) -> te) tfdm))
1634 (Reason.Rwitness p, Tshape (FieldsFullyKnown, fdm))
1636 and class_const ?(incl_tc=false) env p (cid, mid) =
1637 TUtils.process_static_find_ref cid mid;
1638 let env, ce, cty = static_class_id p env cid in
1639 let env, const_ty, cc_abstract_info =
1640 class_get ~is_method:false ~is_const:true ~incl_tc env cty mid cid in
1641 match cc_abstract_info with
1642 | Some (cc_pos, cc_name) ->
1643 let () = match cid with
1644 | CIstatic | CIexpr _ -> ();
1645 | _ -> Errors.abstract_const_usage p cc_pos cc_name; ()
1646 in env, T.make_typed_expr p const_ty (T.Class_const (ce, mid)), const_ty
1647 | None ->
1648 env, T.make_typed_expr p const_ty (T.Class_const (ce, mid)), const_ty
1650 (*****************************************************************************)
1651 (* Anonymous functions. *)
1652 (*****************************************************************************)
1653 and anon_bind_param params env ty : Env.env =
1654 match !params with
1655 | [] ->
1656 (* This code cannot be executed normally, because the arity is wrong
1657 * and it will error later. Bind as many parameters as we can and carry
1658 * on. *)
1660 | param :: paraml ->
1661 params := paraml;
1662 match param.param_hint with
1663 | Some h ->
1665 let h = Decl_hint.hint env.Env.decl_env h in
1666 (* When creating a closure, the 'this' type will mean the
1667 * late bound type of the current enclosing class
1669 let ety_env =
1670 { (Phase.env_with_self env) with from_class = Some CIstatic } in
1671 let env, h = Phase.localize ~ety_env env h in
1672 let pos = Reason.to_pos (fst ty) in
1673 let env = Type.sub_type pos Reason.URparam env ty h in
1674 (* Closures are allowed to have explicit type-hints. When
1675 * that is the case we should check that the argument passed
1676 * is compatible with the type-hint.
1677 * The body of the function should be type-checked with the
1678 * hint and not the type of the argument passed.
1679 * Otherwise it leads to strange results where
1680 * foo(?string $x = null) is called with a string and fails to
1681 * type-check. If $x is a string instead of ?string, null is not
1682 * subtype of string ...
1684 let env, _ = bind_param env (h, param) in
1686 | None ->
1687 let env, _ = bind_param env (ty, param) in
1690 and anon_bind_opt_param env param : Env.env =
1691 match param.param_expr with
1692 | None ->
1693 let ty = Reason.Rnone, Tany in
1694 let env, _ = bind_param env (ty, param) in
1696 | Some default ->
1697 let env, _te, ty = expr env default in
1698 Typing_sequencing.sequence_check_expr default;
1699 let env, _ = bind_param env (ty, param) in
1702 and anon_check_param env param =
1703 match param.param_hint with
1704 | None -> env
1705 | Some hty ->
1706 let env, hty = Phase.hint_locl env hty in
1707 let env, paramty = Env.get_local env (Local_id.get param.param_name) in
1708 let hint_pos = Reason.to_pos (fst hty) in
1709 let env = Type.sub_type hint_pos Reason.URhint env paramty hty in
1712 and anon_make tenv p f =
1713 let anon_lenv = tenv.Env.lenv in
1714 let is_typing_self = ref false in
1715 let nb = Nast.assert_named_body f.f_body in
1716 fun env (supplied_params: (string option * _) list) ->
1717 if !is_typing_self
1718 then begin
1719 Errors.anonymous_recursive p;
1720 env, err_witness p
1722 else begin
1723 is_typing_self := true;
1724 Env.anon anon_lenv env begin fun env ->
1725 let params = ref f.f_params in
1726 let env = List.fold_left ~f:(anon_bind_param params) ~init:env
1727 (List.map supplied_params snd) in
1728 let env = List.fold_left ~f:anon_bind_opt_param ~init:env !params in
1729 let env = List.fold_left ~f:anon_check_param ~init:env f.f_params in
1730 let env, hret =
1731 match f.f_ret with
1732 | None -> Env.fresh_unresolved_type env
1733 | Some x ->
1734 let ret = TI.instantiable_hint env x in
1735 (* If a 'this' type appears it needs to be compatible with the
1736 * late static type
1738 let ety_env =
1739 { (Phase.env_with_self env) with
1740 from_class = Some CIstatic } in
1741 Phase.localize ~ety_env env ret in
1742 let env = Env.set_return env hret in
1743 let env = Env.set_fn_kind env f.f_fun_kind in
1744 let env, _ = block env nb.fnb_nast in
1745 let env =
1746 if Nast_terminality.Terminal.block tenv nb.fnb_nast
1747 || nb.fnb_unsafe || !auto_complete
1748 then env
1749 else fun_implicit_return env p hret nb.fnb_nast f.f_fun_kind
1751 is_typing_self := false;
1752 env, hret
1756 (*****************************************************************************)
1757 (* End of anonymous functions. *)
1758 (*****************************************************************************)
1760 and special_func env p func =
1761 let env, tfunc, ty = (match func with
1762 | Gena e ->
1763 let env, te, ety = expr env e in
1764 let env, ty = Async.gena env p ety in
1765 env, T.Gena te, ty
1766 | Genva el ->
1767 let env, tel, etyl = exprs env el in
1768 let env, ty = Async.genva env p etyl in
1769 env, T.Genva tel, ty
1770 | Gen_array_rec e ->
1771 let env, te, ety = expr env e in
1772 let env, ty = Async.gen_array_rec env p ety in
1773 env, T.Gen_array_rec te, ty
1774 ) in
1775 let result_ty =
1776 (Reason.Rwitness p, Tclass ((p, SN.Classes.cAwaitable), [ty])) in
1777 env, T.make_typed_expr p result_ty (T.Special_func tfunc), result_ty
1780 and requires_consistent_construct = function
1781 | CIstatic -> true
1782 | CIexpr _ -> true
1783 | CIparent -> false
1784 | CIself -> false
1785 | CI _ -> false
1787 and new_object ~check_not_abstract p env cid el uel =
1788 (* Obtain class info from the cid expression. We get multiple
1789 * results with a CIexpr that has a union type *)
1790 let env, tcid, classes = instantiable_cid p env cid in
1791 let rec gather env tel tuel res classes =
1792 match classes with
1793 | [] ->
1794 begin
1795 match res with
1796 | [] ->
1797 let _ = exprs env el in
1798 env, tcid, tel, tuel, (Reason.Runknown_class p, Tobject)
1799 | [ty] -> env, tcid, tel, tuel, ty
1800 | tyl -> env, tcid, tel, tuel, (Reason.Rwitness p, Tunresolved tyl)
1803 | (cname, class_info, c_ty)::classes ->
1804 if check_not_abstract && class_info.tc_abstract
1805 && not (requires_consistent_construct cid) then
1806 uninstantiable_error p cid class_info.tc_pos class_info.tc_name p c_ty;
1807 let env, obj_ty_, params =
1808 match cid, snd c_ty with
1809 | CI (_, _::_), Tclass(_, tyl) -> env, (snd c_ty), tyl
1810 | _, _ ->
1811 let env, params = List.map_env env class_info.tc_tparams
1812 (fun env _ -> Env.fresh_unresolved_type env) in
1813 env, (Tclass (cname, params)), params in
1814 let r_witness = Reason.Rwitness p in
1815 let obj_ty = (r_witness, obj_ty_) in
1816 let env, _tcid, tel, tuel =
1817 call_construct p env class_info params el uel cid in
1818 if not (snd class_info.tc_construct) then
1819 (match cid with
1820 | CIstatic -> Errors.new_inconsistent_construct p cname `static
1821 | CIexpr _ -> Errors.new_inconsistent_construct p cname `classname
1822 | _ -> ());
1823 match cid with
1824 | CIstatic ->
1825 gather env tel tuel
1826 ((r_witness, TUtils.this_of obj_ty)::res) classes
1827 | CIparent ->
1828 (match (fst class_info.tc_construct) with
1829 | Some {ce_type = lazy ty; _ } ->
1830 let ety_env = {
1831 type_expansions = [];
1832 substs = SMap.empty;
1833 this_ty = obj_ty;
1834 from_class = None;
1835 } in
1836 let _, ce_type = Phase.localize ~ety_env env ty in
1837 ignore (check_abstract_parent_meth SN.Members.__construct p ce_type)
1838 | None -> ());
1839 gather env tel tuel (obj_ty::res) classes
1840 | CI _ | CIself -> gather env tel tuel (obj_ty::res) classes
1841 | CIexpr _ ->
1842 let c_ty = r_witness, snd c_ty in
1843 (* When constructing from a (classname) variable, the variable
1844 * dictates what the constructed object is going to be. This allows
1845 * for generic and dependent types to be correctly carried
1846 * through the 'new $foo()' iff the constructed obj_ty is a
1847 * supertype of the variable-dictated c_ty *)
1848 let env = SubType.sub_type env c_ty obj_ty in
1849 gather env tel tuel (c_ty::res) classes
1851 gather env [] [] [] classes
1853 (* FIXME: we need to separate our instantiability into two parts. Currently,
1854 * all this function is doing is checking if a given type is inhabited --
1855 * that is, whether there are runtime values of type T. However,
1856 * instantiability should be the stricter notion that T has a runtime
1857 * constructor; that is, `new T()` should be valid. In particular, interfaces
1858 * are inhabited, but not instantiable.
1859 * To make this work with classname, we likely need to add something like
1860 * concrete_classname<T>, where T cannot be an interface.
1861 * *)
1862 and instantiable_cid p env cid =
1863 let env, te, classes = class_id_for_new p env cid in
1864 begin
1865 List.iter classes begin fun ((pos, name), class_info, c_ty) ->
1866 if class_info.tc_kind = Ast.Ctrait || class_info.tc_kind = Ast.Cenum
1867 then
1868 match cid with
1869 | CIexpr _ | CI _ ->
1870 uninstantiable_error p cid class_info.tc_pos name pos c_ty
1871 | CIstatic | CIparent | CIself -> ()
1872 else if class_info.tc_kind = Ast.Cabstract && class_info.tc_final
1873 then
1874 uninstantiable_error p cid class_info.tc_pos name pos c_ty
1875 else () end;
1876 env, te, classes
1879 and uninstantiable_error reason_pos cid c_tc_pos c_name c_usage_pos c_ty =
1880 let reason_msgl = match cid with
1881 | CIexpr _ ->
1882 let ty_str = "This would be "^Typing_print.error (snd c_ty) in
1883 [(reason_pos, ty_str)]
1884 | _ -> [] in
1885 Errors.uninstantiable_class c_usage_pos c_tc_pos c_name reason_msgl
1887 and exception_ty pos env ty =
1888 let exn_ty = Reason.Rthrow pos, Tclass ((pos, SN.Classes.cException), []) in
1889 Type.sub_type pos (Reason.URthrow) env ty exn_ty
1891 and shape_field_pos = function
1892 | Ast.SFlit (p, _) -> p
1893 | Ast.SFclass_const ((cls_pos, _), (mem_pos, _)) -> Pos.btw cls_pos mem_pos
1895 and check_shape_keys_validity env pos keys =
1896 (* If the key is a class constant, get its class name and type. *)
1897 let get_field_info env key =
1898 let key_pos = shape_field_pos key in
1899 (* Empty strings or literals that start with numbers are not
1900 permitted as shape field names. *)
1901 (match key with
1902 | Ast.SFlit (_, key_name) ->
1903 if (String.length key_name = 0) then
1904 (Errors.invalid_shape_field_name_empty key_pos)
1905 else if (key_name.[0] >= '0' && key_name.[0] <='9') then
1906 (Errors.invalid_shape_field_name_number key_pos);
1907 env, key_pos, None
1908 | Ast.SFclass_const (_, cls as x, y) ->
1909 let env, _te, ty = class_const env pos (CI (x, []), y) in
1910 let env = Typing_enum.check_valid_array_key_type
1911 Errors.invalid_shape_field_type ~allow_any:false
1912 env key_pos ty in
1913 env, key_pos, Some (cls, ty))
1916 let check_field witness_pos witness_info env key =
1917 let env, key_pos, key_info = get_field_info env key in
1918 (match witness_info, key_info with
1919 | Some _, None ->
1920 Errors.invalid_shape_field_literal key_pos witness_pos; env
1921 | None, Some _ ->
1922 Errors.invalid_shape_field_const key_pos witness_pos; env
1923 | None, None -> env
1924 | Some (cls1, ty1), Some (cls2, ty2) ->
1925 if cls1 <> cls2 then
1926 Errors.shape_field_class_mismatch
1927 key_pos witness_pos (strip_ns cls2) (strip_ns cls1);
1928 (* We want to use our own error message here instead of the normal
1929 * unification one. *)
1930 Errors.try_
1931 (fun () -> Unify.iunify env ty1 ty2)
1932 (fun _ ->
1933 Errors.shape_field_type_mismatch
1934 key_pos witness_pos
1935 (Typing_print.error (snd ty2)) (Typing_print.error (snd ty1));
1936 env))
1939 (* Sort the keys by their positions since the error messages will make
1940 * more sense if we take the one that appears first as canonical and if
1941 * they are processed in source order. *)
1942 let cmp_keys x y = Pos.compare (shape_field_pos x) (shape_field_pos y) in
1943 let keys = List.sort cmp_keys keys in
1945 match keys with
1946 | [] -> env
1947 | witness :: rest_keys ->
1948 let env, pos, info = get_field_info env witness in
1949 List.fold_left ~f:(check_field pos info) ~init:env rest_keys
1951 and check_valid_rvalue p env ty =
1952 let rec iter_over_types env tyl =
1953 match tyl with
1954 | [] ->
1955 env, ty
1957 | ty::tyl ->
1958 let env, ety = Env.expand_type env ty in
1959 match ety with
1960 | r, Tprim Tnoreturn ->
1961 Errors.noreturn_usage p
1962 (Reason.to_string "A noreturn function always throws or exits" r);
1963 env, (r, Terr)
1965 | r, Tprim Tvoid ->
1966 Errors.void_usage p
1967 (Reason.to_string "A void function doesn't return a value" r);
1968 env, (r, Terr)
1970 | _, Tunresolved tyl2 ->
1971 iter_over_types env (tyl2 @ tyl)
1973 | _, _ ->
1974 iter_over_types env tyl in
1975 iter_over_types env [ty]
1977 and set_valid_rvalue p env x ty =
1978 let env, ty = check_valid_rvalue p env ty in
1979 let env = Env.set_local env x ty in
1980 (* We are assigning a new value to the local variable, so we need to
1981 * generate a new expression id
1983 let env = Env.set_local_expr_id env x (Ident.tmp()) in
1984 env, ty
1986 (* Deal with assignment of a value of type ty2 to lvalue e1 *)
1987 and assign p env e1 ty2 : _ * T.expr * T.ty =
1988 let make_result env te1 ty1 = (env, T.make_typed_expr p ty1 te1, ty1) in
1989 match e1 with
1990 | (_, Lvar ((_, x) as id)) ->
1991 let env, ty1 = set_valid_rvalue p env x ty2 in
1992 make_result env (T.Lvar id) ty1
1993 | (_, Lplaceholder id) ->
1994 let placeholder_ty = Reason.Rplaceholder p, (Tprim Tvoid) in
1995 make_result env (T.Lplaceholder id) placeholder_ty
1996 | (_, List el) ->
1997 let env, folded_ty2 = TUtils.fold_unresolved env ty2 in
1998 let resl =
1999 try_over_concrete_supertypes env folded_ty2
2000 begin fun env ty2 ->
2001 match ty2 with
2002 (* Vector<t> or ImmVector<t> or ConstVector<t> or vec<T> *)
2003 | (_, Tclass ((_, x), [elt_type]))
2004 when x = SN.Collections.cVector
2005 || x = SN.Collections.cImmVector
2006 || x = SN.Collections.cVec
2007 || x = SN.Collections.cConstVector ->
2008 let env, tel = List.map_env env el begin fun env e ->
2009 let env, te, _ = assign (fst e) env e elt_type in
2010 env, te
2011 end in
2012 make_result env (T.List tel) ty2
2013 (* array<t> *)
2014 | (_, Tarraykind (AKvec elt_type)) ->
2015 let env, tel = List.map_env env el begin fun env e ->
2016 let env, te, _ = assign (fst e) env e elt_type in
2017 env, te
2018 end in
2019 make_result env (T.List tel) ty2
2020 (* array or empty array or Tany *)
2021 | (r, (Tarraykind (AKany | AKempty) | Tany)) ->
2022 let env, tel = List.map_env env el begin fun env e ->
2023 let env, te, _ = assign (fst e) env e (r, Tany) in
2024 env, te
2025 end in
2026 make_result env (T.List tel) ty2
2027 (* Pair<t1,t2> *)
2028 | ((r, Tclass ((_, coll), [ty1; ty2])) as folded_ety2)
2029 when coll = SN.Collections.cPair ->
2030 (match el with
2031 | [x1; x2] ->
2032 let env, te1, _ = assign p env x1 ty1 in
2033 let env, te2, _ = assign p env x2 ty2 in
2034 make_result env (T.List [te1; te2]) folded_ety2
2035 | _ ->
2036 Errors.pair_arity p;
2037 make_result env T.Any (r, Terr))
2038 (* tuple-like array *)
2039 | (r, Tarraykind (AKtuple fields)) ->
2040 let p1 = fst e1 in
2041 let p2 = Reason.to_pos r in
2042 let tyl = List.rev (IMap.values fields) in
2043 let size1 = List.length el in
2044 let size2 = List.length tyl in
2045 if size1 <> size2
2046 then begin
2047 Errors.tuple_arity p2 size2 p1 size1;
2048 make_result env T.Any (r, Terr)
2050 else
2051 let env, reversed_tel =
2052 List.fold2_exn el tyl ~f:begin fun (env,tel) lvalue ty2 ->
2053 let env, te, _ = assign p env lvalue ty2 in
2054 env, te::tel
2055 end ~init:(env,[]) in
2056 make_result env (T.List (List.rev reversed_tel)) ty2
2057 (* Other, including tuples. Create a tuple type for the left hand
2058 * side and attempt subtype against it. In particular this deals with
2059 * types such as (string,int) | (int,bool) *)
2060 | _ ->
2061 let env, tyl =
2062 List.map_env env el (fun env _ -> Env.fresh_unresolved_type env) in
2063 let env = Type.sub_type p Reason.URassign env folded_ty2
2064 (Reason.Rwitness (fst e1), Ttuple tyl) in
2065 let env, reversed_tel =
2066 List.fold2_exn el tyl ~init:(env,[]) ~f:(fun (env,tel) lvalue ty2 ->
2067 let env, te, _ = assign p env lvalue ty2 in
2068 env, te::tel) in
2069 make_result env (T.List (List.rev reversed_tel)) ty2
2070 end in
2071 begin match resl with
2072 | [res] -> res
2073 | _ -> assign_simple p env e1 ty2
2075 | _, Class_get _
2076 | _, Obj_get _ ->
2077 let lenv = env.Env.lenv in
2078 let no_fakes = LEnv.env_with_empty_fakes env in
2079 (* In this section, we check that the assignment is compatible with
2080 * the real type of a member. Remember that members can change
2081 * type (cf fake_members). But when we assign a value to $this->x,
2082 * we want to make sure that the type assign to $this->x is compatible
2083 * with the actual type hint. In this portion of the code, type-check
2084 * the assignment in an environment without fakes, and therefore
2085 * check that the assignment is compatible with the type of
2086 * the member.
2088 let env, _te1, real_type = lvalue no_fakes e1 in
2089 let env, exp_real_type = Env.expand_type env real_type in
2090 let env = { env with Env.lenv = lenv } in
2091 let env, ety2 = Env.expand_type env ty2 in
2092 let real_type_list =
2093 match exp_real_type with
2094 | _, Tunresolved tyl -> tyl
2095 | ty -> [ty]
2097 let env = List.fold_left real_type_list ~f:begin fun env real_type ->
2098 Type.sub_type p (Reason.URassign) env ety2 real_type
2099 end ~init:env in
2100 (match e1 with
2101 | _, Obj_get ((_, This | _, Lvar _ as obj),
2102 (_, Id (_, member_name)),
2103 _) ->
2104 let env, local = Env.FakeMembers.make p env obj member_name in
2105 let () = (match obj with
2106 | _, This ->
2107 Typing_suggest.save_member member_name env exp_real_type ty2
2108 | _ -> ()
2109 ) in
2110 let env, ty = set_valid_rvalue p env local ty2 in
2111 make_result env T.Any ty
2112 | _, Class_get (x, (_, y)) ->
2113 let env, local = Env.FakeMembers.make_static p env x y in
2114 let env, ty3 = set_valid_rvalue p env local ty2 in
2115 (match x with
2116 | CIself
2117 | CIstatic ->
2118 Typing_suggest.save_member y env exp_real_type ty2;
2119 | _ -> ());
2120 make_result env T.Any ty3
2121 | _ -> make_result env T.Any ty2
2123 | _, Array_get ((_, Lvar (_, lvar)) as shape, ((Some _) as e2)) ->
2124 let access_type = Typing_arrays.static_array_access env e2 in
2125 (* In the case of an assignment of the form $x['new_field'] = ...;
2126 * $x could be a shape where the field 'new_field' is not yet defined.
2127 * When that is the case we want to add the field to its type.
2129 let env, _te, shape_ty = expr env shape in
2130 let env, shape_ty = Typing_arrays.update_array_type_on_lvar_assignment
2131 p access_type env shape_ty in
2132 let env, _ = set_valid_rvalue p env lvar shape_ty in
2133 (* We still need to call assign_simple in order to bind the freshly
2134 * created variable in added shape field. Moreover, it's needed because
2135 * shape_ty could be more than just a shape. It could be an unresolved
2136 * type where some elements are shapes and some others are not.
2138 assign_simple p env e1 ty2
2139 | _, This ->
2140 Errors.this_lvalue p;
2141 make_result env T.Any (Reason.Rwitness p, Terr)
2142 | pref, Unop (Ast.Uref, e1') ->
2143 (* references can be "lvalues" in foreach bindings *)
2144 if Env.is_strict env then
2145 Errors.reference_expr pref;
2146 assign p env e1' ty2
2147 | _ ->
2148 assign_simple p env e1 ty2
2150 and assign_simple pos env e1 ty2 =
2151 let env, te1, ty1 = lvalue env e1 in
2152 let env, ty2 = check_valid_rvalue pos env ty2 in
2153 let env, ty2 = TUtils.unresolved env ty2 in
2154 let env = Type.sub_type pos (Reason.URassign) env ty2 ty1 in
2155 env, te1, ty2
2157 and array_field env = function
2158 | Nast.AFvalue ve ->
2159 let env, tve, tv = expr env ve in
2160 let env, tv = Typing_env.unbind env tv in
2161 env, (T.AFvalue tve, None, tv)
2162 | Nast.AFkvalue (ke, ve) ->
2163 let env, tke, tk = expr env ke in
2164 let env, tve, tv = expr env ve in
2165 let env, tv = Typing_env.unbind env tv in
2166 env, (T.AFkvalue (tke, tve), Some tk, tv)
2168 and array_value env x =
2169 let env, te, ty = expr env x in
2170 let env, ty = Typing_env.unbind env ty in
2171 env, (te, ty)
2173 and array_field_value env = function
2174 | Nast.AFvalue x
2175 | Nast.AFkvalue (_, x) ->
2176 array_value env x
2178 and array_field_key env = function
2179 (* This shouldn't happen *)
2180 | Nast.AFvalue (p, _) ->
2181 env, (T.make_implicitly_typed_expr p T.Any,
2182 (Reason.Rwitness p, Tprim Tint))
2183 | Nast.AFkvalue (x, _) ->
2184 array_value env x
2186 and akshape_field env = function
2187 | Nast.AFkvalue (k, v) ->
2188 let env, tek, tk = expr env k in
2189 let env, tk = Typing_env.unbind env tk in
2190 let env, tk = TUtils.unresolved env tk in
2191 let env, tev, tv = expr env v in
2192 let env, tv = Typing_env.unbind env tv in
2193 let env, tv = TUtils.unresolved env tv in
2194 let field_name =
2195 match TUtils.shape_field_name env Pos.none (snd k) with
2196 | Some field_name -> field_name
2197 | None -> assert false in (* Typing_arrays.is_shape_like_array
2198 * should have prevented this *)
2199 env, T.AFkvalue (tek, tev), (field_name, (tk, tv))
2200 | Nast.AFvalue _ -> assert false (* Typing_arrays.is_shape_like_array
2201 * should have prevented this *)
2203 and aktuple_afvalue env v =
2204 let env, tev, tv = expr env v in
2205 let env, tv = Typing_env.unbind env tv in
2206 let env, ty = TUtils.unresolved env tv in
2207 env, tev, ty
2209 and aktuple_field env = function
2210 | Nast.AFvalue v -> aktuple_afvalue env v
2211 | Nast.AFkvalue _ -> assert false (* check_consistent_fields
2212 * should have prevented this *)
2213 and check_parent_construct pos env el uel env_parent =
2214 let check_not_abstract = false in
2215 let env, env_parent = Phase.localize_with_self env env_parent in
2216 let env, _tcid, tel, tuel, parent =
2217 new_object ~check_not_abstract pos env CIparent el uel in
2218 let env, _ = Type.unify pos (Reason.URnone) env env_parent parent in
2219 env, tel, tuel, (Reason.Rwitness pos, Tprim Tvoid)
2221 and call_parent_construct pos env el uel =
2222 let parent = Env.get_parent env in
2223 match parent with
2224 | _, Tapply _ ->
2225 check_parent_construct pos env el uel parent
2226 | _,
2228 Tany
2229 | Tmixed
2230 | Tarray (_, _)
2231 | Tdarray (_, _)
2232 | Tvarray _
2233 | Tgeneric _
2234 | Toption _
2235 | Tprim _
2236 | Terr
2237 | Tfun _
2238 | Ttuple _
2239 | Tshape _
2240 | Taccess (_, _)
2241 | Tthis
2242 ) -> (* continue here *)
2243 let default = env, [], [], (Reason.Rnone, Tany) in
2244 match Env.get_self env with
2245 | _, Tclass ((_, self), _) ->
2246 (match Env.get_class env self with
2247 | Some ({tc_kind = Ast.Ctrait; _}
2248 as trait) ->
2249 (match trait_most_concrete_req_class trait env with
2250 | None -> Errors.parent_in_trait pos; default
2251 | Some (_, parent_ty) ->
2252 check_parent_construct pos env el uel parent_ty
2254 | Some self_tc ->
2255 if not self_tc.tc_members_fully_known
2256 then () (* Don't know the hierarchy, assume it's correct *)
2257 else Errors.undefined_parent pos;
2258 default
2259 | None -> assert false)
2260 | _, (Terr | Tany | Tmixed | Tarraykind _ | Toption _
2261 | Tprim _ | Tfun _ | Ttuple _ | Tshape _ | Tvar _
2262 | Tabstract (_, _) | Tanon (_, _) | Tunresolved _ | Tobject
2263 ) ->
2264 Errors.parent_outside_class pos;
2265 env, [], [], (Reason.Rnone, Terr)
2267 (* parent::method() in a class definition invokes the specific parent
2268 * version of the method ... it better be callable *)
2269 and check_abstract_parent_meth mname pos fty =
2270 if is_abstract_ft fty
2271 then Errors.parent_abstract_call mname pos (Reason.to_pos (fst fty));
2274 and is_abstract_ft fty = match fty with
2275 | _r, Tfun { ft_abstract = true; _ } -> true
2276 | _r, (Terr | Tany | Tmixed | Tarraykind _ | Toption _ | Tprim _
2277 | Tvar _ | Tfun _ | Tclass (_, _) | Tabstract (_, _) | Ttuple _
2278 | Tanon _ | Tunresolved _ | Tobject | Tshape _
2280 -> false
2282 (* Depending on the kind of expression we are dealing with
2283 * The typing of call is different.
2285 and dispatch_call p env call_type (fpos, fun_expr as e) el uel =
2286 let make_call env te tel tuel ty =
2287 env, T.make_typed_expr p ty (T.Call (call_type, te, tel, tuel)), ty in
2288 let make_call_special env id tel ty =
2289 make_call env (T.make_implicitly_typed_expr fpos (T.Id id)) tel [] ty in
2290 match fun_expr with
2291 (* Special function `echo` *)
2292 | Id ((_, pseudo_func) as id) when pseudo_func = SN.SpecialFunctions.echo ->
2293 let env, tel, _ = exprs env el in
2294 make_call_special env id tel (Reason.Rwitness p, Tprim Tvoid)
2295 (* Special function `empty` *)
2296 | Id ((_, pseudo_func) as id) when pseudo_func = SN.PseudoFunctions.empty ->
2297 let env, tel, _ = exprs env el in
2298 if uel <> [] then
2299 Errors.unpacking_disallowed_builtin_function p pseudo_func;
2300 if Env.is_strict env then
2301 Errors.empty_in_strict p;
2302 make_call_special env id tel (Reason.Rwitness p, Tprim Tbool)
2303 (* Special function `isset` *)
2304 | Id ((_, pseudo_func) as id) when pseudo_func = SN.PseudoFunctions.isset ->
2305 let env, tel, _ = exprs env el in
2306 if uel <> [] then
2307 Errors.unpacking_disallowed_builtin_function p pseudo_func;
2308 if Env.is_strict env then
2309 Errors.isset_in_strict p;
2310 make_call_special env id tel (Reason.Rwitness p, Tprim Tbool)
2311 (* Special function `unset` *)
2312 | Id ((_, pseudo_func) as id) when pseudo_func = SN.PseudoFunctions.unset ->
2313 let env, tel, _ = exprs env el in
2314 if uel <> [] then
2315 Errors.unpacking_disallowed_builtin_function p pseudo_func;
2316 let env = if Env.is_strict env then
2317 (match el, uel with
2318 | [(_, Array_get (ea, Some _))], [] ->
2319 let env, _te, ty = expr env ea in
2320 if List.exists ~f:(fun super -> SubType.is_sub_type env ty super) [
2321 (Reason.Rnone, (Tclass ((Pos.none, SN.Collections.cDict),
2322 [(Reason.Rnone, Tany); (Reason.Rnone, Tany)])));
2323 (Reason.Rnone, (Tclass ((Pos.none, SN.Collections.cKeyset),
2324 [(Reason.Rnone, Tany)])));
2325 (Reason.Rnone, Tarraykind AKany)
2326 ] then env
2327 else begin
2328 let env, (r, ety) = Env.expand_type env ty in
2329 Errors.unset_nonidx_in_strict
2331 (Reason.to_string ("This is " ^ Typing_print.error ety) r);
2334 | _ -> Errors.unset_nonidx_in_strict p []; env)
2335 else env in
2336 (match el with
2337 | [(p, Obj_get (_, _, OG_nullsafe))] ->
2338 begin
2339 Errors.nullsafe_property_write_context p;
2340 make_call_special env id tel (Reason.Rwitness p, Terr)
2341 end;
2342 | _ ->
2343 make_call_special env id tel (Reason.Rwitness p, Tprim Tvoid))
2344 (* Pseudo-function `get_called_class` *)
2345 | Id (cp, get_called_class) when
2346 get_called_class = SN.StdlibFunctions.get_called_class
2347 && el = [] && uel = [] ->
2348 (* get_called_class fetches the late-bound class *)
2349 if Env.is_outside_class env then Errors.static_outside_class p;
2350 class_const env p (CIstatic, (cp, SN.Members.mClass))
2351 (* Special function `array_filter` *)
2352 | Id ((_, array_filter) as id)
2353 when array_filter = SN.StdlibFunctions.array_filter && el <> [] && uel = [] ->
2354 (* dispatch the call to typecheck the arguments *)
2355 let env, fty = fun_type_of_id env id in
2356 let env, tel, tuel, res = call p env fty el uel in
2357 (* but ignore the result and overwrite it with custom return type *)
2358 let x = List.hd_exn el in
2359 let env, _tx, ty = expr env x in
2360 let explain_array_filter (r, t) =
2361 (Reason.Rarray_filter (p, r), t) in
2362 let get_value_type env tv =
2363 let env, tv =
2364 if List.length el > 1
2365 then env, tv
2366 else non_null env tv in
2367 env, explain_array_filter tv in
2368 let rec get_array_filter_return_type env ty =
2369 let env, ety = Env.expand_type env ty in
2370 (match ety with
2371 | (_, Tarraykind (AKany | AKempty)) as array_type ->
2372 env, array_type
2373 | (_, Tarraykind (AKtuple _)) ->
2374 let env, ty = Typing_arrays.downcast_aktypes env ty in
2375 get_array_filter_return_type env ty
2376 | (r, Tarraykind (AKvec tv)) ->
2377 let env, tv = get_value_type env tv in
2378 env, (r, Tarraykind (AKvec tv))
2379 | (r, Tunresolved x) ->
2380 let env, x = List.map_env env x get_array_filter_return_type in
2381 env, (r, Tunresolved x)
2382 | (r, Tany) ->
2383 env, (r, Tany)
2384 | (r, Terr) ->
2385 env, (r, Terr)
2386 | (r, _) ->
2387 let tk, tv = Env.fresh_type(), Env.fresh_type() in
2388 Errors.try_
2389 (fun () ->
2390 let keyed_container = (
2391 Reason.Rnone,
2392 Tclass (
2393 (Pos.none, SN.Collections.cKeyedContainer), [tk; tv]
2395 ) in
2396 let env = SubType.sub_type env ety keyed_container in
2397 let env, tv = get_value_type env tv in
2398 env, (r, Tarraykind (AKmap (
2399 (explain_array_filter tk),
2402 (fun _ -> Errors.try_
2403 (fun () ->
2404 let container = (
2405 Reason.Rnone,
2406 Tclass (
2407 (Pos.none, SN.Collections.cContainer), [tv]
2409 ) in
2410 let env = SubType.sub_type env ety container in
2411 let env, tv = get_value_type env tv in
2412 env, (r, Tarraykind (AKmap (
2413 (explain_array_filter (r, Tprim Tarraykey)),
2414 tv))))
2415 (fun _ -> env, res)))
2416 in let env, rty = get_array_filter_return_type env ty in
2417 make_call env (T.make_implicitly_typed_expr fpos (T.Id id)) tel tuel rty
2418 (* Special function `type_structure` *)
2419 | Id (p, type_structure)
2420 when type_structure = SN.StdlibFunctions.type_structure
2421 && (List.length el = 2) && uel = [] ->
2422 (match el with
2423 | [e1; e2] ->
2424 (match e2 with
2425 | p, Nast.String cst ->
2426 (* find the class constant implicitly defined by the typeconst *)
2427 let cid = (match e1 with
2428 | _, Class_const (cid, (_, x))
2429 | _, Class_get (cid, (_, x)) when x = SN.Members.mClass -> cid
2430 | _ -> Nast.CIexpr e1) in
2431 class_const ~incl_tc:true env p (cid, cst)
2432 | _ ->
2433 Errors.illegal_type_structure p "second argument is not a string";
2434 expr_error env Reason.Rnone)
2435 | _ -> assert false)
2436 (* Special function `array_map` *)
2437 | Id ((_, array_map) as x)
2438 when array_map = SN.StdlibFunctions.array_map && el <> [] && uel = [] ->
2439 let env, fty = fun_type_of_id env x in
2440 let env, fty = Env.expand_type env fty in
2441 let env, fty = match fty, el with
2442 | ((r_fty, Tfun fty), _::args) when args <> [] ->
2443 let arity = List.length args in
2445 Builds a function with signature:
2447 function<T1, ..., Tn, Tr>(
2448 (function(T1, ..., Tn):Tr),
2449 Container<T1>,
2450 ...,
2451 Container<Tn>,
2452 ): R
2454 where R is constructed by build_output_container applied to Tr
2456 let build_function build_output_container =
2457 let vars = List.map args (fun _ -> Env.fresh_type()) in
2458 let tr = Env.fresh_type() in
2459 let f = (None, (
2460 r_fty,
2461 Tfun {
2462 ft_pos = fty.ft_pos;
2463 ft_deprecated = None;
2464 ft_abstract = false;
2465 ft_arity = Fstandard (arity, arity);
2466 ft_tparams = [];
2467 ft_where_constraints = [];
2468 ft_params = List.map vars (fun x -> (None, x));
2469 ft_ret = tr;
2471 )) in
2472 let containers = List.map vars (fun var ->
2473 (None,
2474 (r_fty,
2475 Tclass ((fty.ft_pos, SN.Collections.cContainer), [var])
2478 ) in
2479 (r_fty, Tfun {fty with
2480 ft_arity = Fstandard (arity+1, arity+1);
2481 ft_params = f::containers;
2482 ft_ret = build_output_container tr;
2483 }) in
2486 Takes a Container type and returns a function that can "pack" a type
2487 into an array of appropriate shape, preserving the key type, i.e.:
2488 array -> f, where f R = array
2489 array<X> -> f, where f R = array<R>
2490 array<X, Y> -> f, where f R = array<X, R>
2491 Vector<X> -> f where f R = array<R>
2492 KeyedContainer<X, Y> -> f, where f R = array<X, R>
2493 Container<X> -> f, where f R = array<arraykey, R>
2494 X -> f, where f R = Y
2496 let rec build_output_container
2497 (env:Env.env) (x:locl ty) : (Env.env * (locl ty -> locl ty)) =
2498 let env, x = Env.expand_type env x in (match x with
2499 | (_, Tarraykind (AKany | AKempty)) as array_type ->
2500 env, (fun _ -> array_type)
2501 | (_, Tarraykind (AKtuple _ )) ->
2502 let env, x = Typing_arrays.downcast_aktypes env x in
2503 build_output_container env x
2504 | (r, Tarraykind AKvec _) ->
2505 env, (fun tr -> (r, Tarraykind (AKvec(tr))) )
2506 | ((_, Tany) as any) ->
2507 env, (fun _ -> any)
2508 | ((_, Terr) as err) ->
2509 env, (fun _ -> err)
2510 | (r, Tunresolved x) ->
2511 let env, x = List.map_env env x build_output_container in
2512 env, (fun tr -> (r, Tunresolved (List.map x (fun f -> f tr))))
2513 | (r, _) ->
2514 let tk, tv = Env.fresh_type(), Env.fresh_type() in
2515 let try_vector env =
2516 let vector = (
2517 r_fty,
2518 Tclass (
2519 (fty.ft_pos, SN.Collections.cConstVector), [tv]
2521 ) in
2522 let env = SubType.sub_type env x vector in
2523 env, (fun tr -> (r, Tarraykind (
2524 AKvec(tr)
2525 ))) in
2526 let try_keyed_container env =
2527 let keyed_container = (
2528 r_fty,
2529 Tclass (
2530 (fty.ft_pos, SN.Collections.cKeyedContainer), [tk; tv]
2532 ) in
2533 let env = SubType.sub_type env x keyed_container in
2534 env, (fun tr -> (r, Tarraykind (AKmap (
2537 )))) in
2538 let try_container env =
2539 let container = (
2540 r_fty,
2541 Tclass (
2542 (fty.ft_pos, SN.Collections.cContainer), [tv]
2544 ) in
2545 let env = SubType.sub_type env x container in
2546 env, (fun tr -> (r, Tarraykind (AKmap (
2547 (r, Tprim Tarraykey),
2548 tr)))) in
2549 Errors.try_
2550 (fun () ->
2551 try_vector env)
2552 (fun _ -> Errors.try_
2553 (fun () ->
2554 try_keyed_container env)
2555 (fun _ -> Errors.try_
2556 (fun () ->
2557 try_container env)
2558 (fun _ -> env, (fun _ -> (Reason.Rwitness p, Tany)))))) in
2560 Single argument calls preserve the key type, multi argument
2561 calls always return an array<Tr>
2563 (match args with
2564 | [x] ->
2565 let env, _tx, x = expr env x in
2566 let env, output_container = build_output_container env x in
2567 env, build_function output_container
2568 | _ ->
2569 env, build_function (fun tr ->
2570 (r_fty, Tarraykind (AKvec(tr)))))
2571 | _ -> env, fty in
2572 let env, tel, _tuel, ty = call p env fty el [] in
2573 make_call_special env x tel ty
2574 (* Special function `idx` *)
2575 | Id ((_, idx) as id) when idx = SN.FB.idx ->
2576 (* Directly call get_fun so that we can muck with the type before
2577 * instantiation -- much easier to work in terms of Tgeneric Tk/Tv than
2578 * trying to figure out which Tvar is which. *)
2579 (match Env.get_fun env (snd id) with
2580 | Some fty ->
2581 let param1, (name2, (r2, _)), (name3, (r3, _)) =
2582 match fty.ft_params with
2583 | [param1; param2; param3] -> param1, param2, param3
2584 | _ -> assert false in
2585 let params, ret = match List.length el with
2586 | 2 ->
2587 let param2 = (name2, (r2, Toption (r2, Tgeneric "Tk"))) in
2588 let rret = fst fty.ft_ret in
2589 let ret = (rret, Toption (rret, Tgeneric "Tv")) in
2590 [param1; param2], ret
2591 | 3 ->
2592 let param2 = (name2, (r2, Tgeneric "Tk")) in
2593 let param3 = (name3, (r3, Tgeneric "Tv")) in
2594 let ret = (fst fty.ft_ret, Tgeneric "Tv") in
2595 [param1; param2; param3], ret
2596 | _ -> fty.ft_params, fty.ft_ret in
2597 let fty = { fty with ft_params = params; ft_ret = ret } in
2598 let ety_env = Phase.env_with_self env in
2599 let env, fty = Phase.localize_ft ~ety_env env fty in
2600 let tfun = Reason.Rwitness fty.ft_pos, Tfun fty in
2601 let env, tel, _tuel, ty = call p env tfun el [] in
2602 make_call_special env id tel ty
2603 | None -> unbound_name env id)
2605 (* Special function `Shapes::idx` *)
2606 | Class_const (CI((_, shapes), _) as class_id, ((_, idx) as method_id))
2607 when shapes = SN.Shapes.cShapes && idx = SN.Shapes.idx ->
2608 overload_function p env class_id method_id el uel
2609 begin fun env fty res el -> match el with
2610 | [shape; field] ->
2611 let env, _ts, shape_ty = expr env shape in
2612 Typing_shapes.idx env fty shape_ty field None
2613 | [shape; field; default] ->
2614 let env, _ts, shape_ty = expr env shape in
2615 let env, _td, default_ty = expr env default in
2616 Typing_shapes.idx env fty shape_ty field
2617 (Some ((fst default), default_ty))
2618 | _ -> env, res
2620 (* Special function `Shapes::keyExists` *)
2621 | Class_const (CI((_, shapes), _) as class_id, ((_, key_exists) as method_id))
2622 when shapes = SN.Shapes.cShapes && key_exists = SN.Shapes.keyExists ->
2623 overload_function p env class_id method_id el uel
2624 begin fun env fty res el -> match el with
2625 | [shape; field] ->
2626 let env, _te, shape_ty = expr env shape in
2627 (* try accessing the field, to verify existence, but ignore
2628 * the returned type and keep the one coming from function
2629 * return type hint *)
2630 let env, _ = Typing_shapes.idx env fty shape_ty field None in
2631 env, res
2632 | _ -> env, res
2634 (* Special function `Shapes::removeKey` *)
2635 | Class_const (CI((_, shapes), _) as class_id, ((_, remove_key) as method_id))
2636 when shapes = SN.Shapes.cShapes && remove_key = SN.Shapes.removeKey ->
2637 overload_function p env class_id method_id el uel
2638 begin fun env _ res el -> match el with
2639 | [shape; field] -> begin match shape with
2640 | (_, Lvar (_, lvar)) ->
2641 let env, _te, shape_ty = expr env shape in
2642 let env, shape_ty =
2643 Typing_shapes.remove_key p env shape_ty field in
2644 let env, _ = set_valid_rvalue p env lvar shape_ty in
2645 env, res
2646 | _ ->
2647 Errors.invalid_shape_remove_key (fst shape);
2648 env, res
2650 | _ -> env, res
2652 (* Special function `Shapes::toArray` *)
2653 | Class_const (CI((_, shapes), _) as class_id, ((_, to_array) as method_id))
2654 when shapes = SN.Shapes.cShapes && to_array = SN.Shapes.toArray ->
2655 overload_function p env class_id method_id el uel
2656 begin fun env _ res el -> match el with
2657 | [shape] ->
2658 let env, _te, shape_ty = expr env shape in
2659 Typing_shapes.to_array env shape_ty res
2660 | _ -> env, res
2663 (* Special function `parent::__construct` *)
2664 | Class_const (CIparent, ((callee_pos, construct) as id))
2665 when construct = SN.Members.__construct ->
2666 Typing_hooks.dispatch_parent_construct_hook env callee_pos;
2667 let env, tel, tuel, ty = call_parent_construct p env el uel in
2668 make_call env (T.make_implicitly_typed_expr fpos
2669 (T.Class_const (T.CIparent, id))) tel tuel ty
2671 (* Calling parent method *)
2672 | Class_const (CIparent, m) ->
2673 let env, _te, ty1 = static_class_id p env CIparent in
2674 if Env.is_static env
2675 then begin
2676 (* in static context, you can only call parent::foo() on static
2677 * methods *)
2678 let env, fty, _ =
2679 class_get ~is_method:true ~is_const:false env ty1 m CIparent in
2680 let fty = check_abstract_parent_meth (snd m) p fty in
2681 let env, tel, tuel, ty = call p env fty el uel in
2682 make_call env (T.make_typed_expr fpos fty
2683 (T.Class_const (T.CIparent, m))) tel tuel ty
2685 else begin
2686 (* in instance context, you can call parent:foo() on static
2687 * methods as well as instance methods *)
2688 if not(class_contains_smethod env ty1 m)
2689 then
2690 (* parent::nonStaticFunc() is really weird. It's calling a method
2691 * defined on the parent class, but $this is still the child class.
2692 * We can deal with this by hijacking the continuation that
2693 * calculates the SN.Typehints.this type *)
2694 let this_ty = ExprDepTy.make env CIstatic
2695 (Reason.Rwitness fpos, TUtils.this_of (Env.get_self env)) in
2696 let k_lhs _ = this_ty in
2697 let env, method_, _ =
2698 obj_get_ ~is_method:true ~nullsafe:None env ty1 CIparent m
2699 begin fun (env, fty, _) ->
2700 let fty = check_abstract_parent_meth (snd m) p fty in
2701 let env, _tel, _tuel, method_ = call p env fty el uel in
2702 env, method_, None
2704 k_lhs
2706 make_call env (T.make_typed_expr fpos this_ty
2707 (T.Class_const (T.CIparent, m))) [] [] method_
2708 else
2709 let env, fty, _ =
2710 class_get ~is_method:true ~is_const:false env ty1 m CIparent in
2711 let fty = check_abstract_parent_meth (snd m) p fty in
2712 let env, tel, tuel, ty = call p env fty el uel in
2713 make_call env (T.make_typed_expr fpos fty
2714 (T.Class_const (T.CIparent, m))) tel tuel ty
2716 (* Call class method *)
2717 | Class_const(e1, m) ->
2718 TUtils.process_static_find_ref e1 m;
2719 let env, te1, ty1 = static_class_id p env e1 in
2720 let env, fty, _ =
2721 class_get ~is_method:true ~is_const:false env ty1 m e1 in
2722 let () = match e1 with
2723 | CIself when is_abstract_ft fty ->
2724 (match Env.get_self env with
2725 | _, Tclass ((_, self), _) ->
2726 (* at runtime, self:: in a trait is a call to whatever
2727 * self:: is in the context of the non-trait "use"-ing
2728 * the trait's code *)
2729 (match Env.get_class env self with
2730 | Some { tc_kind = Ast.Ctrait; _ } -> ()
2731 | _ -> Errors.self_abstract_call (snd m) p (Reason.to_pos (fst fty))
2733 | _ -> ())
2734 | CI (c, _) when is_abstract_ft fty ->
2735 Errors.classname_abstract_call (snd c) (snd m) p (Reason.to_pos (fst fty))
2736 | _ -> () in
2737 let env, tel, tuel, ty = call p env fty el uel in
2738 make_call env (T.make_typed_expr fpos fty
2739 (T.Class_const(te1, m))) tel tuel ty
2741 (* Call instance method *)
2742 | Obj_get(e1, (pos_id, Id m), nullflavor) ->
2743 let is_method = call_type = Cnormal in
2744 let env, te1, ty1 = expr env e1 in
2745 let nullsafe =
2746 (match nullflavor with
2747 | OG_nullthrows -> None
2748 | OG_nullsafe -> Some p
2749 ) in
2750 let fn = (fun (env, fty, _) ->
2751 let env, _tel, _tuel, method_ = call p env fty el uel in
2752 env, method_, None) in
2753 let env, ty = obj_get ~is_method ~nullsafe env ty1 (CIexpr e1) m fn in
2754 make_call env (T.make_implicitly_typed_expr fpos (T.Obj_get(te1,
2755 T.make_implicitly_typed_expr pos_id (T.Id m), nullflavor))) [] [] ty
2757 (* Function invocation *)
2758 | Fun_id x ->
2759 Typing_hooks.dispatch_id_hook x env;
2760 let env, fty = fun_type_of_id env x in
2761 let env, tel, tuel, ty = call p env fty el uel in
2762 make_call env (T.make_typed_expr fpos fty (T.Fun_id x)) tel tuel ty
2763 | Id x ->
2764 Typing_hooks.dispatch_id_hook x env;
2765 let env, fty = fun_type_of_id env x in
2766 let env, tel, tuel, ty = call p env fty el uel in
2767 make_call env (T.make_typed_expr fpos fty (T.Id x)) tel tuel ty
2768 | _ ->
2769 let env, te, fty = expr env e in
2770 let env, tel, tuel, ty = call p env fty el uel in
2771 make_call env te tel tuel ty
2773 and fun_type_of_id env x =
2774 Typing_hooks.dispatch_fun_id_hook x;
2775 let env, fty =
2776 match Env.get_fun env (snd x) with
2777 | None -> let env, _, ty = unbound_name env x in env, ty
2778 | Some fty ->
2779 let ety_env = Phase.env_with_self env in
2780 let env, fty = Phase.localize_ft ~ety_env env fty in
2781 env, (Reason.Rwitness fty.ft_pos, Tfun fty)
2783 env, fty
2785 (*****************************************************************************)
2786 (* Function type-checking expressions accessing an array (example: $x[...]).
2787 * The parameter is_lvalue is true when the expression is on the left hand
2788 * side of an assignment (example: $x[...] = 0).
2790 (*****************************************************************************)
2791 and array_get is_lvalue p env ty1 e2 ty2 =
2792 (* This is a little weird -- we enforce the right arity when you use certain
2793 * collections, even in partial mode (where normally completely omitting the
2794 * type parameter list is admitted). Basically the "omit type parameter"
2795 * hole was for compatibility with certain interfaces like ArrayAccess, not
2796 * for collections! But it's hard to go back on now, so since we've always
2797 * errored (with an inscrutable error message) when you try to actually use
2798 * a collection with omitted type parameters, we can continue to error and
2799 * give a more useful error message. *)
2800 let env, ety1 = Env.expand_type env ty1 in
2801 let arity_error (_, name) =
2802 Errors.array_get_arity p name (Reason.to_pos (fst ety1)) in
2803 match snd ety1 with
2804 | Tunresolved tyl ->
2805 let env, tyl = List.map_env env tyl begin fun env ty1 ->
2806 array_get is_lvalue p env ty1 e2 ty2
2807 end in
2808 env, (fst ety1, Tunresolved tyl)
2809 | Tarraykind (AKvarray ty | AKvec ty) ->
2810 let ty1 = Reason.Ridx (fst e2, fst ety1), Tprim Tint in
2811 let env = Type.sub_type p Reason.index_array env ty2 ty1 in
2812 env, ty
2813 | Tclass ((_, cn) as id, argl)
2814 when cn = SN.Collections.cVector
2815 || cn = SN.Collections.cVec ->
2816 let ty = match argl with
2817 | [ty] -> ty
2818 | _ -> arity_error id; err_witness p in
2819 let ty1 = Reason.Ridx_vector (fst e2), Tprim Tint in
2820 let env = Type.sub_type p (Reason.index_class cn) env ty2 ty1 in
2821 env, ty
2822 | Tclass ((_, cn) as id, argl)
2823 when cn = SN.Collections.cMap
2824 || cn = SN.Collections.cStableMap
2825 || cn = SN.Collections.cDict
2826 || cn = SN.Collections.cKeyset ->
2827 if cn = SN.Collections.cKeyset && is_lvalue then begin
2828 Errors.keyset_set p (Reason.to_pos (fst ety1));
2829 env, (Reason.Rwitness p, Terr)
2830 end else
2831 let (k, v) = match argl with
2832 | [t] when cn = SN.Collections.cKeyset -> (t, t)
2833 | [k; v] when cn <> SN.Collections.cKeyset -> (k, v)
2834 | _ ->
2835 arity_error id;
2836 let any = err_witness p in
2837 any, any
2839 let env, ty2 = TUtils.unresolved env ty2 in
2840 let env = Type.sub_type p (Reason.index_class cn) env ty2 k in
2841 env, v
2842 (* Certain container/collection types are intended to be immutable/const,
2843 * thus they should never appear as a lvalue when indexing i.e.
2845 * $x[0] = 100; // ERROR
2846 * $x[0]; // OK
2848 | Tclass ((_, cn) as id, argl)
2849 when cn = SN.Collections.cConstMap
2850 || cn = SN.Collections.cImmMap
2851 || cn = SN.Collections.cIndexish
2852 || cn = SN.Collections.cKeyedContainer ->
2853 if is_lvalue then
2854 error_const_mutation env p ety1
2855 else
2856 let (k, v) = match argl with
2857 | [k; v] -> (k, v)
2858 | _ ->
2859 arity_error id;
2860 let any = err_witness p in
2861 any, any
2863 let env = Type.sub_type p (Reason.index_class cn) env ty2 k in
2864 env, v
2865 | Tclass ((_, cn) as id, argl)
2866 when not is_lvalue &&
2867 (cn = SN.Collections.cConstVector || cn = SN.Collections.cImmVector) ->
2868 let ty = match argl with
2869 | [ty] -> ty
2870 | _ -> arity_error id; err_witness p in
2871 let ty1 = Reason.Ridx (fst e2, fst ety1), Tprim Tint in
2872 let env = Type.sub_type p (Reason.index_class cn) env ty2 ty1 in
2873 env, ty
2874 | Tclass ((_, cn), _)
2875 when is_lvalue &&
2876 (cn = SN.Collections.cConstVector || cn = SN.Collections.cImmVector) ->
2877 error_const_mutation env p ety1
2878 | Tarraykind (AKdarray (k, v) | AKmap (k, v)) ->
2879 let env, ty2 = TUtils.unresolved env ty2 in
2880 let env = Type.sub_type p Reason.index_array env ty2 k in
2881 env, v
2882 | Tarraykind ((AKshape _ | AKtuple _) as akind) ->
2883 let key = Typing_arrays.static_array_access env (Some e2) in
2884 let env, result = match key, akind with
2885 | Typing_arrays.AKtuple_index index, AKtuple fields ->
2886 begin match IMap.get index fields with
2887 | Some ty ->
2888 let ty1 = Reason.Ridx (fst e2, fst ety1), Tprim Tint in
2889 let env = Type.sub_type p Reason.index_array env ty2 ty1 in
2890 env, Some ty
2891 | None -> env, None
2893 | Typing_arrays.AKshape_key field_name, AKshape fdm ->
2894 begin match Nast.ShapeMap.get field_name fdm with
2895 | Some (k, v) ->
2896 let env, ty2 = TUtils.unresolved env ty2 in
2897 let env = Type.sub_type p Reason.index_array env ty2 k in
2898 env, Some v
2899 | None -> env, None
2901 | _ -> env, None in
2902 begin match result with
2903 | Some ty -> env, ty
2904 | None ->
2905 (* Key is dynamic, or static and not in the array - treat it as
2906 regular map or vec like array *)
2907 let env, ty1 = Typing_arrays.downcast_aktypes env ety1 in
2908 array_get is_lvalue p env ty1 e2 ty2
2910 | Terr -> env, (Reason.Rnone, Terr)
2911 | Tany | Tarraykind (AKany | AKempty) ->
2912 env, (Reason.Rnone, Tany)
2913 | Tprim Tstring ->
2914 let ty = Reason.Rwitness p, Tprim Tstring in
2915 let int = Reason.Ridx (fst e2, fst ety1), Tprim Tint in
2916 let env = Type.sub_type p Reason.index_array env ty2 int in
2917 env, ty
2918 | Ttuple tyl ->
2919 (match e2 with
2920 | p, Int n ->
2921 (try
2922 let idx = int_of_string (snd n) in
2923 let nth = List.nth_exn tyl idx in
2924 env, nth
2925 with _ ->
2926 Errors.typing_error p (Reason.string_of_ureason Reason.index_tuple);
2927 env, (Reason.Rwitness p, Terr)
2929 | p, _ ->
2930 Errors.typing_error p (Reason.string_of_ureason Reason.URtuple_access);
2931 env, (Reason.Rwitness p, Terr)
2933 | Tclass ((_, cn) as id, argl) when cn = SN.Collections.cPair ->
2934 let (ty1, ty2) = match argl with
2935 | [ty1; ty2] -> (ty1, ty2)
2936 | _ ->
2937 arity_error id;
2938 let any = err_witness p in
2939 any, any
2941 (match e2 with
2942 | p, Int n ->
2943 (try
2944 let idx = int_of_string (snd n) in
2945 let nth = List.nth_exn [ty1; ty2] idx in
2946 env, nth
2947 with _ ->
2948 Errors.typing_error p @@
2949 Reason.string_of_ureason (Reason.index_class cn);
2950 env, (Reason.Rwitness p, Terr)
2952 | p, _ ->
2953 Errors.typing_error p (Reason.string_of_ureason Reason.URpair_access);
2954 env, (Reason.Rwitness p, Terr)
2956 | Tshape (_, fdm) ->
2957 let p, e2' = e2 in
2958 (match TUtils.shape_field_name env p e2' with
2959 | None ->
2960 (* there was already an error in shape_field name,
2961 don't report another one for a missing field *)
2962 env, (Reason.Rwitness p, Terr)
2963 | Some field -> (match ShapeMap.get field fdm with
2964 | None ->
2965 Errors.undefined_field
2966 p (TUtils.get_printable_shape_field_name field);
2967 env, (Reason.Rwitness p, Terr)
2968 | Some { sft_optional = true; _ } ->
2969 let declared_field =
2970 List.find_exn
2971 ~f:(fun x -> Ast.ShapeField.compare field x = 0)
2972 (ShapeMap.keys fdm) in
2973 let declaration_pos = match declared_field with
2974 | Ast.SFlit (p, _) | Ast.SFclass_const ((p, _), _) -> p in
2975 Errors.array_get_with_optional_field
2977 declaration_pos
2978 (TUtils.get_printable_shape_field_name field);
2979 env, (Reason.Rwitness p, Terr)
2980 | Some { sft_optional = false; sft_ty } -> env, sft_ty)
2982 | Toption _ ->
2983 Errors.null_container p
2984 (Reason.to_string
2985 "This is what makes me believe it can be null"
2986 (fst ety1)
2988 env, (Reason.Rwitness p, Terr)
2989 | Tobject ->
2990 if Env.is_strict env
2991 then error_array env p ety1
2992 else env, (Reason.Rnone, Tany)
2993 | Tabstract (AKnewtype (ts, [ty]), Some (r, Tshape (fk, fields)))
2994 when ts = SN.FB.cTypeStructure ->
2995 let env, fields = TS.transform_shapemap env ty fields in
2996 let ty = r, Tshape (fk, fields) in
2997 array_get is_lvalue p env ty e2 ty2
2998 | Tabstract _ ->
2999 let resl =
3000 try_over_concrete_supertypes env ety1
3001 begin fun env ty ->
3002 array_get is_lvalue p env ty e2 ty2
3003 end in
3004 begin match resl with
3005 | [res] -> res
3006 | _ -> error_array env p ety1
3008 | Tmixed | Tprim _ | Tvar _ | Tfun _
3009 | Tclass (_, _) | Tanon (_, _) ->
3010 error_array env p ety1
3012 and array_append p env ty1 =
3013 let env, ty1 = TUtils.fold_unresolved env ty1 in
3014 let resl = try_over_concrete_supertypes env ty1
3015 begin fun env ty ->
3016 match ty with
3017 | (_, ty_) ->
3018 match ty_ with
3019 | Tany | Tarraykind (AKany | AKempty) ->
3020 env, (Reason.Rnone, Tany)
3022 | Terr ->
3023 env, (Reason.Rnone, Terr)
3025 | Tclass ((_, n), [ty])
3026 when n = SN.Collections.cVector
3027 || n = SN.Collections.cSet
3028 || n = SN.Collections.cVec
3029 || n = SN.Collections.cKeyset ->
3030 env, ty
3031 | Tclass ((_, n), [])
3032 when n = SN.Collections.cVector || n = SN.Collections.cSet ->
3033 (* Handle the case where "Vector" or "Set" was used as a typehint
3034 without type parameters *)
3035 env, (Reason.Rnone, Tany)
3036 | Tclass ((_, n), [tkey; tvalue]) when n = SN.Collections.cMap ->
3037 (* You can append a pair to a map *)
3038 env, (Reason.Rmap_append p, Tclass ((p, SN.Collections.cPair),
3039 [tkey; tvalue]))
3040 | Tclass ((_, n), []) when n = SN.Collections.cMap ->
3041 (* Handle the case where "Map" was used as a typehint without
3042 type parameters *)
3043 env, (Reason.Rmap_append p,
3044 Tclass ((p, SN.Collections.cPair), []))
3045 | Tarraykind (AKvec ty) ->
3046 env, ty
3047 | Tobject ->
3048 if Env.is_strict env
3049 then error_array_append env p ty1
3050 else env, (Reason.Rnone, Tany)
3051 | Tmixed | Tarraykind _ | Toption _ | Tprim _
3052 | Tvar _ | Tfun _ | Tclass (_, _) | Ttuple _
3053 | Tanon (_, _) | Tunresolved _ | Tshape _ | Tabstract _ ->
3054 error_array_append env p ty1
3055 end in
3056 match resl with
3057 | [res] -> res
3058 | _ -> error_array_append env p ty1
3061 and error_array env p (r, ty) =
3062 Errors.array_access p (Reason.to_pos r) (Typing_print.error ty);
3063 env, err_witness p
3065 and error_array_append env p (r, ty) =
3066 Errors.array_append p (Reason.to_pos r) (Typing_print.error ty);
3067 env, err_witness p
3069 and error_const_mutation env p (r, ty) =
3070 Errors.const_mutation p (Reason.to_pos r) (Typing_print.error ty);
3071 env, err_witness p
3074 * Checks if a class (given by cty) contains a given static method.
3076 * We could refactor this + class_get
3078 and class_contains_smethod env cty (_pos, mid) =
3079 let lookup_member ty =
3080 match ty with
3081 | _, Tclass ((_, c), _) ->
3082 (match Env.get_class env c with
3083 | None -> false
3084 | Some class_ ->
3085 Option.is_some @@ Env.get_static_member true env class_ mid
3087 | _ -> false in
3088 let _env, tyl = TUtils.get_concrete_supertypes env cty in
3089 List.exists tyl ~f:lookup_member
3091 and class_get ~is_method ~is_const ?(incl_tc=false) env cty (p, mid) cid =
3092 let env, this_ty =
3093 if is_method then
3094 this_for_method env cid cty
3095 else
3096 env, cty in
3097 let ety_env = {
3098 type_expansions = [];
3099 this_ty = this_ty;
3100 substs = SMap.empty;
3101 from_class = Some cid;
3102 } in
3103 class_get_ ~is_method ~is_const ~ety_env ~incl_tc env cid cty (p, mid)
3105 and class_get_ ~is_method ~is_const ~ety_env ?(incl_tc=false) env cid cty
3106 (p, mid) =
3107 let env, cty = Env.expand_type env cty in
3108 match cty with
3109 | _, Tany -> env, (Reason.Rnone, Tany), None
3110 | _, Terr -> env, err_none, None
3111 | _, Tunresolved tyl ->
3112 let env, tyl = List.map_env env tyl begin fun env ty ->
3113 let env, ty, _ =
3114 class_get_ ~is_method ~is_const ~ety_env ~incl_tc env cid ty (p, mid)
3115 in env, ty
3116 end in
3117 let env, method_ = TUtils.in_var env (fst cty, Tunresolved tyl) in
3118 env, method_, None
3120 | _, Tabstract _ ->
3121 begin match TUtils.get_concrete_supertypes env cty with
3122 | env, [cty] ->
3123 class_get_ ~is_method ~is_const ~ety_env ~incl_tc env cid cty (p, mid)
3124 | env, _ ->
3125 env, (Reason.Rnone, Tany), None
3127 | _, Tclass ((_, c), paraml) ->
3128 let class_ = Env.get_class env c in
3129 (match class_ with
3130 | None -> env, (Reason.Rnone, Tany), None
3131 | Some class_ ->
3132 Typing_hooks.dispatch_smethod_hook class_ paraml (p, mid) env
3133 ety_env.from_class ~is_method ~is_const;
3134 (* We need to instantiate generic parameters in the method signature *)
3135 let ety_env =
3136 { ety_env with
3137 substs = Subst.make class_.tc_tparams paraml } in
3138 if is_const then begin
3139 let const =
3140 if incl_tc then Env.get_const env class_ mid else
3141 match Env.get_typeconst env class_ mid with
3142 | Some _ ->
3143 Errors.illegal_typeconst_direct_access p;
3144 None
3145 | None ->
3146 Env.get_const env class_ mid
3148 match const with
3149 | None ->
3150 smember_not_found p ~is_const ~is_method class_ mid;
3151 env, (Reason.Rnone, Terr), None
3152 | Some { cc_type; cc_abstract; cc_pos; _ } ->
3153 let env, cc_type = Phase.localize ~ety_env env cc_type in
3154 env, cc_type,
3155 (if cc_abstract
3156 then Some (cc_pos, class_.tc_name ^ "::" ^ mid)
3157 else None)
3158 end else begin
3159 let smethod = Env.get_static_member is_method env class_ mid in
3160 match smethod with
3161 | None ->
3162 (match Env.get_static_member is_method env class_
3163 SN.Members.__callStatic with
3164 | None ->
3165 smember_not_found p ~is_const ~is_method class_ mid;
3166 env, (Reason.Rnone, Terr), None
3167 | Some {ce_visibility = vis; ce_type = lazy (r, Tfun ft); _} ->
3168 let p_vis = Reason.to_pos r in
3169 TVis.check_class_access p env (p_vis, vis) cid class_;
3170 let env, ft = Phase.localize_ft ~ety_env env ft in
3171 let ft = { ft with
3172 ft_arity = Fellipsis 0;
3173 ft_tparams = []; ft_params = [];
3174 } in
3175 env, (r, Tfun ft), None
3176 | _ -> assert false)
3177 | Some { ce_visibility = vis; ce_type = lazy method_; _ } ->
3178 let p_vis = Reason.to_pos (fst method_) in
3179 TVis.check_class_access p env (p_vis, vis) cid class_;
3180 let env, method_ =
3181 Phase.localize ~ety_env env method_ in
3182 env, method_, None
3185 | _, (Tmixed | Tarraykind _ | Toption _
3186 | Tprim _ | Tvar _ | Tfun _ | Ttuple _ | Tanon (_, _) | Tobject
3187 | Tshape _) ->
3188 (* should never happen; static_class_id takes care of these *)
3189 env, (Reason.Rnone, Tany), None
3191 and smember_not_found pos ~is_const ~is_method class_ member_name =
3192 let kind =
3193 if is_const then `class_constant
3194 else if is_method then `static_method
3195 else `class_variable in
3196 let error hint =
3197 let cid = (class_.tc_pos, class_.tc_name) in
3198 Errors.smember_not_found kind pos cid member_name hint
3200 match Env.suggest_static_member is_method class_ member_name with
3201 | None ->
3202 (match Env.suggest_member is_method class_ member_name with
3203 | None when not class_.tc_members_fully_known ->
3204 (* no error in this case ... the member might be present
3205 * in one of the parents of class_ that the typing cannot see *)
3207 | None ->
3208 error `no_hint
3209 | Some (pos2, v) ->
3210 error (`closest (pos2, v))
3212 | Some (pos2, v) ->
3213 error (`did_you_mean (pos2, v))
3215 and member_not_found pos ~is_method class_ member_name r =
3216 let kind = if is_method then `method_ else `member in
3217 let cid = class_.tc_pos, class_.tc_name in
3218 let reason = Reason.to_string
3219 ("This is why I think it is an object of type "^strip_ns class_.tc_name) r
3221 let error hint =
3222 Errors.member_not_found kind pos cid member_name hint reason in
3223 match Env.suggest_member is_method class_ member_name with
3224 | None ->
3225 (match Env.suggest_static_member is_method class_ member_name with
3226 | None when not class_.tc_members_fully_known ->
3227 (* no error in this case ... the member might be present
3228 * in one of the parents of class_ that the typing cannot see *)
3230 | None ->
3231 error `no_hint
3232 | Some (def_pos, v) ->
3233 error (`closest (def_pos, v))
3235 | Some (def_pos, v) ->
3236 error (`did_you_mean (def_pos, v))
3238 (* The type of the object member is passed into the continuation k. This is
3239 * useful for typing nullsafed method calls. Consider `$x?->f()`: obj_get will
3240 * pass `k` the type of f, and `k` will typecheck the method call and return
3241 * the method's return type. obj_get then wraps that type in a Toption. *)
3242 and obj_get ~is_method ~nullsafe env ty1 cid id k =
3243 let env =
3244 match nullsafe with
3245 | Some p when not (type_could_be_null env ty1) ->
3246 let env, (r, _) = Env.expand_type env ty1 in
3247 Errors.nullsafe_not_needed p
3248 (Reason.to_string
3249 "This is what makes me believe it cannot be null" r);
3251 | _ -> env in
3252 let env, method_, _ =
3253 obj_get_with_visibility ~is_method ~nullsafe env ty1 cid id k in
3254 env, method_
3256 and obj_get_with_visibility ~is_method ~nullsafe env ty1
3257 cid id k =
3258 obj_get_ ~is_method ~nullsafe env ty1 cid id k (fun ty -> ty)
3260 (* We know that the receiver is a concrete class: not a generic with
3261 * bounds, or a Tunresolved. *)
3262 and obj_get_concrete_ty ~is_method env concrete_ty class_id
3263 (id_pos, id_str as id) k_lhs =
3264 let default () = env, (Reason.Rnone, Tany), None in
3265 match concrete_ty with
3266 | (r, Tclass(x, paraml)) ->
3267 begin
3268 match Env.get_class env (snd x) with
3269 | None ->
3270 default ()
3272 | Some class_info when not is_method
3273 && not (Env.is_strict env)
3274 && class_info.tc_name = SN.Classes.cStdClass ->
3275 default ()
3277 | Some class_info ->
3278 let paraml =
3279 if List.length paraml = 0
3280 then List.map class_info.tc_tparams
3281 (fun _ -> Reason.Rwitness id_pos, Tany)
3282 else paraml in
3283 let member_info = Env.get_member is_method env class_info id_str in
3284 Typing_hooks.dispatch_cmethod_hook class_info paraml id env None
3285 ~is_method;
3287 match member_info with
3288 | None when not is_method ->
3289 if not (SN.Members.is_special_xhp_attribute id_str)
3290 then member_not_found id_pos ~is_method class_info id_str r;
3291 default ()
3293 | None ->
3294 begin
3295 match Env.get_member is_method env class_info SN.Members.__call with
3296 | None ->
3297 member_not_found id_pos ~is_method class_info id_str r;
3298 default ()
3300 | Some {ce_visibility = vis; ce_type = lazy (r, Tfun ft); _} ->
3301 let mem_pos = Reason.to_pos r in
3302 TVis.check_obj_access id_pos env (mem_pos, vis);
3304 (* the return type of __call can depend on the
3305 * class params or be this *)
3306 let this_ty = k_lhs (r, (Tclass(x, paraml))) in
3307 let ety_env = {
3308 type_expansions = [];
3309 this_ty = this_ty;
3310 substs = Subst.make class_info.tc_tparams paraml;
3311 from_class = Some class_id;
3312 } in
3313 let env, ft = Phase.localize_ft ~ety_env env ft in
3315 (* we change the params of the underlying
3316 * declaration to act as a variadic function
3317 * ... this transform cannot be done when
3318 * processing the declaration of call because
3319 * direct calls to $inst->__call are also
3320 * valid. *)
3321 let ft = {ft with
3322 ft_arity = Fellipsis 0; ft_tparams = []; ft_params = []; } in
3324 let member_ty = (r, Tfun ft) in
3325 env, member_ty, Some (mem_pos, vis)
3327 | _ -> assert false
3330 | Some ({ce_visibility = vis; ce_type = lazy member_; _ } as member_ce) ->
3331 let mem_pos = Reason.to_pos (fst member_) in
3332 TVis.check_obj_access id_pos env (mem_pos, vis);
3333 let member_ty = Typing_enum.member_type env member_ce in
3334 let this_ty = k_lhs (r, (Tclass(x, paraml))) in
3335 let ety_env = {
3336 type_expansions = [];
3337 this_ty = this_ty;
3338 substs = Subst.make class_info.tc_tparams paraml;
3339 from_class = Some class_id;
3340 } in
3341 let env, member_ty = Phase.localize ~ety_env env member_ty in
3342 env, member_ty, Some (mem_pos, vis)
3345 | _, Tobject
3346 | _, Tany
3347 | _, Terr ->
3348 default ()
3350 | _ ->
3351 Errors.non_object_member
3352 id_str id_pos (Typing_print.error (snd concrete_ty))
3353 (Reason.to_pos (fst concrete_ty));
3354 default ()
3357 (* k_lhs takes the type of the object receiver *)
3358 and obj_get_ ~is_method ~nullsafe env ty1 cid (id_pos, id_str as id) k k_lhs =
3359 let env, ety1 = Env.expand_type env ty1 in
3360 match ety1 with
3361 | _, Tunresolved tyl ->
3362 let (env, vis), tyl = List.map_env (env, None) tyl
3363 begin fun (env, vis) ty ->
3364 let env, ty, vis' =
3365 obj_get_ ~is_method ~nullsafe env ty cid id k k_lhs in
3366 (* There is one special case where we need to expose the
3367 * visibility outside of obj_get (checkout inst_meth special
3368 * function).
3369 * We keep a witness of the "most restrictive" visibility
3370 * we encountered (position + visibility), to be able to
3371 * special case inst_meth.
3373 let vis = TVis.min_vis_opt vis vis' in
3374 (env, vis), ty
3375 end in
3376 let env, method_ = TUtils.in_var env (fst ety1, Tunresolved (tyl)) in
3377 env, method_, vis
3379 | p', (Tabstract(ak, Some ty)) ->
3380 let k_lhs' ty = match ak with
3381 | AKnewtype (_, _) -> k_lhs ty
3382 | _ -> k_lhs (p', Tabstract (ak, Some ty)) in
3383 obj_get_ ~is_method ~nullsafe env ty cid id k k_lhs'
3385 | p', (Tabstract(ak,_)) ->
3386 let resl =
3387 try_over_concrete_supertypes env ety1
3388 (fun env ty ->
3389 (* We probably don't want to rewrap new types for the 'this' closure *)
3390 (* TODO AKENN: we shouldn't refine constraints by changing
3391 * the type like this *)
3392 let k_lhs' ty = match ak with
3393 | AKnewtype (_, _) -> k_lhs ty
3394 | _ -> k_lhs (p', Tabstract (ak, Some ty)) in
3395 obj_get_concrete_ty ~is_method env ty cid id k_lhs'
3396 ) in
3397 begin match resl with
3398 | [] -> begin
3399 Errors.non_object_member
3400 id_str id_pos (Typing_print.error (snd ety1))
3401 (Reason.to_pos (fst ety1));
3402 k (env, err_none, None)
3404 | ((_env, (_, ty), _vis) as res)::rest ->
3405 if List.exists rest (fun (_, (_,ty'), _) -> ty' <> ty)
3406 then
3407 begin
3408 Errors.ambiguous_member
3409 id_str id_pos (Typing_print.error (snd ety1))
3410 (Reason.to_pos (fst ety1));
3411 k (env, err_none, None)
3413 else k res
3416 | _, Toption ty -> begin match nullsafe with
3417 | Some p1 ->
3418 let k' (env, fty, x) = begin
3419 let env, method_, x = k (env, fty, x) in
3420 let env, method_ = non_null env method_ in
3421 env, (Reason.Rnullsafe_op p1, Toption method_), x
3422 end in
3423 obj_get_ ~is_method ~nullsafe env ty cid id k' k_lhs
3424 | None ->
3425 Errors.null_member id_str id_pos
3426 (Reason.to_string
3427 "This is what makes me believe it can be null"
3428 (fst ety1)
3430 k (env, (fst ety1, Terr), None)
3432 | _, _ ->
3433 k (obj_get_concrete_ty ~is_method env ety1 cid id k_lhs)
3436 (* Return true if the type ty1 contains the null value *)
3437 and type_could_be_null env ty1 =
3438 let _, tyl = TUtils.get_concrete_supertypes env ty1 in
3439 List.exists tyl
3440 (fun ety ->
3441 match snd ety with
3442 Toption _ | Tunresolved _ | Tmixed | Tany | Terr -> true
3443 | Tarraykind _ | Tprim _ | Tvar _ | Tfun _ | Tabstract _
3444 | Tclass (_, _) | Ttuple _ | Tanon (_, _) | Tobject
3445 | Tshape _ -> false)
3447 and class_id_for_new p env cid =
3448 let env, te, ty = static_class_id p env cid in
3449 (* Need to deal with union case *)
3450 let rec get_info res tyl =
3451 match tyl with
3452 | [] -> env, te, res
3453 | ty::tyl ->
3454 match snd ty with
3455 | Tunresolved tyl' ->
3456 get_info res (tyl' @ tyl)
3457 | _ ->
3458 (* Instantiation on an abstract class (e.g. from classname<T>) is
3459 * via the base type (to check constructor args), but the actual
3460 * type `ty` must be preserved. *)
3461 match TUtils.get_base_type env ty with
3462 | _, Tclass (sid, _) ->
3463 begin
3464 let class_ = Env.get_class env (snd sid) in
3465 match class_ with
3466 | None -> get_info res tyl
3467 | Some class_info -> get_info ((sid, class_info, ty)::res) tyl
3469 | _, (Tany | Terr | Tmixed | Tarraykind _ | Toption _ | Tprim _
3470 | Tvar _ | Tfun _ | Tabstract (_, _) | Ttuple _ | Tanon (_, _)
3471 | Tunresolved _ | Tobject | Tshape _) -> get_info res tyl in
3472 get_info [] [ty]
3474 (* To be a valid trait declaration, all of its 'require extends' must
3475 * match; since there's no multiple inheritance, it follows that all of
3476 * the 'require extends' must belong to the same inheritance hierarchy
3477 * and one of them should be the child of all the others *)
3478 and trait_most_concrete_req_class trait env =
3479 List.fold_left trait.tc_req_ancestors ~f:begin fun acc (_p, ty) ->
3480 let _r, (_p, name), _paraml = TUtils.unwrap_class_type ty in
3481 let keep = match acc with
3482 | Some (c, _ty) -> SMap.mem name c.tc_ancestors
3483 | None -> false
3485 if keep then acc
3486 else
3487 let class_ = Env.get_class env name in
3488 (match class_ with
3489 | None
3490 | Some { tc_kind = Ast.Cinterface; _ } -> acc
3491 | Some { tc_kind = Ast.Ctrait; _ } ->
3492 (* this is an error case for which the nastCheck spits out
3493 * an error, but does *not* currently remove the offending
3494 * 'require extends' or 'require implements' *)
3496 | Some c -> Some (c, ty)
3498 end ~init:None
3500 (* For explicit type arguments we support a wildcard syntax `_` for which
3501 * Hack will generate a fresh type variable
3503 and type_argument env hint =
3504 match hint with
3505 | (_, Happly((_, "_"), [])) ->
3506 Env.fresh_unresolved_type env
3507 | _ ->
3508 Phase.hint_locl env hint
3510 (* If there are no explicit type arguments then generate fresh type variables
3511 * for all of them. Otherwise, check the arity, and use the explicit types *)
3512 and type_arguments env p class_name tparams hintl =
3513 let default () = List.map_env env tparams begin fun env _ ->
3514 Env.fresh_unresolved_type env end in
3515 if hintl = []
3516 then default ()
3517 else if List.length hintl != List.length tparams
3518 then begin
3519 Errors.type_arity p class_name (string_of_int (List.length tparams));
3520 default ()
3522 else
3523 List.map_env env hintl type_argument
3525 (* When invoking a method the class_id is used to determine what class we
3526 * lookup the method in, but the type of 'this' will be the late bound type.
3527 * For example:
3529 * class C {
3530 * public static function get(): this { return new static(); }
3532 * public static function alias(): this { return self::get(); }
3535 * In C::alias, when we invoke self::get(), 'self' is resolved to the class
3536 * in the lexical scope (C), so call C::get. However the method is executed in
3537 * the current context, so static inside C::get will be resolved to the late
3538 * bound type (get_called_class() within C::alias).
3540 * This means when determining the type of this, CIparent and CIself should be
3541 * changed to CIstatic. For the other cases of C::get() or $c::get(), we only
3542 * look at the left hand side of the '::' and use the type type associated
3543 * with it.
3545 * Thus C::get() will return a type C, while $c::get() will return the same
3546 * type as $c.
3548 and this_for_method env cid default_ty = match cid with
3549 | CIparent | CIself | CIstatic ->
3550 let p = Reason.to_pos (fst default_ty) in
3551 let env, _te, ty = static_class_id p env CIstatic in
3552 env, ExprDepTy.make env CIstatic ty
3553 | _ ->
3554 env, default_ty
3556 and static_class_id p env =
3557 let make_result env te ty =
3558 env, te, ty in
3559 function
3560 | CIparent ->
3561 (match Env.get_self env with
3562 | _, Tclass ((_, self), _) ->
3563 (match Env.get_class env self with
3564 | Some (
3565 {tc_kind = Ast.Ctrait; _}
3566 as trait) ->
3567 (match trait_most_concrete_req_class trait env with
3568 | None ->
3569 Errors.parent_in_trait p;
3570 make_result env T.CIparent (Reason.Rwitness p, Terr)
3571 | Some (_, parent_ty) ->
3572 (* inside a trait, parent is SN.Typehints.this, but with the
3573 * type of the most concrete class that the trait has
3574 * "require extend"-ed *)
3575 let r = Reason.Rwitness p in
3576 let env, parent_ty = Phase.localize_with_self env parent_ty in
3577 make_result env T.CIparent (r, TUtils.this_of parent_ty)
3579 | _ ->
3580 let parent = Env.get_parent env in
3581 let parent_defined = snd parent <> Tany in
3582 if not parent_defined
3583 then Errors.parent_undefined p;
3584 let r = Reason.Rwitness p in
3585 let env, parent = Phase.localize_with_self env parent in
3586 (* parent is still technically the same object. *)
3587 make_result env T.CIparent (r, TUtils.this_of (r, snd parent))
3589 | _, (Terr | Tany | Tmixed | Tarraykind _ | Toption _ | Tprim _
3590 | Tfun _ | Ttuple _ | Tshape _ | Tvar _
3591 | Tanon (_, _) | Tunresolved _ | Tabstract (_, _) | Tobject
3592 ) ->
3593 let parent = Env.get_parent env in
3594 let parent_defined = snd parent <> Tany in
3595 if not parent_defined
3596 then Errors.parent_undefined p;
3597 let r = Reason.Rwitness p in
3598 let env, parent = Phase.localize_with_self env parent in
3599 (* parent is still technically the same object. *)
3600 make_result env T.CIparent (r, TUtils.this_of (r, snd parent))
3602 | CIstatic ->
3603 make_result env T.CIstatic
3604 (Reason.Rwitness p, TUtils.this_of (Env.get_self env))
3605 | CIself ->
3606 make_result env T.CIself
3607 (Reason.Rwitness p, snd (Env.get_self env))
3608 | CI (c, hl) ->
3609 let class_ = Env.get_class env (snd c) in
3610 (match class_ with
3611 | None ->
3612 make_result env (T.CI (c, hl)) (Reason.Rnone, Tany)
3613 | Some class_ ->
3614 let env, tyl = type_arguments env p (snd c) class_.tc_tparams hl in
3615 make_result env (T.CI (c, hl))
3616 (Reason.Rwitness (fst c), Tclass (c, tyl))
3618 | CIexpr (p, _ as e) ->
3619 let env, te, ty = expr env e in
3620 let rec resolve_ety ty =
3621 let env, ty = TUtils.fold_unresolved env ty in
3622 let _, ty = Env.expand_type env ty in
3623 match TUtils.get_base_type env ty with
3624 | _, Tabstract (AKnewtype (classname, [the_cls]), _) when
3625 classname = SN.Classes.cClassname -> resolve_ety the_cls
3626 | _, Tabstract (AKgeneric _, _)
3627 | _, Tclass _ -> ty
3628 | r, Tunresolved tyl -> r, Tunresolved (List.map tyl resolve_ety)
3629 | _, Tvar _ as ty -> resolve_ety ty
3630 | _, (Tany | Tprim Tstring | Tabstract (_, None) | Tmixed | Tobject)
3631 when not (Env.is_strict env) ->
3632 Reason.Rnone, Tany
3633 | _, (Terr | Tany | Tmixed | Tarraykind _ | Toption _
3634 | Tprim _ | Tfun _ | Ttuple _
3635 | Tabstract ((AKenum _ | AKdependent _ | AKnewtype _), _)
3636 | Tanon (_, _) | Tobject | Tshape _ as ty
3637 ) ->
3638 Errors.expected_class ~suffix:(", but got "^Typing_print.error ty) p;
3639 Reason.Rnone, Terr in
3640 let result_ty = resolve_ety ty in
3641 make_result env (T.CIexpr te) result_ty
3643 and call_construct p env class_ params el uel cid =
3644 let cid = if cid = CIparent then CIstatic else cid in
3645 let env, tcid, cid_ty = static_class_id p env cid in
3646 let ety_env = {
3647 type_expansions = [];
3648 this_ty = cid_ty;
3649 substs = Subst.make class_.tc_tparams params;
3650 from_class = Some cid;
3651 } in
3652 let env = Phase.check_tparams_constraints ~ety_env env class_.tc_tparams in
3653 if SSet.mem "XHP" class_.tc_extends then env, tcid, [], [] else
3654 let cstr = Env.get_construct env class_ in
3655 let mode = Env.get_mode env in
3656 Typing_hooks.dispatch_constructor_hook class_ params env p;
3657 match (fst cstr) with
3658 | None ->
3659 if el <> [] &&
3660 (mode = FileInfo.Mstrict || mode = FileInfo.Mpartial) &&
3661 class_.tc_members_fully_known
3662 then Errors.constructor_no_args p;
3663 let env, tel, _tyl = exprs env el in
3664 env, tcid, tel, []
3665 | Some { ce_visibility = vis; ce_type = lazy m; _ } ->
3666 TVis.check_obj_access p env (Reason.to_pos (fst m), vis);
3667 let env, m = Phase.localize ~ety_env env m in
3668 let env, tel, tuel, _ty = call p env m el uel in
3669 env, tcid, tel, tuel
3671 and check_arity ?(check_min=true) pos pos_def (arity:int) exp_arity =
3672 let exp_min = (Typing_defs.arity_min exp_arity) in
3673 if check_min && arity < exp_min then
3674 Errors.typing_too_few_args pos pos_def;
3675 match exp_arity with
3676 | Fstandard (_, exp_max) ->
3677 if (arity > exp_max)
3678 then Errors.typing_too_many_args pos pos_def;
3679 | Fvariadic _ | Fellipsis _ -> ()
3681 and check_deprecated p { ft_pos; ft_deprecated; _ } =
3682 match ft_deprecated with
3683 | Some s -> Errors.deprecated_use p ft_pos s
3684 | None -> ()
3686 (* The variadic capture argument is an array listing the passed
3687 * variable arguments for the purposes of the function body; callsites
3688 * should not unify with it *)
3689 and variadic_param env ft =
3690 match ft.ft_arity with
3691 | Fvariadic (_, p_ty) -> env, Some p_ty
3692 | Fellipsis _ | Fstandard _ -> env, None
3694 and call pos env fty el uel =
3695 let env, tel, tuel, ty = call_ pos env fty el uel in
3696 (* We need to solve the constraints after every single function call.
3697 * The type-checker is control-flow sensitive, the same value could
3698 * have different type depending on the branch that we are in.
3699 * When this is the case, a call could violate one of the constraints
3700 * in a branch. *)
3701 let env = fold_fun_list env env.Env.todo in
3702 env, tel, tuel, ty
3704 (* Enforces that e is unpackable. If e is a tuple, appends its unpacked types
3705 * into the e_tyl returned.
3707 and unpack_expr env e_tyl e =
3708 let env, _te, ety = expr env e in
3709 (match ety with
3710 | _, Ttuple tyl ->
3711 (* Todo: Check that tuples are allowed - that is, disallow a tuple
3712 * unpacking after an array unpacking.
3714 let unpacked_e_tyl = List.map tyl (fun ty -> e, ty) in
3715 env, e_tyl @ unpacked_e_tyl, true
3716 | _ ->
3717 let pos = fst e in
3718 let unpack_r = Reason.Runpack_param pos in
3719 let container_ty = (unpack_r, Tclass ((pos, SN.Collections.cContainer),
3720 [unpack_r, Tany])) in
3721 let env = Type.sub_type pos Reason.URparam env ety container_ty in
3722 env, e_tyl, false
3725 (* Unpacks uel. If tuples are found, unpacked types are appended to the
3726 * e_tyl returned.
3728 and unpack_exprl env e_tyl uel =
3729 List.fold_left uel ~init:(env, e_tyl, false)
3730 ~f: begin fun (env, e_tyl, unpacked_tuple) e ->
3731 let env, e_tyl, is_tuple = unpack_expr env e_tyl e in
3732 (env, e_tyl, is_tuple || unpacked_tuple)
3735 and call_ pos env fty el uel =
3736 let env, efty = Env.expand_type env fty in
3737 (match efty with
3738 | _, (Terr | Tany | Tunresolved []) ->
3739 let el = el @ uel in
3740 let env, tel = List.map_env env el begin fun env elt ->
3741 let env, te, arg_ty = expr env elt in
3742 let env, _arg_ty = check_valid_rvalue pos env arg_ty in
3743 env, te
3744 end in
3745 Typing_hooks.dispatch_fun_call_hooks [] (List.map (el @ uel) fst) env;
3746 env, tel, [], (Reason.Rnone, Tany)
3747 | r, Tunresolved tyl ->
3748 let env, retl = List.map_env env tyl begin fun env ty ->
3749 let env, _, _, ty = call pos env ty el uel in env, ty
3750 end in
3751 let env, ty = TUtils.in_var env (r, Tunresolved retl) in
3752 env, [], [], ty
3753 | r2, Tfun ft ->
3754 (* Typing of format string functions. It is dependent on the arguments (el)
3755 * so it cannot be done earlier.
3757 let env, ft = Typing_exts.retype_magic_func env ft el in
3758 check_deprecated pos ft;
3759 let pos_def = Reason.to_pos r2 in
3760 let env, var_param = variadic_param env ft in
3761 let env, e_tyl = List.map_env env el begin fun env e ->
3762 let env, _te, ty = expr env e in
3763 env, (e, ty)
3764 end in
3765 let env, e_tyl, unpacked_tuple = unpack_exprl env e_tyl uel in
3766 let arity = if unpacked_tuple
3767 then List.length e_tyl
3768 (* Each array unpacked corresponds with at least 1 param. *)
3769 else List.length el + List.length uel in
3770 (* If we unpacked an array, we don't check arity exactly. Since each
3771 * unpacked array consumes 1 or many parameters, it is nonsensical to say
3772 * that not enough args were passed in (so we don't do the min check).
3774 let () = check_arity ~check_min:(uel = [] || unpacked_tuple)
3775 pos pos_def arity ft.ft_arity in
3776 let todos = ref [] in
3777 let env = wfold_left_default (call_param todos) (env, var_param)
3778 ft.ft_params e_tyl in
3779 let env = fold_fun_list env !todos in
3780 Typing_hooks.dispatch_fun_call_hooks
3781 ft.ft_params (List.map (el @ uel) fst) env;
3782 env, [], [], ft.ft_ret
3783 | r2, Tanon (arity, id) when uel = [] ->
3784 let env, tel, tyl = exprs env el in
3785 let anon = Env.get_anonymous env id in
3786 let fpos = Reason.to_pos r2 in
3787 (match anon with
3788 | None ->
3789 Errors.anonymous_recursive_call pos;
3790 env, tel, [], err_none
3791 | Some anon ->
3792 let () = check_arity pos fpos (List.length tyl) arity in
3793 let tyl = List.map tyl (fun x -> None, x) in
3794 let env, ty = anon env tyl in
3795 env, tel, [], ty)
3796 | _, Tarraykind _ when not (Env.is_strict env) ->
3797 (* Relaxing call_user_func to work with an array in partial mode *)
3798 env, [], [], (Reason.Rnone, Tany)
3799 | _, ty ->
3800 bad_call pos ty;
3801 env, [], [], err_none
3804 and call_param todos env (name, x) ((pos, _ as e), arg_ty) =
3805 (match name with
3806 | None -> ()
3807 | Some name -> Typing_suggest.save_param name env x arg_ty
3809 let env, arg_ty = check_valid_rvalue pos env arg_ty in
3811 (* When checking params the type 'x' may be expression dependent. Since
3812 * we store the expression id in the local env for Lvar, we want to apply
3813 * it in this case.
3815 let dep_ty = match snd e with
3816 | Lvar _ -> ExprDepTy.make env (CIexpr e) arg_ty
3817 | _ -> arg_ty in
3818 (* We solve for Tanon types after all the other params because we want to
3819 * typecheck the lambda bodies with as much type information as possible. For
3820 * example, in array_map(fn, x), we might be able to use the type of x to
3821 * infer the type of fn, but if we call sub_type on fn first, we end up
3822 * typechecking its body without the benefit of knowing its full type. If
3823 * fn is typehinted but not x, we could use fn to infer the type of x, but
3824 * in practice the reverse situation is more likely. This rearrangement is
3825 * particularly useful since higher-order functions usually put fn before x.
3827 match arg_ty with
3828 | _, Tanon _ ->
3829 todos := (fun env ->
3830 Type.sub_type pos Reason.URparam env arg_ty x) :: !todos;
3832 | _, (Terr | Tany | Tmixed | Tarraykind _ | Toption _ | Tprim _
3833 | Tvar _ | Tfun _ | Tabstract (_, _) | Tclass (_, _) | Ttuple _
3834 | Tunresolved _ | Tobject | Tshape _) ->
3835 Type.sub_type pos Reason.URparam env dep_ty x
3837 and bad_call p ty =
3838 Errors.bad_call p (Typing_print.error ty)
3840 and unop p env uop te ty =
3841 let make_result env te result_ty =
3842 env, T.make_typed_expr p result_ty (T.Unop(uop, te)), result_ty in
3843 match uop with
3844 | Ast.Unot ->
3845 Async.enforce_not_awaitable env p ty;
3846 (* !$x (logical not) works with any type, so we just return Tbool *)
3847 make_result env te (Reason.Rlogic_ret p, Tprim Tbool)
3848 | Ast.Utild ->
3849 (* ~$x (bitwise not) only works with int *)
3850 let env = Type.sub_type p Reason.URnone env ty
3851 (Reason.Rarith p, Tprim Tint) in
3852 make_result env te (Reason.Rarith p, Tprim Tint)
3853 | Ast.Uincr
3854 | Ast.Upincr
3855 | Ast.Updecr
3856 | Ast.Udecr
3857 | Ast.Uplus
3858 | Ast.Uminus ->
3859 (* math operators work with int or floats, so we call sub_type *)
3860 let env = Type.sub_type p Reason.URnone env ty
3861 (Reason.Rarith p, Tprim Tnum) in
3862 make_result env te ty
3863 | Ast.Uref ->
3864 (* We basically just ignore references in non-strict files *)
3865 if Env.is_strict env then
3866 Errors.reference_expr p;
3867 make_result env te ty
3869 and binop in_cond p env bop p1 te1 ty1 p2 te2 ty2 =
3870 let rec is_any ty =
3871 match Env.expand_type env ty with
3872 | (_, (_, (Tany | Terr))) -> true
3873 | (_, (_, Tunresolved tyl)) -> List.for_all tyl is_any
3874 | _ -> false in
3875 (* Test if `ty` is *not* the any type (or a variant thereof) and
3876 * is a subtype of the primitive type `prim`. *)
3877 let is_sub_prim env ty prim =
3878 let ty_prim = (Reason.Rarith p, Tprim prim) in
3879 if not (is_any ty) && SubType.is_sub_type env ty ty_prim
3880 then Some (fst ty) else None in
3881 (* Test if `ty` is *not* the any type (or a variant thereof) and
3882 * is a subtype of `num` but is not a subtype of `int` *)
3883 let is_sub_num_not_sub_int env ty =
3884 let ty_num = (Reason.Rarith p, Tprim Tnum) in
3885 let ty_int = (Reason.Rarith p, Tprim Tint) in
3886 if not (is_any ty) && SubType.is_sub_type env ty ty_num
3887 && not (SubType.is_sub_type env ty ty_int)
3888 then Some (fst ty) else None in
3889 (* Force ty1 to be a subtype of ty2 (unless it is any) *)
3890 let enforce_sub_ty env ty1 ty2 =
3891 let env = Type.sub_type p Reason.URnone env ty1 ty2 in
3892 Env.expand_type env ty1 in
3893 let make_result env te1 te2 ty =
3894 env, T.make_typed_expr p ty (T.Binop(bop, te1, te2)), ty in
3895 match bop with
3896 | Ast.Plus ->
3897 let env, ty1 = TUtils.fold_unresolved env ty1 in
3898 let env, ty2 = TUtils.fold_unresolved env ty2 in
3899 let env, ety1 = Env.expand_type env ty1 in
3900 let env, ety2 = Env.expand_type env ty2 in
3901 (match ety1, ety2 with
3902 (* For array<V1>+array<V2> and array<K1,V1>+array<K2,V2>, allow
3903 * the addition to produce a supertype. (We could also handle
3904 * when they have mismatching annotations, but we get better error
3905 * messages if we just let those get unified in the next case. *)
3906 (* The general types are:
3907 * function<Tk,Tv>(array<Tk,Tv>, array<Tk,Tv>): array<Tk,Tv>
3908 * function<T>(array<T>, array<T>): array<T>
3909 * and subtyping on the arguments deals with everything
3911 | (_, Tarraykind (AKmap _ as ak)), (_, Tarraykind (AKmap _))
3912 | (_, Tarraykind (AKvec _ as ak)), (_, Tarraykind (AKvec _)) ->
3913 let env, a_sup = Env.fresh_unresolved_type env in
3914 let env, b_sup = Env.fresh_unresolved_type env in
3915 let res_ty = Reason.Rarray_plus_ret p, Tarraykind (
3916 match ak with
3917 | AKvec _ -> AKvec a_sup
3918 | AKmap _ -> AKmap (a_sup, b_sup)
3919 | _ -> assert false
3920 ) in
3921 let env = Type.sub_type p1 Reason.URnone env ety1 res_ty in
3922 let env = Type.sub_type p2 Reason.URnone env ety2 res_ty in
3923 make_result env te1 te2 res_ty
3924 | (_, Tarraykind _), (_, Tarraykind (AKshape _)) ->
3925 let env, ty2 = Typing_arrays.downcast_aktypes env ty2 in
3926 binop in_cond p env bop p1 te1 ty1 p2 te2 ty2
3927 | (_, Tarraykind (AKshape _)), (_, Tarraykind _) ->
3928 let env, ty1 = Typing_arrays.downcast_aktypes env ty1 in
3929 binop in_cond p env bop p1 te1 ty1 p2 te2 ty2
3930 | (_, Tarraykind _), (_, Tarraykind _)
3931 | (_, (Tany | Terr)), (_, Tarraykind _)
3932 | (_, Tarraykind _), (_, Tany) ->
3933 let env, ty = Type.unify p Reason.URnone env ty1 ty2 in
3934 make_result env te1 te2 ty
3935 | (_, (Tany | Terr | Tmixed | Tarraykind _ | Toption _
3936 | Tprim _ | Tvar _ | Tfun _ | Tabstract (_, _) | Tclass (_, _)
3937 | Ttuple _ | Tanon (_, _) | Tunresolved _ | Tobject | Tshape _
3939 ), _ -> binop in_cond p env Ast.Minus p1 te1 ty1 p2 te2 ty2
3941 | Ast.Minus | Ast.Star ->
3942 begin
3943 let env, ty1 = enforce_sub_ty env ty1 (Reason.Rarith p1, Tprim Tnum) in
3944 let env, ty2 = enforce_sub_ty env ty2 (Reason.Rarith p2, Tprim Tnum) in
3945 (* If either side is a float then float: 1.0 - 1 -> float *)
3946 (* These have types
3947 * function(float, num): float
3948 * function(num, float): float
3950 match is_sub_prim env ty1 Tfloat, is_sub_prim env ty2 Tfloat with
3951 | (Some r, _) | (_, Some r) ->
3952 make_result env te1 te2 (r, Tprim Tfloat)
3953 | _, _ ->
3954 (* Both sides are integers, then integer: 1 - 1 -> int *)
3955 (* This has type
3956 * function(int, int): int
3958 match is_sub_prim env ty1 Tint, is_sub_prim env ty2 Tint with
3959 | (Some _, Some _) ->
3960 make_result env te1 te2 (Reason.Rarith_ret p, Tprim Tint)
3961 | _, _ ->
3962 (* Either side is a non-int num then num *)
3963 (* This has type
3964 * function(num, num): num
3966 match is_sub_num_not_sub_int env ty1,
3967 is_sub_num_not_sub_int env ty2 with
3968 | (Some r, _) | (_, Some r) ->
3969 make_result env te1 te2 (r, Tprim Tnum)
3970 (* Otherwise? *)
3971 | _, _ -> env, T.make_typed_expr p ty1 T.Any, ty1
3973 | Ast.Slash | Ast.Starstar ->
3974 begin
3975 let env, ty1 = enforce_sub_ty env ty1 (Reason.Rarith p1, Tprim Tnum) in
3976 let env, ty2 = enforce_sub_ty env ty2 (Reason.Rarith p2, Tprim Tnum) in
3977 (* If either side is a float then float *)
3978 (* These have types
3979 * function(float, num) : float
3980 * function(num, float) : float
3981 * [Actually, for division result can be false if second arg is zero]
3983 match is_sub_prim env ty1 Tfloat, is_sub_prim env ty2 Tfloat with
3984 | (Some r, _) | (_, Some r) ->
3985 make_result env te1 te2 (r, Tprim Tfloat)
3986 (* Otherwise it has type
3987 * function(num, num) : num
3988 * [Actually, for division result can be false if second arg is zero]
3990 | _, _ ->
3991 let r = match bop with
3992 | Ast.Slash -> Reason.Rret_div p
3993 | _ -> Reason.Rarith_ret p in
3994 make_result env te1 te2 (r, Tprim Tnum)
3996 | Ast.Percent ->
3997 (* Integer remainder function has type
3998 * function(int, int) : int
3999 * [Actually, result can be false if second arg is zero]
4001 let env, _ = enforce_sub_ty env ty1 (Reason.Rarith p1, Tprim Tint) in
4002 let env, _ = enforce_sub_ty env ty2 (Reason.Rarith p1, Tprim Tint) in
4003 make_result env te1 te2 (Reason.Rarith_ret p, Tprim Tint)
4004 | Ast.Xor ->
4005 begin
4006 match is_sub_prim env ty1 Tbool, is_sub_prim env ty2 Tbool with
4007 | (Some _, _) | (_, Some _) ->
4008 (* Logical xor:
4009 * function(bool, bool) : bool
4011 let env, _ =
4012 enforce_sub_ty env ty1 (Reason.Rlogic_ret p1, Tprim Tbool) in
4013 let env, _ =
4014 enforce_sub_ty env ty2 (Reason.Rlogic_ret p1, Tprim Tbool) in
4015 make_result env te1 te2 (Reason.Rlogic_ret p, Tprim Tbool)
4016 | _, _ ->
4017 (* Arithmetic xor:
4018 * function(int, int) : int
4020 let env, _ = enforce_sub_ty env ty1 (Reason.Rarith p1, Tprim Tint) in
4021 let env, _ = enforce_sub_ty env ty2 (Reason.Rarith p1, Tprim Tint) in
4022 make_result env te1 te2 (Reason.Rarith_ret p, Tprim Tint)
4024 (* Equality and disequality:
4025 * function<T>(T, T): bool
4027 | Ast.Eqeq | Ast.Diff ->
4028 make_result env te1 te2 (Reason.Rcomp p, Tprim Tbool)
4029 | Ast.EQeqeq | Ast.Diff2 ->
4030 if not in_cond
4031 then Typing_equality_check.assert_nontrivial p bop env ty1 ty2;
4032 make_result env te1 te2 (Reason.Rcomp p, Tprim Tbool)
4033 | Ast.Lt | Ast.Lte | Ast.Gt | Ast.Gte ->
4034 let ty_num = (Reason.Rcomp p, Tprim Nast.Tnum) in
4035 let ty_string = (Reason.Rcomp p, Tprim Nast.Tstring) in
4036 let ty_datetime =
4037 (Reason.Rcomp p, Tclass ((p, SN.Classes.cDateTime), [])) in
4038 let both_sub ty =
4039 SubType.is_sub_type env ty1 ty && SubType.is_sub_type env ty2 ty in
4040 (* So we have three different types here:
4041 * function(num, num): bool
4042 * function(string, string): bool
4043 * function(DateTime, DateTime): bool
4045 if both_sub ty_num || both_sub ty_string || both_sub ty_datetime
4046 then make_result env te1 te2 (Reason.Rcomp p, Tprim Tbool)
4047 else
4048 (* TODO this is questionable; PHP's semantics for conversions with "<"
4049 * are pretty crazy and we may want to just disallow this? *)
4050 (* This is universal:
4051 * function<T>(T, T): bool
4053 let env, _ = Type.unify p Reason.URnone env ty1 ty2 in
4054 make_result env te1 te2 (Reason.Rcomp p, Tprim Tbool)
4055 | Ast.Dot ->
4056 (* A bit weird, this one:
4057 * function(Stringish | string, Stringish | string) : string)
4059 let env = SubType.sub_string p1 env ty1 in
4060 let env = SubType.sub_string p2 env ty2 in
4061 make_result env te1 te2 (Reason.Rconcat_ret p, Tprim Tstring)
4062 | Ast.AMpamp
4063 | Ast.BArbar ->
4064 make_result env te1 te2 (Reason.Rlogic_ret p, Tprim Tbool)
4065 | Ast.Amp | Ast.Bar | Ast.Ltlt | Ast.Gtgt ->
4066 let env, _ = enforce_sub_ty env ty1 (Reason.Rbitwise p1, Tprim Tint) in
4067 let env, _ = enforce_sub_ty env ty2 (Reason.Rbitwise p2, Tprim Tint) in
4068 make_result env te1 te2 (Reason.Rbitwise_ret p, Tprim Tint)
4069 | Ast.Eq _ ->
4070 assert false
4072 and non_null env ty =
4073 let env, ty = Env.expand_type env ty in
4074 match ty with
4075 | _, Toption ty ->
4076 (* When "??T" appears in the typing environment due to implicit
4077 * typing, the recursion here ensures that it's treated as
4078 * isomorphic to "?T"; that is, all nulls are created equal.
4080 non_null env ty
4081 | r, Tunresolved tyl ->
4082 let env, tyl = List.map_env env tyl
4083 (fun env e -> non_null env e) in
4084 (* We need to flatten the unresolved types, otherwise we could
4085 * end up with "Tunresolved[Tunresolved _]" which is not supposed
4086 * to happen.
4088 let tyl = List.fold_right tyl ~f:begin fun ty tyl ->
4089 match ty with
4090 | _, Tunresolved l -> l @ tyl
4091 | x -> x :: tyl
4092 end ~init:[] in
4093 env, (r, Tunresolved tyl)
4095 | r, Tabstract (ak, _) ->
4096 begin match TUtils.get_concrete_supertypes env ty with
4097 | env, [ty] -> let env, ty = non_null env ty in
4098 env, (r, Tabstract (ak, Some ty))
4099 | env, _ -> env, ty
4101 | _, (Terr | Tany | Tmixed | Tarraykind _ | Tprim _ | Tvar _
4102 | Tclass (_, _) | Ttuple _ | Tanon (_, _) | Tfun _
4103 | Tobject | Tshape _) ->
4104 env, ty
4106 and condition_var_non_null env = function
4107 | _, Lvar (_, x)
4108 | _, Dollardollar (_, x) ->
4109 let env, x_ty = Env.get_local env x in
4110 let env, x_ty = non_null env x_ty in
4111 Env.set_local env x x_ty
4112 | p, Class_get (cname, (_, member_name)) as e ->
4113 let env, _te, ty = expr env e in
4114 let env, local = Env.FakeMembers.make_static p env cname member_name in
4115 let env = Env.set_local env local ty in
4116 let local = p, Lvar (p, local) in
4117 condition_var_non_null env local
4118 (* TODO TAST: generate an assignment to the fake local in the TAST *)
4119 | p, Obj_get ((_, This | _, Lvar _ as obj),
4120 (_, Id (_, member_name)),
4121 _) as e ->
4122 let env, _te, ty = expr env e in
4123 let env, local = Env.FakeMembers.make p env obj member_name in
4124 let env = Env.set_local env local ty in
4125 let local = p, Lvar (p, local) in
4126 condition_var_non_null env local
4127 | _ -> env
4129 and condition_isset env = function
4130 | _, Array_get (x, _) -> condition_isset env x
4131 | v -> condition_var_non_null env v
4134 * Build an environment for the true or false branch of
4135 * conditional statements.
4137 and condition env tparamet =
4138 let expr env x =
4139 let env, _te, ty = raw_expr ~in_cond:true env x in
4140 Async.enforce_not_awaitable env (fst x) ty;
4141 env, ty
4142 in function
4143 | _, Expr_list [] -> env
4144 | _, Expr_list [x] ->
4145 let env, _ = expr env x in
4146 condition env tparamet x
4147 | r, Expr_list (x::xs) ->
4148 let env, _ = expr env x in
4149 condition env tparamet (r, Expr_list xs)
4150 | _, Call (Cnormal, (_, Id (_, func)), [param], [])
4151 when SN.PseudoFunctions.isset = func && tparamet &&
4152 not (Env.is_strict env) ->
4153 condition_isset env param
4154 | _, Call (Cnormal, (_, Id (_, func)), [e], [])
4155 when not tparamet && SN.StdlibFunctions.is_null = func ->
4156 condition_var_non_null env e
4157 | r, Binop ((Ast.Eqeq | Ast.EQeqeq as bop),
4158 (_, Null), e)
4159 | r, Binop ((Ast.Eqeq | Ast.EQeqeq as bop),
4160 e, (_, Null)) when not tparamet ->
4161 let env, x_ty = expr env e in
4162 let env =
4163 if bop == Ast.Eqeq then check_null_wtf env r x_ty else env in
4164 condition_var_non_null env e
4165 | (p, (Lvar _ | Obj_get _ | Class_get _) as e) ->
4166 let env, ty = expr env e in
4167 let env, ety = Env.expand_type env ty in
4168 (match ety with
4169 | _, Tarraykind (AKany | AKempty)
4170 | _, Tprim Tbool -> env
4171 | _, (Terr | Tany | Tmixed | Tarraykind _ | Toption _
4172 | Tprim _ | Tvar _ | Tfun _ | Tabstract (_, _) | Tclass (_, _)
4173 | Ttuple _ | Tanon (_, _) | Tunresolved _ | Tobject | Tshape _
4174 ) ->
4175 condition env (not tparamet) (p, Binop (Ast.Eqeq, e, (p, Null))))
4176 | r, Binop (Ast.Eq None, var, e) when tparamet ->
4177 let env, e_ty = expr env e in
4178 let env = check_null_wtf env r e_ty in
4179 condition_var_non_null env var
4180 | p1, Binop (Ast.Eq None, (_, (Lvar _ | Obj_get _) as lv), (p2, _)) ->
4181 let env, _ = expr env (p1, Binop (Ast.Eq None, lv, (p2, Null))) in
4182 condition env tparamet lv
4183 | p, Binop ((Ast.Diff | Ast.Diff2 as op), e1, e2) ->
4184 let op = if op = Ast.Diff then Ast.Eqeq else Ast.EQeqeq in
4185 condition env (not tparamet) (p, Binop (op, e1, e2))
4186 | _, Binop (Ast.AMpamp, e1, e2) when tparamet ->
4187 let env = condition env true e1 in
4188 let env = condition env true e2 in
4190 | _, Binop (Ast.BArbar, e1, e2) when not tparamet ->
4191 let env = condition env false e1 in
4192 let env = condition env false e2 in
4194 | _, Call (Cnormal, (p, Id (_, f)), [lv], [])
4195 when tparamet && f = SN.StdlibFunctions.is_array ->
4196 is_array env `PHPArray p f lv
4197 | _, Call (Cnormal, (p, Id (_, f)), [lv], [])
4198 when tparamet && f = SN.StdlibFunctions.is_vec ->
4199 is_array env `HackVec p f lv
4200 | _, Call (Cnormal, (p, Id (_, f)), [lv], [])
4201 when tparamet && f = SN.StdlibFunctions.is_dict ->
4202 is_array env `HackDict p f lv
4203 | _, Call (Cnormal, (p, Id (_, f)), [lv], [])
4204 when tparamet && f = SN.StdlibFunctions.is_keyset ->
4205 is_array env `HackKeyset p f lv
4206 | _, Call (Cnormal, (p, Id (_, f)), [lv], [])
4207 when tparamet && f = SN.StdlibFunctions.is_int ->
4208 is_type env lv Tint (Reason.Rpredicated (p, f))
4209 | _, Call (Cnormal, (p, Id (_, f)), [lv], [])
4210 when tparamet && f = SN.StdlibFunctions.is_bool ->
4211 is_type env lv Tbool (Reason.Rpredicated (p, f))
4212 | _, Call (Cnormal, (p, Id (_, f)), [lv], [])
4213 when tparamet && f = SN.StdlibFunctions.is_float ->
4214 is_type env lv Tfloat (Reason.Rpredicated (p, f))
4215 | _, Call (Cnormal, (p, Id (_, f)), [lv], [])
4216 when tparamet && f = SN.StdlibFunctions.is_string ->
4217 is_type env lv Tstring (Reason.Rpredicated (p, f))
4218 | _, Call (Cnormal, (p, Id (_, f)), [lv], [])
4219 when tparamet && f = SN.StdlibFunctions.is_resource ->
4220 is_type env lv Tresource (Reason.Rpredicated (p, f))
4221 | _, Unop (Ast.Unot, e) ->
4222 condition env (not tparamet) e
4223 | p, InstanceOf (ivar, cid) when tparamet && is_instance_var ivar ->
4224 (* Check the expession and determine its static type *)
4225 let env, _te, x_ty = raw_expr ~in_cond:false env ivar in
4227 (* What is the local variable bound to the expression? *)
4228 let env, (ivar_pos, x) = get_instance_var env ivar in
4230 (* The position p here is not really correct... it's the position
4231 * of the instanceof expression, not the class id. But we don't store
4232 * position data for the latter. *)
4233 let env, _te, obj_ty = static_class_id p env cid in
4235 (* New implementation of instanceof that is statically safe *)
4236 let safe_instanceof env obj_ty _c class_info =
4237 (* Generate fresh names consisting of formal type parameter name
4238 * with unique suffix *)
4239 let env, tparams_with_new_names =
4240 List.map_env env class_info.tc_tparams
4241 (fun env ((_,(_,name),_) as tp) ->
4242 let env, name = Env.add_fresh_generic_parameter env name in
4243 env, (tp, name)) in
4244 let s =
4245 snd _c ^ "<" ^
4246 String.concat "," (List.map tparams_with_new_names ~f:snd)
4247 ^ ">" in
4248 let reason = Reason.Rinstanceof (ivar_pos, s) in
4249 let tyl_fresh = List.map
4250 ~f:(fun (_,newname) -> (reason, Tabstract(AKgeneric newname, None)))
4251 tparams_with_new_names in
4253 (* Type of variable in block will be class name
4254 * with fresh type parameters *)
4255 let obj_ty = (fst obj_ty, Tclass(_c, tyl_fresh)) in
4257 (* Add in constraints as assumptions on those type parameters *)
4258 let ety_env = {
4259 type_expansions = [];
4260 substs = Subst.make class_info.tc_tparams tyl_fresh;
4261 this_ty = obj_ty; (* In case `this` appears in constraints *)
4262 from_class = None;
4263 } in
4264 let add_bounds env ((_, _, cstr_list), ty_fresh) =
4265 List.fold_left cstr_list ~init:env ~f:begin fun env (ck, ty) ->
4266 (* Substitute fresh type parameters for
4267 * original formals in constraint *)
4268 let env, ty = Phase.localize ~ety_env env ty in
4269 SubType.add_constraint p env ck ty_fresh ty end in
4270 let env =
4271 List.fold_left (List.zip_exn class_info.tc_tparams tyl_fresh)
4272 ~f:add_bounds ~init:env in
4274 (* Finally, if we have a class-test on something with static class type,
4275 * then we can chase the hierarchy and decompose the types to deduce
4276 * further assumptions on type parameters. For example, we might have
4277 * class B<Tb> { ... }
4278 * class C extends B<int>
4279 * and have obj_ty = C and x_ty = B<T> for a generic parameter T.
4280 * Then SubType.add_constraint will deduce that T=int and add int as
4281 * both lower and upper bound on T in env.lenv.tpenv
4283 let env = SubType.add_constraint p env Ast.Constraint_as obj_ty x_ty in
4284 env, obj_ty in
4286 if SubType.is_sub_type env obj_ty (
4287 Reason.none, Tclass ((Pos.none, SN.Classes.cAwaitable), [Reason.none, Tany])
4288 ) then () else Async.enforce_not_awaitable env (fst ivar) x_ty;
4290 let safe_instanceof_enabled =
4291 TypecheckerOptions.experimental_feature_enabled
4292 (Env.get_options env) TypecheckerOptions.experimental_instanceof in
4293 let rec resolve_obj env obj_ty =
4294 (* Expand so that we don't modify x *)
4295 let env, obj_ty = Env.expand_type env obj_ty in
4296 match obj_ty with
4297 | _, Tabstract (AKgeneric name, _) ->
4298 if safe_instanceof_enabled
4299 then Errors.instanceof_generic_classname p name;
4300 env, obj_ty
4301 | _, Tabstract (AKdependent (`this, []), Some (_, Tclass _)) ->
4302 let obj_ty =
4303 (* Technically instanceof static is not strong enough to prove
4304 * that a type is exactly the same as the late bound type.
4305 * For now we allow this lie to exist. To solve
4306 * this we either need to create a new type that means
4307 * subtype of static or provide a way of specifying exactly
4308 * the late bound type i.e. $x::class === static::class
4310 if cid = CIstatic then
4311 ExprDepTy.make env CIstatic obj_ty
4312 else
4313 obj_ty in
4314 env, obj_ty
4315 | _, Tabstract ((AKdependent _ | AKnewtype _), Some ty) ->
4316 resolve_obj env ty
4317 | _, Tclass ((_, cid as _c), tyl) ->
4318 begin match Env.get_class env cid with
4319 (* Why would this happen? *)
4320 | None ->
4321 env, (Reason.Rwitness ivar_pos, Tobject)
4323 | Some class_info ->
4324 if SubType.is_sub_type env x_ty obj_ty
4325 then
4326 (* If the right side of the `instanceof` object is
4327 * a super type of what we already knew. In this case,
4328 * since we already have a more specialized object, we
4329 * don't touch the original object. Check out the unit
4330 * test srecko.php if this is unclear.
4332 * Note that if x_ty is Tany, no amount of subtype
4333 * checking will be able to specify it
4334 * further. This is arguably desirable to maintain
4335 * the invariant that removing annotations gets rid
4336 * of typing errors in partial mode (See also
4337 * t3216948). *)
4338 env, x_ty
4339 else
4340 (* We only implement the safe instanceof in strict mode *)
4341 (* Also: for generic types we implememt it only with
4342 * experimental feature enabled *)
4343 if Env.is_strict env && (tyl = [] || safe_instanceof_enabled)
4344 then safe_instanceof env obj_ty _c class_info
4345 else env, obj_ty
4347 | r, Tunresolved tyl ->
4348 let env, tyl = List.map_env env tyl resolve_obj in
4349 env, (r, Tunresolved tyl)
4350 | _, (Terr | Tany | Tmixed | Tarraykind _ | Tprim _ | Tvar _ | Tfun _
4351 | Tabstract ((AKenum _ | AKnewtype _ | AKdependent _), _)
4352 | Ttuple _ | Tanon (_, _) | Toption _ | Tobject | Tshape _) ->
4353 env, (Reason.Rwitness ivar_pos, Tobject)
4355 let env, x_ty = resolve_obj env obj_ty in
4356 Env.set_local env x x_ty
4357 | _, Binop ((Ast.Eqeq | Ast.EQeqeq), e, (_, Null))
4358 | _, Binop ((Ast.Eqeq | Ast.EQeqeq), (_, Null), e) ->
4359 let env, _ = expr env e in
4361 | e ->
4362 let env, _ = expr env e in
4365 and is_instance_var = function
4366 | _, (Lvar _ | This) -> true
4367 | _, Obj_get ((_, This), (_, Id _), _) -> true
4368 | _, Obj_get ((_, Lvar _), (_, Id _), _) -> true
4369 | _, Class_get (_, _) -> true
4370 | _ -> false
4372 and get_instance_var env = function
4373 | p, Class_get (cname, (_, member_name)) ->
4374 let env, local = Env.FakeMembers.make_static p env cname member_name in
4375 env, (p, local)
4376 | p, Obj_get ((_, This | _, Lvar _ as obj), (_, Id (_, member_name)), _) ->
4377 let env, local = Env.FakeMembers.make p env obj member_name in
4378 env, (p, local)
4379 | _, Lvar (p, x) -> env, (p, x)
4380 | p, This -> env, (p, this)
4381 | _ -> failwith "Should only be called when is_instance_var is true"
4383 and check_null_wtf env p ty =
4384 if not (Env.is_strict env) then env else
4385 let env, ty = TUtils.fold_unresolved env ty in
4386 let env, ety = Env.expand_type env ty in
4387 match ety with
4388 | _, Toption ty ->
4389 (* Find sketchy nulls hidden under singleton Tunresolved *)
4390 let env, ty = TUtils.fold_unresolved env ty in
4391 (match ty with
4392 | _, Tmixed ->
4393 Errors.sketchy_null_check p
4394 | _, Tprim _ ->
4395 Errors.sketchy_null_check_primitive p
4396 | _, (Terr | Tany | Tarraykind _ | Toption _ | Tvar _ | Tfun _
4397 | Tabstract (_, _) | Tclass (_, _) | Ttuple _ | Tanon (_, _)
4398 | Tunresolved _ | Tobject | Tshape _ ) -> ());
4400 | _, (Terr | Tany | Tmixed | Tarraykind _ | Tprim _ | Tvar _
4401 | Tfun _ | Tabstract (_, _) | Tclass (_, _) | Ttuple _ | Tanon (_, _)
4402 | Tunresolved _ | Tobject | Tshape _ ) -> env
4404 and is_type env e tprim r =
4405 match e with
4406 | p, Class_get (cname, (_, member_name)) ->
4407 let env, local = Env.FakeMembers.make_static p env cname member_name in
4408 Env.set_local env local (r, Tprim tprim)
4409 | p, Obj_get ((_, This | _, Lvar _ as obj), (_, Id (_, member_name)), _) ->
4410 let env, local = Env.FakeMembers.make p env obj member_name in
4411 Env.set_local env local (r, Tprim tprim)
4412 | _, Lvar (_px, x) ->
4413 Env.set_local env x (r, Tprim tprim)
4414 | _ -> env
4416 (* Refine type for is_array, is_vec, is_keyset and is_dict tests
4417 * `pred_name` is the function name itself (e.g. 'is_vec')
4418 * `p` is position of the function name in the source
4419 * `arg_expr` is the argument to the function
4421 and is_array env ty p pred_name arg_expr =
4422 let env, _te, arg_ty = expr env arg_expr in
4423 let r = Reason.Rpredicated (p, pred_name) in
4424 let env, tarrkey_name = Env.add_fresh_generic_parameter env "Tk" in
4425 let tarrkey = (r, Tabstract (AKgeneric tarrkey_name, None)) in
4426 let env = SubType.add_constraint p env Ast.Constraint_as
4427 tarrkey (r, Tprim Tarraykey) in
4428 let env, tfresh_name = Env.add_fresh_generic_parameter env "T" in
4429 let tfresh = (r, Tabstract (AKgeneric tfresh_name, None)) in
4430 (* This is the refined type of e inside the branch *)
4431 let refined_ty =
4432 (r, (match ty with
4433 | `HackDict ->
4434 Tclass ((Pos.none, SN.Collections.cDict), [tarrkey; tfresh])
4435 | `HackVec ->
4436 Tclass ((Pos.none, SN.Collections.cVec), [tfresh])
4437 | `HackKeyset ->
4438 Tclass ((Pos.none, SN.Collections.cKeyset), [tarrkey])
4439 | `PHPArray ->
4440 Tarraykind AKany)) in
4441 (* Add constraints on generic parameters that must
4442 * hold for refined_ty <:arg_ty. For example, if arg_ty is Traversable<T>
4443 * and refined_ty is keyset<T#1> then we know T#1 <: T *)
4444 let env = SubType.add_constraint p env Ast.Constraint_as refined_ty arg_ty in
4445 match arg_expr with
4446 | (_, Class_get (cname, (_, member_name))) ->
4447 let env, local = Env.FakeMembers.make_static p env cname member_name in
4448 Env.set_local env local refined_ty
4449 | (_, Obj_get ((_, This | _, Lvar _ as obj), (_, Id (_, member_name)), _)) ->
4450 let env, local = Env.FakeMembers.make p env obj member_name in
4451 Env.set_local env local refined_ty
4452 | (_, Lvar (_, x)) ->
4453 Env.set_local env x refined_ty
4454 | _ -> env
4456 and string2 env idl =
4457 let env, tel =
4458 List.fold_left idl ~init:(env,[]) ~f:begin fun (env,tel) x ->
4459 let env, te, ty = expr env x in
4460 let p = fst x in
4461 let env = SubType.sub_string p env ty in
4462 env, te::tel
4463 end in
4464 env, List.rev tel
4466 (* If the current class inherits from classes that take type arguments, we need
4467 * to check that the arguments provided are consistent with the constraints on
4468 * the type parameters. *)
4469 and check_implements_tparaml (env: Env.env) ht =
4470 let _r, (p, c), paraml = TUtils.unwrap_class_type ht in
4471 let class_ = Decl_env.get_class_dep env.Env.decl_env c in
4472 match class_ with
4473 | None ->
4474 (* The class lives in PHP land *)
4476 | Some class_ ->
4477 let size1 = List.length class_.dc_tparams in
4478 let size2 = List.length paraml in
4479 if size1 <> size2 then Errors.class_arity p class_.dc_pos c size1;
4480 let subst = Inst.make_subst class_.dc_tparams paraml in
4481 iter2_shortest begin fun (_, (p, _), cstrl) ty ->
4482 List.iter cstrl begin fun (ck, cstr) ->
4483 (* Constraint might contain uses of generic type parameters *)
4484 let cstr = Inst.instantiate subst cstr in
4485 match ck with
4486 | Ast.Constraint_as ->
4487 Type.sub_type_decl p Reason.URnone env ty cstr
4488 | Ast.Constraint_eq ->
4489 (* This code could well be unreachable, because we don't allow
4490 * equality constraints on class generics. *)
4491 Type.sub_type_decl p Reason.URnone env ty cstr;
4492 Type.sub_type_decl p Reason.URnone env cstr ty
4493 | Ast.Constraint_super ->
4494 Type.sub_type_decl p Reason.URnone env cstr ty
4496 end class_.dc_tparams paraml
4498 (* In order to type-check a class, we need to know what "parent"
4499 * refers to. Sometimes people write "parent::", when that happens,
4500 * we need to know the type of parent.
4502 and class_def_parent env class_def class_type =
4503 match class_def.c_extends with
4504 | (_, Happly ((_, x), _) as parent_ty) :: _ ->
4505 let parent_type = Decl_env.get_class_dep env.Env.decl_env x in
4506 (match parent_type with
4507 | Some parent_type -> check_parent class_def class_type parent_type
4508 | None -> ());
4509 let parent_ty = Decl_hint.hint env.Env.decl_env parent_ty in
4510 env, Some x, parent_ty
4511 (* The only case where we have more than one parent class is when
4512 * dealing with interfaces and interfaces cannot use parent.
4514 | _ :: _
4515 | _ -> env, None, (Reason.Rnone, Tany)
4517 and check_parent class_def class_type parent_type =
4518 let position = fst class_def.c_name in
4519 (* Are all the parents in Hack? Do we know all their methods?
4520 * If so, let's check that the abstract methods have been implemented.
4522 if class_type.tc_members_fully_known
4523 then check_parent_abstract position parent_type class_type;
4524 if parent_type.dc_final
4525 then Errors.extend_final position parent_type.dc_pos parent_type.dc_name
4526 else ()
4528 and check_parent_abstract position parent_type class_type =
4529 let is_final = class_type.tc_final in
4530 if parent_type.dc_kind = Ast.Cabstract &&
4531 (class_type.tc_kind <> Ast.Cabstract || is_final)
4532 then begin
4533 check_extend_abstract_meth ~is_final position class_type.tc_methods;
4534 check_extend_abstract_meth ~is_final position class_type.tc_smethods;
4535 check_extend_abstract_const ~is_final position class_type.tc_consts;
4536 check_extend_abstract_typeconst
4537 ~is_final position class_type.tc_typeconsts;
4538 end else ()
4540 and class_def tcopt c =
4541 let filename = Pos.filename (fst c.Nast.c_name) in
4542 let dep = Dep.Class (snd c.c_name) in
4543 let env = Env.empty tcopt filename (Some dep) in
4544 let c = TNBody.class_meth_bodies tcopt c in
4545 if not !auto_complete then begin
4546 NastCheck.class_ env c;
4547 NastInitCheck.class_ env c;
4548 end;
4549 let tc = Env.get_class env (snd c.c_name) in
4550 match tc with
4551 | None ->
4552 (* This can happen if there was an error during the declaration
4553 * of the class. *)
4554 None
4555 | Some tc ->
4556 Typing_requirements.check_class env tc;
4557 Some (class_def_ env c tc)
4559 (* Given a class definition construct a type consisting of the
4560 * class instantiated at its generic parameters.
4562 and get_self_from_c c =
4563 let tparams = List.map (fst c.c_tparams) begin fun (_, (p, s), _) ->
4564 Reason.Rwitness p, Tgeneric s
4565 end in
4566 Reason.Rwitness (fst c.c_name), Tapply (c.c_name, tparams)
4568 and class_def_ env c tc =
4569 Typing_hooks.dispatch_enter_class_def_hook c tc;
4570 let env = Env.set_mode env c.c_mode in
4571 let pc, _ = c.c_name in
4572 let impl = List.map
4573 (c.c_extends @ c.c_implements @ c.c_uses)
4574 (Decl_hint.hint env.Env.decl_env) in
4575 TI.check_tparams_instantiable env (fst c.c_tparams);
4576 let env, constraints =
4577 Phase.localize_generic_parameters_with_bounds env (fst c.c_tparams)
4578 ~ety_env:(Phase.env_with_self env) in
4579 let env = add_constraints (fst c.c_name) env constraints in
4580 Typing_variance.class_ (Env.get_options env) (snd c.c_name) tc impl;
4581 List.iter impl (check_implements_tparaml env);
4583 (* Set up self identifier and type *)
4584 let env = Env.set_self_id env (snd c.c_name) in
4585 let self = get_self_from_c c in
4586 (* For enums, localize makes self:: into an abstract type, which we don't
4587 * want *)
4588 let env, self = match c.c_kind with
4589 | Ast.Cenum -> env, (fst self, Tclass (c.c_name, []))
4590 | Ast.Cinterface | Ast.Cabstract | Ast.Ctrait
4591 | Ast.Cnormal -> Phase.localize_with_self env self in
4592 let env = Env.set_self env self in
4594 let env, parent_id, parent = class_def_parent env c tc in
4595 let is_final = tc.tc_final in
4596 if (tc.tc_kind = Ast.Cnormal || is_final) && tc.tc_members_fully_known
4597 then begin
4598 check_extend_abstract_meth ~is_final pc tc.tc_methods;
4599 check_extend_abstract_meth ~is_final pc tc.tc_smethods;
4600 check_extend_abstract_const ~is_final pc tc.tc_consts;
4601 check_extend_abstract_typeconst ~is_final pc tc.tc_typeconsts;
4602 end;
4603 let env = Env.set_parent env parent in
4604 let env = match parent_id with
4605 | None -> env
4606 | Some parent_id -> Env.set_parent_id env parent_id in
4607 if tc.tc_final then begin
4608 match c.c_kind with
4609 | Ast.Cinterface -> Errors.interface_final (fst c.c_name)
4610 | Ast.Cabstract -> ()
4611 | Ast.Ctrait -> Errors.trait_final (fst c.c_name)
4612 | Ast.Cenum ->
4613 Errors.internal_error pc "The parser should not parse final on enums"
4614 | Ast.Cnormal -> ()
4615 end;
4616 SMap.iter (check_static_method tc.tc_methods) tc.tc_smethods;
4617 List.iter impl (class_implements_type env c);
4618 let typed_vars = List.map c.c_vars (class_var_def env ~is_static:false c) in
4619 let typed_methods = List.map c.c_methods (method_def env) in
4620 let typed_typeconsts = List.map c.c_typeconsts (typeconst_def env) in
4621 let typed_consts, const_types =
4622 List.unzip (List.map c.c_consts (class_const_def env)) in
4623 let env = Typing_enum.enum_class_check env tc c.c_consts const_types in
4624 let typed_constructor = class_constr_def env c in
4625 let env = Env.set_static env in
4626 let typed_static_vars =
4627 List.map c.c_static_vars (class_var_def env ~is_static:true c) in
4628 let typed_static_methods = List.map c.c_static_methods (method_def env) in
4629 Typing_hooks.dispatch_exit_class_def_hook c tc;
4631 T.c_mode = c.c_mode;
4632 T.c_final = c.c_final;
4633 T.c_is_xhp = c.c_is_xhp;
4634 T.c_kind = c.c_kind;
4635 T.c_name = c.c_name;
4636 T.c_tparams = c.c_tparams;
4637 T.c_extends = c.c_extends;
4638 T.c_uses = c.c_uses;
4639 T.c_xhp_attr_uses = c.c_xhp_attr_uses;
4640 T.c_xhp_category = c.c_xhp_category;
4641 T.c_req_extends = c.c_req_extends;
4642 T.c_req_implements = c.c_req_implements;
4643 T.c_implements = c.c_implements;
4644 T.c_consts = typed_consts;
4645 T.c_typeconsts = typed_typeconsts;
4646 T.c_static_vars = typed_static_vars;
4647 T.c_vars = typed_vars;
4648 T.c_constructor = typed_constructor;
4649 T.c_static_methods = typed_static_methods;
4650 T.c_methods = typed_methods;
4651 T.c_user_attributes = List.map c.c_user_attributes (user_attribute env);
4652 T.c_enum = c.c_enum;
4655 and check_static_method obj method_name static_method =
4656 if SMap.mem method_name obj
4657 then begin
4658 let lazy (static_method_reason, _) = static_method.ce_type in
4659 let dyn_method = SMap.find_unsafe method_name obj in
4660 let lazy (dyn_method_reason, _) = dyn_method.ce_type in
4661 Errors.static_dynamic
4662 (Reason.to_pos static_method_reason)
4663 (Reason.to_pos dyn_method_reason)
4664 method_name
4666 else ()
4668 and check_extend_abstract_meth ~is_final p smap =
4669 SMap.iter begin fun x ce ->
4670 match ce.ce_type with
4671 | lazy (r, Tfun { ft_abstract = true; _ }) ->
4672 Errors.implement_abstract ~is_final p (Reason.to_pos r) "method" x
4673 | _ -> ()
4674 end smap
4676 (* Type constants must be bound to a concrete type for non-abstract classes.
4678 and check_extend_abstract_typeconst ~is_final p smap =
4679 SMap.iter begin fun x tc ->
4680 if tc.ttc_type = None then
4681 Errors.implement_abstract ~is_final p (fst tc.ttc_name) "type constant" x
4682 end smap
4684 and check_extend_abstract_const ~is_final p smap =
4685 SMap.iter begin fun x cc ->
4686 match cc.cc_type with
4687 | r, _ when cc.cc_abstract && not cc.cc_synthesized ->
4688 Errors.implement_abstract ~is_final p (Reason.to_pos r) "constant" x
4689 | _,
4691 Terr
4692 | Tany
4693 | Tmixed
4694 | Tarray (_, _)
4695 | Tdarray (_, _)
4696 | Tvarray _
4697 | Toption _
4698 | Tprim _
4699 | Tfun _
4700 | Tapply (_, _)
4701 | Ttuple _
4702 | Tshape _
4703 | Taccess (_, _)
4704 | Tthis
4705 | Tgeneric _
4706 ) -> ()
4707 end smap
4709 and typeconst_def env {
4710 c_tconst_name = (pos, _) as id;
4711 c_tconst_constraint;
4712 c_tconst_type;
4714 let env, cstr = opt Phase.hint_locl env c_tconst_constraint in
4715 let env, ty = opt Phase.hint_locl env c_tconst_type in
4716 ignore (
4717 Option.map2 ty cstr ~f:(Type.sub_type pos Reason.URtypeconst_cstr env)
4720 T.c_tconst_name = id;
4721 T.c_tconst_constraint = c_tconst_constraint;
4722 T.c_tconst_type = c_tconst_type;
4725 and class_const_def env (h, id, e) =
4726 let env, ty =
4727 match h with
4728 | None -> env, Env.fresh_type()
4729 | Some h ->
4730 Phase.hint_locl env h
4732 match e with
4733 | Some e ->
4734 let env, te, ty' = expr env e in
4735 ignore (Type.sub_type (fst id) Reason.URhint env ty' ty);
4736 (h, id, Some te), ty'
4737 | None ->
4738 (h, id, None), ty
4740 and class_constr_def env c =
4741 Option.map c.c_constructor (method_def env)
4743 and class_implements_type env c1 ctype2 =
4744 let params =
4745 List.map (fst c1.c_tparams) begin fun (_, (p, s), _) ->
4746 (Reason.Rwitness p, Tgeneric s)
4747 end in
4748 let r = Reason.Rwitness (fst c1.c_name) in
4749 let ctype1 = r, Tapply (c1.c_name, params) in
4750 Typing_extends.check_implements env ctype2 ctype1;
4753 and class_var_def env ~is_static c cv =
4754 let env, typed_cv_expr, ty =
4755 match cv.cv_expr with
4756 | None -> env, None, Env.fresh_type()
4757 | Some e -> let env, te, ty = expr env e in env, Some te, ty in
4758 (match cv.cv_type with
4759 | None when Env.is_strict env ->
4760 Errors.add_a_typehint (fst cv.cv_id)
4761 | None ->
4762 let pos, name = cv.cv_id in
4763 let name = if is_static then "$"^name else name in
4764 let var_type = Reason.Rwitness pos, Tany in
4765 (match cv.cv_expr with
4766 | None ->
4767 Typing_suggest.uninitialized_member (snd c.c_name) name env var_type ty;
4769 | Some _ ->
4770 Typing_suggest.save_member name env var_type ty;
4773 | Some (p, _ as cty) ->
4774 let env =
4775 (* If this is an XHP attribute and we're in strict mode,
4776 relax to partial mode to allow the use of the "array"
4777 annotation without specifying type parameters. Until
4778 recently HHVM did not allow "array" with type parameters
4779 in XHP attribute declarations, so this is a temporary
4780 hack to support existing code for now. *)
4781 (* Task #5815945: Get rid of this Hack *)
4782 if cv.cv_is_xhp && (Env.is_strict env)
4783 then Env.set_mode env FileInfo.Mpartial
4784 else env in
4785 let cty = TI.instantiable_hint env cty in
4786 let env, cty = Phase.localize_with_self env cty in
4787 let _ = Type.sub_type p Reason.URhint env ty cty in
4788 ());
4790 T.cv_final = cv.cv_final;
4791 T.cv_is_xhp = cv.cv_is_xhp;
4792 T.cv_visibility = cv.cv_visibility;
4793 T.cv_type = cv.cv_type;
4794 T.cv_id = cv.cv_id;
4795 T.cv_expr = typed_cv_expr;
4798 and localize_where_constraints
4799 ~ety_env (env:Env.env) (where_constraints:Nast.where_constraint list) =
4800 let add_constraint env (h1, ck, h2) =
4801 let env, ty1 =
4802 Phase.localize env (Decl_hint.hint env.Env.decl_env h1) ~ety_env in
4803 let env, ty2 =
4804 Phase.localize env (Decl_hint.hint env.Env.decl_env h2) ~ety_env in
4805 SubType.add_constraint (fst h1) env ck ty1 ty2
4807 List.fold_left where_constraints ~f:add_constraint ~init:env
4809 and add_constraints p env constraints =
4810 let add_constraint env (ty1, ck, ty2) =
4811 SubType.add_constraint p env ck ty1 ty2 in
4812 List.fold_left constraints ~f:add_constraint ~init: env
4814 and user_attribute env ua =
4815 let typed_ua_params =
4816 List.map ua.ua_params (fun e -> let _env, te, _ty = expr env e in te) in
4818 T.ua_name = ua.ua_name;
4819 T.ua_params = typed_ua_params;
4822 and method_def env m =
4823 (* reset the expression dependent display ids for each method body *)
4824 Reason.expr_display_id_map := IMap.empty;
4825 Typing_hooks.dispatch_enter_method_def_hook m;
4826 let env =
4827 Env.env_with_locals env Typing_continuations.Map.empty Local_id.Map.empty
4829 let ety_env =
4830 { (Phase.env_with_self env) with from_class = Some CIstatic; } in
4831 let env, constraints =
4832 Phase.localize_generic_parameters_with_bounds env m.m_tparams
4833 ~ety_env:ety_env in
4834 TI.check_tparams_instantiable env m.m_tparams;
4835 let env = add_constraints (fst m.m_name) env constraints in
4836 let env =
4837 localize_where_constraints ~ety_env env m.m_where_constraints in
4838 let env = Env.set_local env this (Env.get_self env) in
4839 let env, ret = match m.m_ret with
4840 | None -> env, (Reason.Rwitness (fst m.m_name), Tany)
4841 | Some ret ->
4842 let ret = TI.instantiable_hint env ret in
4843 (* If a 'this' type appears it needs to be compatiable with the
4844 * late static type
4846 let ety_env =
4847 { (Phase.env_with_self env) with
4848 from_class = Some CIstatic } in
4849 Phase.localize ~ety_env env ret in
4850 let m_params = match m.m_variadic with
4851 | FVvariadicArg param -> param :: m.m_params
4852 | _ -> m.m_params
4854 TI.check_params_instantiable env m_params;
4855 let env, param_tys = List.map_env env m_params make_param_local_ty in
4856 if Env.is_strict env then begin
4857 List.iter2_exn ~f:(check_param env) m_params param_tys;
4858 end;
4859 if Attributes.mem SN.UserAttributes.uaMemoize m.m_user_attributes then
4860 List.iter2_exn ~f:(check_memoizable env) m_params param_tys;
4861 let env, typed_params =
4862 List.map_env env (List.zip_exn param_tys m_params) bind_param in
4863 let nb = Nast.assert_named_body m.m_body in
4864 let env, tb =
4865 fun_ ~abstract:m.m_abstract env ret (fst m.m_name) nb m.m_fun_kind in
4866 let env =
4867 List.fold_left (Env.get_todo env) ~f:(fun env f -> f env) ~init:env in
4868 (match m.m_ret with
4869 | None when Env.is_strict env && snd m.m_name <> SN.Members.__destruct ->
4870 (* if we are in strict mode, the only case where we don't want to enforce
4871 * a return type is when the method is a destructor
4873 suggest_return env (fst m.m_name) ret
4874 | None
4875 | Some _ -> ());
4876 Typing_hooks.dispatch_exit_method_def_hook m;
4878 T.m_final = m.m_final;
4879 T.m_abstract = m.m_abstract;
4880 T.m_visibility = m.m_visibility;
4881 T.m_name = m.m_name;
4882 T.m_tparams = m.m_tparams;
4883 T.m_where_constraints = m.m_where_constraints;
4884 T.m_variadic = T.FVnonVariadic (* TODO TAST: get this right m.m_variadic *);
4885 T.m_params = typed_params;
4886 T.m_fun_kind = m.m_fun_kind;
4887 T.m_user_attributes = List.map m.m_user_attributes (user_attribute env);
4888 T.m_ret = m.m_ret;
4889 T.m_body = T.NamedBody {
4890 T.fnb_nast = tb;
4891 T.fnb_unsafe = false (* TAST get this right *)
4895 and typedef_def tcopt typedef =
4896 let tid = (snd typedef.t_name) in
4897 let filename = Pos.filename (fst typedef.t_kind) in
4898 let dep = Typing_deps.Dep.Class tid in
4899 let env =
4900 Typing_env.empty tcopt filename (Some dep) in
4901 (* Mode for typedefs themselves doesn't really matter right now, but
4902 * they can expand hints, so make it loose so that the typedef doesn't
4903 * fail. (The hint will get re-checked with the proper mode anyways.)
4904 * Ideally the typedef would carry the right mode with it, but it's a
4905 * slightly larger change than I want to deal with right now. *)
4906 let env = Typing_env.set_mode env FileInfo.Mdecl in
4907 let env, constraints =
4908 Phase.localize_generic_parameters_with_bounds env typedef.t_tparams
4909 ~ety_env:(Phase.env_with_self env) in
4910 let env = add_constraints (fst typedef.t_name) env constraints in
4911 NastCheck.typedef env typedef;
4912 let {
4913 t_name = t_pos, _;
4914 t_tparams = _;
4915 t_constraint = tcstr;
4916 t_kind = hint;
4917 t_user_attributes = _;
4918 t_vis = _;
4919 t_mode = _;
4920 } = typedef in
4921 let ty = TI.instantiable_hint env hint in
4922 let env, ty = Phase.localize_with_self env ty in
4923 begin match tcstr with
4924 | Some tcstr ->
4925 let cstr = TI.instantiable_hint env tcstr in
4926 let env, cstr = Phase.localize_with_self env cstr in
4927 ignore @@ Typing_ops.sub_type t_pos Reason.URnewtype_cstr env ty cstr
4928 | _ -> ()
4929 end;
4930 match hint with
4931 | pos, Hshape { nsi_allows_unknown_fields=_; nsi_field_map } ->
4932 ignore (check_shape_keys_validity env pos (ShapeMap.keys nsi_field_map))
4933 | _ -> ()
4935 and gconst_def cst tcopt =
4936 Typing_hooks.dispatch_global_const_hook cst.cst_name;
4937 let typed_cst_value =
4938 match cst.cst_value with
4939 | None -> None
4940 | Some value ->
4941 let filename = Pos.filename (fst cst.cst_name) in
4942 let dep = Typing_deps.Dep.GConst (snd cst.cst_name) in
4943 let env =
4944 Typing_env.empty tcopt filename (Some dep) in
4945 let env = Typing_env.set_mode env cst.cst_mode in
4946 let env, te, value_type = expr env value in
4947 begin match cst.cst_type with
4948 | Some hint ->
4949 let ty = TI.instantiable_hint env hint in
4950 let env, dty = Phase.localize_with_self env ty in
4951 ignore @@ Typing_utils.sub_type env value_type dty
4952 | None -> () end;
4953 Some te in
4954 { T.cst_mode = cst.cst_mode;
4955 T.cst_name = cst.cst_name;
4956 T.cst_type = cst.cst_type;
4957 T.cst_value = typed_cst_value
4960 (* Calls the method of a class, but allows the f callback to override the
4961 * return value type *)
4962 and overload_function p env class_id method_id el uel f =
4963 let env, _ce, ty = static_class_id p env class_id in
4964 let env, _tel, _ = exprs env el in
4965 let env, fty, _ =
4966 class_get ~is_method:true ~is_const:false env ty method_id class_id in
4967 (* call the function as declared to validate arity and input types,
4968 but ignore the result and overwrite with custom one *)
4969 let (env, res), has_error = Errors.try_with_error
4970 (fun () -> (let env, _, _, ty = call p env fty el uel in env, ty), false)
4971 (fun () -> (env, (Reason.Rwitness p, Tany)), true) in
4972 (* if there are errors already stop here - going forward would
4973 * report them twice *)
4974 if has_error then env, T.make_typed_expr p res T.Any, res
4975 else let env, ty = f env fty res el in
4976 (* TODO TAST: do this right *)
4977 env, T.make_typed_expr p ty T.Any, ty
4979 and update_array_type p env e1 e2 valkind =
4980 let access_type = Typing_arrays.static_array_access env e2 in
4981 let type_mapper =
4982 Typing_arrays.update_array_type p access_type in
4983 match valkind with
4984 | `lvalue | `lvalue_subexpr ->
4985 let env, te1, ty1 =
4986 raw_expr ~valkind:`lvalue_subexpr ~in_cond:false env e1 in
4987 let env, ty1 = type_mapper env ty1 in
4988 begin match e1 with
4989 | (_, Lvar (_, x)) ->
4990 (* type_mapper has updated the type in ty1 typevars, but we
4991 need to update the local variable type too *)
4992 let env, ty1 = set_valid_rvalue p env x ty1 in
4993 env, te1, ty1
4994 | _ -> env, te1, ty1
4996 | _ ->
4997 expr env e1