Typechecker support for throwing and catching throwables and errors
[hiphop-php.git] / hphp / hack / src / typing / typing.ml
blob37d89d13d905535a5a12162602bc1c05c73dc44a
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 Hh_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 TI = Typing_instantiability
36 module TVis = Typing_visibility
37 module TNBody = Typing_naming_body
38 module TS = Typing_structure
39 module T = Tast
40 module Phase = Typing_phase
41 module Subst = Decl_subst
42 module ExprDepTy = Typing_dependent_type.ExprDepTy
43 module TCO = TypecheckerOptions
44 module EnvFromDef = Typing_env_from_def.EnvFromDef(Nast.Annotations)
45 module TySet = Typing_set
46 module IsAsExprHint = TUtils.IsAsExprHint
48 (*****************************************************************************)
49 (* Debugging *)
50 (*****************************************************************************)
52 (* A guess as to the last position we were typechecking, for use in debugging,
53 * such as figuring out what a runaway hh_server thread is doing. Updated
54 * only best-effort -- it's an approximation to point debugging in the right
55 * direction, nothing more. *)
56 let debug_last_pos = ref Pos.none
57 let debug_print_last_pos _ = print_endline (Pos.string (Pos.to_absolute
58 !debug_last_pos))
60 (****************************************************************************)
61 (* Hooks *)
62 (****************************************************************************)
64 let expr_hook = ref None
66 let with_expr_hook hook f = with_context
67 ~enter: (fun () -> expr_hook := Some hook)
68 ~exit: (fun () -> expr_hook := None)
69 ~do_: f
71 (*****************************************************************************)
72 (* Helpers *)
73 (*****************************************************************************)
75 let suggest env p ty =
76 let ty = Typing_expand.fully_expand env ty in
77 (match Typing_print.suggest ty with
78 | "..." -> Errors.expecting_type_hint p
79 | ty -> Errors.expecting_type_hint_suggest p ty
82 let err_witness env p = Reason.Rwitness p, Typing_utils.terr env
84 (* When typing an expression, we optionally pass in the type
85 * that we *expect* the expression to have.
87 type _expected_ty =
88 Pos.t * Reason.ureason * locl ty
90 let expr_error env p r =
91 let ty = (r, Typing_utils.terr env) in
92 env, T.make_typed_expr p ty T.Any, ty
94 let expr_any env p r =
95 let ty = (r, Typing_utils.tany env) in
96 env, T.make_typed_expr p ty T.Any, ty
98 let compare_field_kinds x y =
99 match x, y with
100 | Nast.AFvalue (p1, _), Nast.AFkvalue ((p2, _), _)
101 | Nast.AFkvalue ((p2, _), _), Nast.AFvalue (p1, _) ->
102 Errors.field_kinds p1 p2;
103 false
104 | _ ->
105 true
107 let check_consistent_fields x l =
108 List.for_all l (compare_field_kinds x)
110 let unbound_name env (pos, name) =
111 match Env.get_mode env with
112 | FileInfo.Mstrict ->
113 (Errors.unbound_name_typing pos name;
114 expr_error env pos (Reason.Rwitness pos))
116 | FileInfo.Mdecl | FileInfo.Mpartial | FileInfo.Mphp ->
117 expr_any env pos (Reason.Rwitness pos)
119 (* Is this type Traversable<vty> or Container<vty> for some vty? *)
120 let get_value_collection_inst ty =
121 match ty with
122 | (_, Tclass ((_, c), [vty])) when
123 c = SN.Collections.cTraversable ||
124 c = SN.Collections.cContainer ->
125 Some vty
126 | _ ->
127 None
129 (* Is this type KeyedTraversable<kty,vty>
130 * or KeyedContainer<kty,vty>
131 * or Indexish<kty,vty>
132 * for some kty, vty?
134 let get_key_value_collection_inst ty =
135 match ty with
136 | (_, Tclass ((_, c), [kty; vty])) when
137 c = SN.Collections.cKeyedTraversable ||
138 c = SN.Collections.cKeyedContainer ||
139 c = SN.Collections.cIndexish ->
140 Some (kty, vty)
141 | _ ->
142 None
144 (* Is this type varray<vty> or a supertype for some vty? *)
145 let get_varray_inst ty =
146 match ty with
147 (* It's varray<vty> *)
148 | (_, Tarraykind (AKvarray vty)) -> Some vty
149 | _ -> get_value_collection_inst ty
151 (* Is this type one of the value collection types with element type vty? *)
152 let get_vc_inst vc_kind ty =
153 match ty with
154 | (_, Tclass ((_, c), [vty]))
155 when c = vc_kind_to_name vc_kind -> Some vty
156 | _ -> get_value_collection_inst ty
158 (* Is this type array<vty> or a supertype for some vty? *)
159 let get_akvec_inst ty =
160 match ty with
161 | (_, Tarraykind (AKvec vty)) -> Some vty
162 | _ -> get_value_collection_inst ty
164 (* Is this type array<kty,vty> or a supertype for some kty and vty? *)
165 let get_akmap_inst ty =
166 match ty with
167 | (_, Tarraykind (AKmap (kty, vty))) -> Some (kty, vty)
168 | _ -> get_key_value_collection_inst ty
170 (* Is this type one of the three key-value collection types
171 * e.g. dict<kty,vty> or a supertype for some kty and vty? *)
172 let get_kvc_inst kvc_kind ty =
173 match ty with
174 | (_, Tclass ((_, c), [kty; vty]))
175 when c = kvc_kind_to_name kvc_kind -> Some (kty, vty)
176 | _ -> get_key_value_collection_inst ty
178 (* Is this type darray<kty, vty> or a supertype for some kty and vty? *)
179 let get_darray_inst ty =
180 match ty with
181 (* It's darray<kty, vty> *)
182 | (_, Tarraykind (AKdarray (kty, vty))) -> Some (kty, vty)
183 | _ -> get_key_value_collection_inst ty
186 (* Try running function on each concrete supertype in turn. Return all
187 * successful results
189 let try_over_concrete_supertypes env ty f =
190 let env, tyl = TUtils.get_concrete_supertypes env ty in
191 (* If there is just a single result then don't swallow errors *)
192 match tyl with
193 | [ty] ->
194 [f env ty]
195 | _ ->
196 let rec iter_over_types env resl tyl =
197 match tyl with
198 [] -> resl
199 | ty::tyl ->
200 Errors.try_
201 (fun () -> iter_over_types env (f env ty::resl) tyl)
202 (fun _ -> iter_over_types env resl tyl) in
203 iter_over_types env [] tyl
205 let save_env env =
206 T.{tcopt = Env.get_tcopt env; tenv = env.Env.tenv; subst = env.Env.subst}
209 (*****************************************************************************)
210 (* Handling function/method arguments *)
211 (*****************************************************************************)
214 let has_accept_disposable_attribute param =
215 List.exists param.param_user_attributes
216 (fun { ua_name; _ } -> SN.UserAttributes.uaAcceptDisposable = snd ua_name)
218 let has_mutable_attribute param =
219 List.exists param.param_user_attributes
220 (fun { ua_name; _ } -> SN.UserAttributes.uaMutable = snd ua_name)
222 (* Check whether this is a function type that (a) either returns a disposable
223 * or (b) has the <<__ReturnDisposable>> attribute
225 let is_return_disposable_fun_type env ty =
226 match Env.expand_type env ty with
227 | _env, (_, Tfun ft) ->
228 ft.ft_return_disposable || Option.is_some (Typing_disposable.is_disposable_type env ft.ft_ret)
229 | _ -> false
231 let enforce_param_not_disposable env param ty =
232 if has_accept_disposable_attribute param then ()
233 else
234 let p = param.param_pos in
235 match Typing_disposable.is_disposable_type env ty with
236 | Some class_name ->
237 Errors.invalid_disposable_hint p (strip_ns class_name)
238 | None ->
241 (* This function is used to determine the type of an argument.
242 * When we want to type-check the body of a function, we need to
243 * introduce the type of the arguments of the function in the environment
244 * Let's take an example, we want to check the code of foo:
246 * function foo(int $x): int {
247 * // CALL TO make_param_type on (int $x)
248 * // Now we know that the type of $x is int
250 * return $x; // in the environment $x is an int, the code is correct
253 * When we localize, we want to resolve to "static" or "$this" depending on
254 * the context. Even though we are passing in CIstatic, resolve_with_class_id
255 * is smart enough to know what to do. Why do this? Consider the following
257 * abstract class C {
258 * abstract const type T;
260 * private this::T $val;
262 * final public function __construct(this::T $x) {
263 * $this->val = $x;
266 * public static function create(this::T $x): this {
267 * return new static($x);
271 * class D extends C { const type T = int; }
273 * In __construct() we want to be able to assign $x to $this->val. The type of
274 * $this->val will expand to '$this::T', so we need $x to also be '$this::T'.
275 * We can do this soundly because when we construct a new class such as,
276 * 'new D(0)' we can determine the late static bound type (D) and resolve
277 * 'this::T' to 'D::T' which is int.
279 * A similar line of reasoning is applied for the static method create.
281 let make_param_local_ty env param =
282 let ety_env =
283 { (Phase.env_with_self env) with from_class = Some CIstatic; } in
284 let env, ty =
285 match param.param_hint with
286 | None when param.param_expr = None ->
287 let r = Reason.Rwitness param.param_pos in
288 env, (r, TUtils.tany env)
289 | None ->
290 (* if the type is missing, use an unbound type variable *)
291 let _r, ty = Env.fresh_type () in
292 let r = Reason.Rwitness param.param_pos in
293 env, (r, ty)
294 | Some x ->
295 let ty = Decl_hint.hint env.Env.decl_env x in
296 Phase.localize ~ety_env env ty
298 let ty = match ty with
299 | _, t when param.param_is_variadic ->
300 (* when checking the body of a function with a variadic
301 * argument, "f(C ...$args)", $args is a varray<C> *)
302 let r = Reason.Rvar_param param.param_pos in
303 let arr_values = r, t in
304 let akind =
305 if TypecheckerOptions.experimental_feature_enabled
306 (Env.get_options env)
307 TypecheckerOptions.experimental_darray_and_varray
308 then AKvarray arr_values
309 else AKvec arr_values in
310 r, Tarraykind akind
311 | x -> x
313 env, ty
315 (* Given a localized parameter type and parameter information, infer
316 * a type for the parameter default expression (if present) and check that
317 * it is a subtype of the parameter type. Set the type of the parameter in
318 * the locals environment *)
319 let rec bind_param env (ty1, param) =
320 let env, param_te, ty2 =
321 match param.param_expr with
322 | None ->
323 (* XXX: We don't want to replace this Tany with Tdynamic, since it
324 represents the lack of a default parameter, which is valid
325 We really want a bottom type here rather than Tany, but this is fine
326 for now *)
327 env, None, (Reason.none, Tany)
328 | Some e ->
329 let env, te, ty = expr ~expected:(param.param_pos, Reason.URparam, ty1) env e in
330 Typing_sequencing.sequence_check_expr e;
331 env, Some te, ty
333 Typing_suggest.save_param (param.param_name) env ty1 ty2;
334 let env = Type.sub_type param.param_pos Reason.URhint env ty2 ty1 in
335 let tparam = {
336 T.param_annotation = T.make_expr_annotation param.param_pos ty1;
337 T.param_hint = param.param_hint;
338 T.param_is_reference = param.param_is_reference;
339 T.param_is_variadic = param.param_is_variadic;
340 T.param_pos = param.param_pos;
341 T.param_name = param.param_name;
342 T.param_expr = param_te;
343 T.param_callconv = param.param_callconv;
344 T.param_user_attributes = List.map param.param_user_attributes (user_attribute env);
345 } in
346 let mode = get_param_mode param.param_is_reference param.param_callconv in
347 let id = Local_id.get param.param_name in
348 let env = Env.set_local env id ty1 in
349 let env = Env.set_param env id (ty1, mode) in
350 let env = if has_accept_disposable_attribute param
351 then Env.set_using_var env id else env in
352 let env = if has_mutable_attribute param
353 then Env.add_mutable_var env id (param.param_pos, Typing_mutability_env.Borrowed)
354 else env in
355 env, tparam
357 (* In strict mode, we force you to give a type declaration on a parameter *)
358 (* But the type checker is nice: it makes a suggestion :-) *)
359 and check_param env param ty =
360 match param.param_hint with
361 | None -> suggest env param.param_pos ty
362 | Some _ ->
363 (* We do not permit hints to implement IDisposable or IAsyncDisposable *)
364 enforce_param_not_disposable env param ty
366 and check_inout_return env =
367 let params = Local_id.Map.elements (Env.get_params env) in
368 List.fold params ~init:env ~f:begin fun env (id, ((r, ty), mode)) ->
369 match mode with
370 | FPinout ->
371 (* Whenever the function exits normally, we require that each local
372 * corresponding to an inout parameter be compatible with the original
373 * type for the parameter (under subtyping rules). *)
374 let local_ty = Env.get_local env id in
375 let env, ety = Env.expand_type env local_ty in
376 let pos = Reason.to_pos (fst ety) in
377 let param_ty = Reason.Rinout_param (Reason.to_pos r), ty in
378 Type.sub_type pos Reason.URassign_inout env ety param_ty
379 | _ -> env
382 and add_decl_errors = function
383 | None -> ()
384 | Some errors -> Errors.merge_into_current errors
386 (*****************************************************************************)
387 (* Now we are actually checking stuff! *)
388 (*****************************************************************************)
389 and fun_def tcopt f =
390 (* reset the expression dependent display ids for each function body *)
391 Reason.expr_display_id_map := IMap.empty;
392 Typing_hooks.dispatch_enter_fun_def_hook f;
393 let pos = fst f.f_name in
394 let nb = TNBody.func_body tcopt f in
395 let env = EnvFromDef.fun_env tcopt f in
396 add_decl_errors (Option.map
397 (Env.get_fun env (snd f.f_name))
398 ~f:(fun x -> Option.value_exn x.ft_decl_errors)
400 let env = Env.set_env_function_pos env pos in
401 let reactive = Decl.fun_reactivity env.Env.decl_env f.f_user_attributes in
402 let mut = TUtils.fun_mutable f.f_user_attributes in
403 let env = Env.set_env_reactive env reactive in
404 let env = Env.set_fun_mutable env mut in
405 NastCheck.fun_ env f nb;
406 (* Fresh type environment is actually unnecessary, but I prefer to
407 * have a guarantee that we are using a clean typing environment. *)
408 let tfun_def = Env.fresh_tenv env (
409 fun env ->
410 let env = Env.set_mode env f.f_mode in
411 let env, constraints =
412 Phase.localize_generic_parameters_with_bounds env f.f_tparams
413 ~ety_env:(Phase.env_with_self env) in
414 let env = add_constraints pos env constraints in
415 let env =
416 localize_where_constraints
417 ~ety_env:(Phase.env_with_self env) env f.f_where_constraints in
418 let env, ty =
419 match f.f_ret with
420 | None ->
421 env, (Reason.Rwitness pos, Typing_utils.tany env)
422 | Some ret ->
423 let ty = TI.instantiable_hint env ret in
424 Phase.localize_with_self env ty in
425 let return = Typing_return.make_info f.f_fun_kind f.f_user_attributes env
426 ~is_explicit:(Option.is_some f.f_ret) ~is_by_ref:f.f_ret_by_ref ty in
427 TI.check_params_instantiable env f.f_params;
428 TI.check_tparams_instantiable env f.f_tparams;
429 let env, param_tys = List.map_env env f.f_params make_param_local_ty in
430 if Env.is_strict env then
431 List.iter2_exn ~f:(check_param env) f.f_params param_tys;
432 Typing_memoize.check_function env f;
433 let env, typed_params = List.map_env env (List.zip_exn param_tys f.f_params)
434 bind_param in
435 let env, t_variadic = match f.f_variadic with
436 | FVvariadicArg vparam ->
437 TI.check_param_instantiable env vparam;
438 let env, ty = make_param_local_ty env vparam in
439 if Env.is_strict env then
440 check_param env vparam ty;
441 let env, t_vparam = bind_param env (ty, vparam) in
442 env, T.FVvariadicArg t_vparam
443 | FVellipsis ->
444 if Env.is_strict env then
445 Errors.ellipsis_strict_mode ~require:`Type_and_param_name pos;
446 env, T.FVellipsis
447 | FVnonVariadic -> env, T.FVnonVariadic in
448 let env, tb = fun_ env return pos nb f.f_fun_kind in
449 let env = Env.check_todo env in
450 if Env.is_strict env then Env.log_anonymous env;
451 begin match f.f_ret with
452 | None when Env.is_strict env ->
453 Typing_return.suggest_return env pos return.Typing_env_return_info.return_type
454 | None -> Typing_suggest.save_fun_or_method f.f_name
455 | Some hint ->
456 Typing_return.async_suggest_return (f.f_fun_kind) hint pos
457 end;
459 T.f_annotation = save_env env;
460 T.f_mode = f.f_mode;
461 T.f_ret = f.f_ret;
462 T.f_name = f.f_name;
463 T.f_tparams = f.f_tparams;
464 T.f_where_constraints = f.f_where_constraints;
465 T.f_variadic = t_variadic;
466 T.f_params = typed_params;
467 T.f_fun_kind = f.f_fun_kind;
468 T.f_user_attributes = List.map f.f_user_attributes (user_attribute env);
469 T.f_body = T.NamedBody {
470 T.fnb_nast = tb;
471 T.fnb_unsafe = nb.fnb_unsafe;
473 T.f_ret_by_ref = f.f_ret_by_ref;
475 ) in
476 Typing_hooks.dispatch_exit_fun_def_hook f;
477 tfun_def
479 (*****************************************************************************)
480 (* function used to type closures, functions and methods *)
481 (*****************************************************************************)
483 and fun_ ?(abstract=false) env return pos named_body f_kind =
484 Env.with_env env begin fun env ->
485 debug_last_pos := pos;
486 let env = Env.set_return env return in
487 let env = Env.set_fn_kind env f_kind in
488 let env, tb = block env named_body.fnb_nast in
489 Typing_sequencing.sequence_check_block named_body.fnb_nast;
490 let { Typing_env_return_info.return_type = ret; _} = Env.get_return env in
491 let env =
492 if Nast_terminality.Terminal.block env named_body.fnb_nast ||
493 abstract ||
494 named_body.fnb_unsafe ||
495 !auto_complete
496 then env
497 else fun_implicit_return env pos ret f_kind in
498 debug_last_pos := Pos.none;
499 env, tb
502 and fun_implicit_return env pos ret = function
503 | Ast.FGenerator | Ast.FAsyncGenerator -> env
504 | Ast.FCoroutine
505 | Ast.FSync ->
506 (* A function without a terminal block has an implicit return; the
507 * "void" type *)
508 let env = check_inout_return env in
509 let rty = Reason.Rno_return pos, Tprim Nast.Tvoid in
510 Typing_suggest.save_return env ret rty;
511 Type.sub_type pos Reason.URreturn env rty ret
512 | Ast.FAsync ->
513 (* An async function without a terminal block has an implicit return;
514 * the Awaitable<void> type *)
515 let r = Reason.Rno_return_async pos in
516 let rty = r, Tclass ((pos, SN.Classes.cAwaitable), [r, Tprim Nast.Tvoid]) in
517 Typing_suggest.save_return env ret rty;
518 Type.sub_type pos Reason.URreturn env rty ret
520 and block env stl =
521 List.map_env env stl stmt
523 (* Set a local; must not be already assigned if it is a using variable *)
524 and set_local ?(is_using_clause = false) env (pos,x) ty =
525 if Env.is_using_var env x
526 then
527 if is_using_clause
528 then Errors.duplicate_using_var pos
529 else Errors.illegal_disposable pos "assigned";
530 let env = Env.set_local env x ty in
531 if is_using_clause then Env.set_using_var env x else env
533 (* Check an individual component in the expression `e` in the
534 * `using (e) { ... }` statement.
535 * This consists of either
536 * a simple assignment `$x = e`, in which `$x` is the using variable, or
537 * an arbitrary expression `e`, in which case a temporary is the using
538 * variable, inaccessible in the source.
539 * Return the typed expression and its type, and any variables that must
540 * be designated as "using variables" for avoiding escapes.
542 and check_using_expr has_await env ((pos, content) as using_clause) =
543 match content with
544 (* Simple assignment to local of form `$lvar = e` *)
545 | Binop (Ast.Eq None, (lvar_pos, Lvar lvar), e) ->
546 let env, te, ty = expr ~is_using_clause:true env e in
547 let env = Typing_disposable.enforce_is_disposable_type env has_await (fst e) ty in
548 let env = set_local ~is_using_clause:true env lvar ty in
549 (* We are assigning a new value to the local variable, so we need to
550 * generate a new expression id
552 let env = Env.set_local_expr_id env (snd lvar) (Ident.tmp()) in
553 env, (T.make_typed_expr pos ty (T.Binop (Ast.Eq None,
554 T.make_typed_expr lvar_pos ty (T.Lvar lvar), te)), [snd lvar])
556 (* Arbitrary expression. This will be assigned to a temporary *)
557 | _ ->
558 let env, typed_using_clause, ty = expr ~is_using_clause:true env using_clause in
559 let env = Typing_disposable.enforce_is_disposable_type env has_await pos ty in
560 env, (typed_using_clause, [])
562 (* Check the using clause e in
563 * `using (e) { ... }` statement (`has_await = false`) or
564 * `await using (e) { ... }` statement (`has_await = true`).
565 * The expression consists of a comma-separated list of expressions (Expr_list)
566 * or a single expression.
567 * Return the typed expression, and any variables that must
568 * be designated as "using variables" for avoiding escapes.
570 and check_using_clause env has_await ((pos, content) as using_clause) =
571 match content with
572 | Expr_list using_clauses ->
573 let env, pairs = List.map_env env using_clauses (check_using_expr has_await) in
574 let typed_using_clauses, vars_list = List.unzip pairs in
575 env, T.make_implicitly_typed_expr pos (T.Expr_list typed_using_clauses),
576 List.concat vars_list
577 | _ ->
578 let env, (typed_using_clause, vars) = check_using_expr has_await env using_clause in
579 env, typed_using_clause, vars
581 (* Require a new construct with disposable *)
582 and enforce_return_disposable _env e =
583 match e with
584 | _, New _ -> ()
585 | _, Call _ -> ()
586 | p, _ ->
587 Errors.invalid_return_disposable p
589 and enforce_mutable_return env e =
590 Typing_mutability.verify_valid_mutable_return_value env env.Env.function_pos e
592 and stmt env = function
593 | Fallthrough ->
594 env, T.Fallthrough
595 | GotoLabel _
596 | Goto _
597 | Noop ->
598 env, T.Noop
599 | Expr e ->
600 let env, te, ty = expr env e in
601 (* NB: this check does belong here and not in expr, even though it only
602 * applies to expressions -- we actually want to perform the check on
603 * statements that are expressions, e.g., "foo();" we want to check, but
604 * "return foo();" we do not even though the expression "foo()" is a
605 * subexpression of the statement "return foo();". *)
606 (match snd e with
607 | Nast.Binop (Ast.Eq _, _, _) -> ()
608 | _ -> Async.enforce_not_awaitable env (fst e) ty);
609 env, T.Expr te
610 | If (e, b1, b2) ->
611 let env, te, _ = expr env e in
612 (* We stash away the locals environment because condition updates it
613 * locally for checking b1. For example, we might have condition
614 * $x === null, or $x instanceof C, which changes the type of $x in
615 * lenv *)
616 let parent_lenv = env.Env.lenv in
617 let env = condition env true e in
618 let env, tb1 = block env b1 in
619 let lenv1 = env.Env.lenv in
620 let env = { env with Env.lenv = parent_lenv } in
621 let env = condition env false e in
622 let env, tb2 = block env b2 in
623 let lenv2 = env.Env.lenv in
624 let terminal1 = Nast_terminality.Terminal.block env b1 in
625 let terminal2 = Nast_terminality.Terminal.block env b2 in
626 let env =
627 if terminal1 && terminal2
628 then
629 let env = LEnv.integrate env parent_lenv lenv1 in
630 let env = LEnv.integrate env env.Env.lenv lenv2 in
631 LEnv.integrate env env.Env.lenv parent_lenv
632 else if terminal1
633 then begin
634 let env = LEnv.integrate env parent_lenv lenv1 in
635 LEnv.integrate env env.Env.lenv lenv2
637 else if terminal2
638 then begin
639 let env = LEnv.integrate env parent_lenv lenv2 in
640 LEnv.integrate env env.Env.lenv lenv1
642 else LEnv.intersect env parent_lenv lenv1 lenv2 in
643 (* TODO TAST: annotate with joined types *)
644 env, T.If(te, tb1, tb2)
645 | Return (p, None) ->
646 let env = check_inout_return env in
647 let rty = Typing_return.wrap_awaitable env p (Reason.Rwitness p, Tprim Tvoid) in
648 let { Typing_env_return_info.return_type = expected_return; _ } = Env.get_return env in
649 Typing_suggest.save_return env expected_return rty;
650 let env = Type.sub_type p Reason.URreturn env rty expected_return in
651 env, T.Return (p, None)
652 | Return (p, Some e) ->
653 let env = check_inout_return env in
654 let pos = fst e in
655 let Typing_env_return_info.{
656 return_type; return_disposable; return_mutable; return_explicit; return_by_ref } =
657 Env.get_return env in
658 let expected =
659 if return_explicit
660 then Some (pos, Reason.URreturn,
661 Typing_return.strip_awaitable (Env.get_fn_kind env) env return_type)
662 else Some (pos, Reason.URreturn, (Reason.Rwitness p, Typing_utils.tany env)) in
663 if return_disposable then enforce_return_disposable env e;
664 let env, te, rty = expr ~is_using_clause:return_disposable ?expected:expected env e in
665 if return_mutable then enforce_mutable_return env te;
666 if return_by_ref && TypecheckerOptions.experimental_feature_enabled
667 (Env.get_options env)
668 TypecheckerOptions.experimental_disallow_refs_in_array
669 then begin match snd e with
670 | Array_get _ -> Errors.return_ref_in_array p
671 | _ -> ()
672 end;
673 let rty = Typing_return.wrap_awaitable env p rty in
674 (match snd (Env.expand_type env return_type) with
675 | r, Tprim Tvoid ->
676 (* Yell about returning a value from a void function. This catches
677 * more issues than just unifying with void would do -- in particular
678 * just unifying allows you to return a Typing_utils.tany env from a void function,
679 * which is clearly wrong. Note this check is best-effort; if the
680 * function returns a generic type which later ends up being Tvoid
681 * then there's not much we can do here. *)
682 Errors.return_in_void p (Reason.to_pos r);
683 env, T.Return(p, Some te)
684 | _, Tunresolved _ ->
685 (* we allow return types to grow for anonymous functions *)
686 let env, rty = TUtils.unresolved env rty in
687 let env = Type.sub_type pos Reason.URreturn env rty return_type in
688 env, T.Return(p, Some te)
689 | _, (Terr | Tany | Tmixed | Tnonnull | Tarraykind _ | Toption _ | Tprim _
690 | Tvar _ | Tfun _ | Tabstract (_, _) | Tclass (_, _) | Ttuple _
691 | Tanon (_, _) | Tobject | Tshape _ | Tdynamic) ->
692 Typing_suggest.save_return env return_type rty;
693 let env = Type.sub_type pos Reason.URreturn env rty return_type in
694 env, T.Return(p, Some te)
696 | Do (b, e) as st ->
697 (* NOTE: leaks scope as currently implemented; this matches
698 the behavior in naming (cf. `do_stmt` in naming/naming.ml).
700 let parent_lenv = env.Env.lenv in
701 let env = Env.freeze_local_env env in
702 let env, _ = block env b in
703 let env, te, _ = expr env e in
704 let after_block = env.Env.lenv in
705 let alias_depth =
706 if env.Env.in_loop then 1 else Typing_alias.get_depth st in
707 let env, tb = Env.in_loop env begin
708 iter_n_acc alias_depth begin fun env ->
709 let env = condition env true e in
710 let env, tb = block env b in
711 env, tb
712 end end in
713 let env =
714 if Nast.Visitor.HasContinue.block b
715 then LEnv.fully_integrate env parent_lenv
716 else { env with Env.lenv = after_block } in
717 let env = condition env false e in
718 env, T.Do(tb, te)
719 | While (e, b) as st ->
720 let env, te, _ = expr env e in
721 let parent_lenv = env.Env.lenv in
722 let env = Env.freeze_local_env env in
723 let alias_depth =
724 if env.Env.in_loop then 1 else Typing_alias.get_depth st in
725 let env, tb = Env.in_loop env begin
726 iter_n_acc alias_depth begin fun env ->
727 let env = condition env true e in
728 (* TODO TAST: avoid repeated generation of block *)
729 let env, tb = block env b in
730 env, tb
732 end in
733 let env = LEnv.fully_integrate env parent_lenv in
734 let env = condition env false e in
735 env, T.While (te, tb)
736 | Using (has_await, using_clause, using_block) ->
737 let env, typed_using_clause, using_vars = check_using_clause env has_await using_clause in
738 let env, typed_using_block = block env using_block in
739 (* Remove any using variables from the environment, as they should not
740 * be in scope outside the block *)
741 let env = List.fold_left using_vars ~init:env ~f:Env.unset_local in
742 env, T.Using (has_await, typed_using_clause, typed_using_block)
743 | For (e1, e2, e3, b) as st ->
744 (* For loops leak their initalizer, but nothing that's defined in the
745 body
747 let (env, te1, _) = expr env e1 in (* initializer *)
748 let (env, te2, _) = expr env e2 in
749 let parent_lenv = env.Env.lenv in
750 let env = Env.freeze_local_env env in
751 let alias_depth =
752 if env.Env.in_loop then 1 else Typing_alias.get_depth st in
753 let env, (tb, te3) = Env.in_loop env begin
754 iter_n_acc alias_depth begin fun env ->
755 let env = condition env true e2 in (* iteration 0 *)
756 let env, tb = block env b in
757 let (env, te3, _) = expr env e3 in
758 env, (tb, te3)
760 end in
761 let env = LEnv.fully_integrate env parent_lenv in
762 let env = condition env false e2 in
763 env, T.For(te1, te2, te3, tb)
764 | Switch (e, cl) ->
765 let cl = List.map ~f:drop_dead_code_after_break cl in
766 Nast_terminality.SafeCase.check (fst e) env cl;
767 let env, te, ty = expr env e in
768 Async.enforce_not_awaitable env (fst e) ty;
769 let env = check_exhaustiveness env (fst e) ty cl in
770 let parent_lenv = env.Env.lenv in
771 let env, cl, tcl = case_list parent_lenv ty env cl in
772 let env = LEnv.intersect_nonterminal_branches env parent_lenv cl in
773 env, T.Switch(te, tcl)
774 | Foreach (e1, e2, b) as st ->
775 let check_dynamic env ty ~f =
776 if TUtils.is_dynamic env ty then
778 else f() in
779 (* It's safe to do foreach over a disposable, as no leaking is possible *)
780 let env, te1, ty1 = expr ~accept_using_var:true env e1 in
781 let parent_lenv = env.Env.lenv in
782 let env = Env.freeze_local_env env in
783 let env, ty2 = as_expr env (fst e1) e2 in
784 let env =
785 check_dynamic env ty1 ~f:begin fun () ->
786 Type.sub_type (fst e1) Reason.URforeach env ty1 ty2
787 end in
788 let alias_depth =
789 if env.Env.in_loop then 1 else Typing_alias.get_depth st in
790 let env, (te2, tb) = Env.in_loop env begin
791 iter_n_acc alias_depth begin fun env ->
792 let env, te2 = bind_as_expr env ty1 ty2 e2 in
793 let env, tb = block env b in
794 env, (te2, tb)
796 end in
797 let env = LEnv.fully_integrate env parent_lenv in
798 env, T.Foreach (te1, te2, tb)
799 | Try (tb, cl, fb) ->
800 let env, ttb, tcl, tfb = try_catch env tb cl fb in
801 env, T.Try (ttb, tcl, tfb)
802 | Static_var el ->
803 Typing_reactivity.disallow_static_or_global_in_reactive_context env el
804 ~is_static:true;
805 let env = List.fold_left el ~f:begin fun env e ->
806 match e with
807 | _, Binop (Ast.Eq _, (_, Lvar (p, x)), _) ->
808 Env.add_todo env (TGen.no_generic p x)
809 | _ -> env
810 end ~init:env in
811 let env, tel, _ = exprs env el in
812 env, T.Static_var tel
813 | Global_var el ->
814 Typing_reactivity.disallow_static_or_global_in_reactive_context env el
815 ~is_static:false;
816 let env = List.fold_left el ~f:begin fun env e ->
817 match e with
818 | _, Binop (Ast.Eq _, (_, Lvar (p, x)), _) ->
819 Env.add_todo env (TGen.no_generic p x)
820 | _ -> env
821 end ~init:env in
822 let env, tel, _ = exprs env el in
823 env, T.Global_var tel
824 | Throw (is_terminal, e) ->
825 let p = fst e in
826 let env, te, ty = expr env e in
827 let env = exception_ty p env ty in
828 env, T.Throw(is_terminal, te)
829 | Continue p ->
830 env, T.Continue p
831 | Break p ->
832 env, T.Break p
834 and check_exhaustiveness env pos ty caselist =
835 check_exhaustiveness_ env pos ty caselist false
837 and check_exhaustiveness_ env pos ty caselist enum_coming_from_unresolved =
838 (* Right now we only do exhaustiveness checking for enums. *)
839 (* This function has a built in hack where if Tunresolved has an enum
840 inside then it tells the enum exhaustiveness checker to
841 not punish for extra default *)
842 let env, (_, ty) = Env.expand_type env ty in
843 match ty with
844 | Tunresolved tyl ->
845 let new_enum = enum_coming_from_unresolved ||
846 (List.length tyl> 1 && List.exists tyl ~f:begin fun cur_ty ->
847 let _, (_, cur_ty) = Env.expand_type env cur_ty in
848 match cur_ty with
849 | Tabstract (AKenum _, _) -> true
850 | _ -> false
851 end) in
852 List.fold_left tyl ~init:env ~f:begin fun env ty ->
853 check_exhaustiveness_ env pos ty caselist new_enum
855 | Tabstract (AKenum id, _) ->
856 let dep = Dep.AllMembers id in
857 Option.iter env.Env.decl_env.Decl_env.droot
858 (fun root -> Typing_deps.add_idep root dep);
859 let tc = unsafe_opt @@ Env.get_enum env id in
860 Typing_enum.check_enum_exhaustiveness pos tc
861 caselist enum_coming_from_unresolved;
863 | Terr | Tany | Tmixed | Tnonnull | Tarraykind _ | Tclass _ | Toption _
864 | Tprim _ | Tvar _ | Tfun _ | Tabstract (_, _) | Ttuple _ | Tanon (_, _)
865 | Tobject | Tshape _ | Tdynamic -> env
867 and case_list parent_lenv ty env cl =
868 let env = { env with Env.lenv = parent_lenv } in
869 case_list_ parent_lenv ty env cl
871 and try_catch env tb cl fb =
872 let unfrozen_parent_lenv = env.Env.lenv in
873 let env = Env.freeze_local_env env in
874 let parent_lenv = env.Env.lenv in
875 let env, ttb = block env tb in
876 let after_try = env.Env.lenv in
877 let env, term_lenv_tcb_l = List.map_env env cl
878 begin fun env (_, _, b as catch_block) ->
879 let env, lenv, tcb = catch parent_lenv after_try env catch_block in
880 let term = Nast_terminality.Terminal.block env b in
881 env, (term, lenv, tcb)
882 end in
883 let term_lenv_l = List.map term_lenv_tcb_l (fun (a, b, _) -> (a, b)) in
884 let tcb_l = List.map term_lenv_tcb_l (fun (_, _, a) -> a) in
885 let term_lenv_l =
886 (Nast_terminality.Terminal.block env tb, after_try) :: term_lenv_l in
887 let env, tfb = if List.is_empty fb then env, [] else begin
888 let after_catchl = env.Env.lenv in
889 let lenv_l = List.map term_lenv_l (fun (_, a) -> a) in
890 let env = LEnv.integrate_list env parent_lenv lenv_l in
891 let env = LEnv.fully_integrate env parent_lenv in
892 let env, tfb = block env fb in
893 { env with Env.lenv = after_catchl }, tfb
894 end in
895 let env = LEnv.intersect_nonterminal_branches env parent_lenv term_lenv_l in
896 let env, _ = block env fb in
897 let after_finally = env.Env.lenv in
898 let env = LEnv.integrate env after_finally unfrozen_parent_lenv in
899 let env = LEnv.integrate env unfrozen_parent_lenv after_finally in
900 env, ttb, tcb_l, tfb
902 and drop_dead_code_after_break_block = function
903 | [] -> [], false
904 | Break x :: _ -> [Break x], true
905 | x :: rest ->
906 let x', drop =
907 match x with
908 | If (_, [], []) as if_stmt -> if_stmt, false
909 | If (cond, b1, b2) ->
910 let b1, drop1 = if b1 = [] then [], true
911 else drop_dead_code_after_break_block b1 in
912 let b2, drop2 = if b2 = [] then [], true
913 else drop_dead_code_after_break_block b2 in
914 If (cond, b1, b2), drop1 && drop2
915 | x -> x, false
917 if drop then ([x'], true) else begin
918 let rest', drop = drop_dead_code_after_break_block rest in
919 x'::rest', drop
922 and drop_dead_code_after_break = function
923 | Default b -> Default (fst (drop_dead_code_after_break_block b))
924 | Case (e, b) -> Case (e, fst (drop_dead_code_after_break_block b))
926 and case_list_ parent_lenv ty env = function
927 | [] -> env, [], []
928 | Default b :: _ ->
929 (* TODO this is wrong, should continue on to the other cases, but it
930 * doesn't matter in practice since our parser won't parse default
931 * anywhere but in the last position :) Should fix all of this as well
932 * as totality detection for switch. *)
933 let env, tb = block env b in
934 env, [Nast_terminality.Terminal.case env (Default b), env.Env.lenv],
935 [T.Default tb]
936 | (Case (e, b)) as ce :: rl ->
937 (* TODO - we should consider handling the comparisons the same
938 * way as Binop Ast.EqEq, since case statements work using ==
939 * comparison rules *)
941 (* The way we handle terminal/nonterminal here is not quite right, you
942 * can still break the type system with things like P3131824. *)
943 let ty_num = (Reason.Rnone, Tprim Nast.Tnum) in
944 let ty_arraykey = (Reason.Rnone, Tprim Nast.Tarraykey) in
945 let both_are_sub_types env tprim ty1 ty2 =
946 (SubType.is_sub_type env ty1 tprim) &&
947 (SubType.is_sub_type env ty2 tprim) in
948 let env, te, ty2 = expr env e in
949 let env, _ = if (both_are_sub_types env ty_num ty ty2) ||
950 (both_are_sub_types env ty_arraykey ty ty2)
951 then env, ty
952 else Type.unify (fst e) Reason.URnone env ty ty2 in
954 if Nast_terminality.Terminal.block env b then
955 let env, tb = block env b in
956 let lenv = env.Env.lenv in
957 let env, rl, tcl = case_list parent_lenv ty env rl in
958 env, (Nast_terminality.Terminal.case env ce, lenv) :: rl, T.Case (te, tb)::tcl
959 else
960 (* Since this block is not terminal we will end up falling through to the
961 * next block. This means the lenv will include what our current
962 * environment is, intersected (or integrated?) with the environment
963 * after executing the block. Example:
965 * $x = 0; // $x = int
966 * switch (0) {
967 * case 1:
968 * $x = ''; // $x = string
969 * // FALLTHROUGH
970 * case 2:
971 * $x; // $x = int & string
972 * ...
974 let lenv1 = env.Env.lenv in
975 let env, tb = block env b in
976 (* PERF: If the case is empty or a Noop then we do not need to intersect
977 * the lenv since they will be the same.
979 * This saves the cost of intersecting the lenv for the common pattern of
980 * case 1:
981 * case 2:
982 * case 3:
983 * ...
985 let env = match b with
986 | [] | [Noop] -> env
987 | _ -> LEnv.intersect env parent_lenv lenv1 env.Env.lenv in
988 let env, rl, tcl = case_list_ parent_lenv ty env rl in
989 env, rl, T.Case (te, tb)::tcl
991 and catch parent_lenv after_try env (sid, exn, b) =
992 let env = { env with Env.lenv = after_try } in
993 let env = LEnv.fully_integrate env parent_lenv in
994 let cid = CI (sid, []) in
995 let ety_p = (fst sid) in
996 TUtils.process_class_id cid;
997 let env, _, _ = instantiable_cid ety_p env cid in
998 let env, _te, ety = static_class_id ety_p env cid in
999 let env = exception_ty ety_p env ety in
1000 let env = set_local env exn ety in
1001 let env, tb = block env b in
1002 (* Only keep the local bindings if this catch is non-terminal *)
1003 env, env.Env.lenv, (sid, exn, tb)
1005 and as_expr env pe =
1006 let make_result ty = env, ty in
1007 function
1008 | As_v _ ->
1009 let ty = Env.fresh_type() in
1010 let tvector = Tclass ((pe, SN.Collections.cTraversable), [ty]) in
1011 make_result (Reason.Rforeach pe, tvector)
1012 | As_kv _ ->
1013 let ty1 = Env.fresh_type() in
1014 let ty2 = Env.fresh_type() in
1015 let tmap = Tclass((pe, SN.Collections.cKeyedTraversable), [ty1; ty2]) in
1016 make_result (Reason.Rforeach pe, tmap)
1017 | Await_as_v _ ->
1018 let ty = Env.fresh_type() in
1019 let tvector = Tclass ((pe, SN.Classes.cAsyncIterator), [ty]) in
1020 make_result (Reason.Rasyncforeach pe, tvector)
1021 | Await_as_kv _ ->
1022 let ty1 = Env.fresh_type() in
1023 let ty2 = Env.fresh_type() in
1024 let tmap = Tclass ((pe, SN.Classes.cAsyncKeyedIterator), [ty1; ty2]) in
1025 make_result (Reason.Rasyncforeach pe, tmap)
1027 and bind_as_expr env loop_ty ty aexpr =
1028 let env, ety = Env.expand_type env ty in
1029 let p, ty1, ty2 =
1030 match ety with
1031 | _, Tclass ((p, _), [ty2]) ->
1032 (p, (Reason.Rnone, TUtils.desugar_mixed env Reason.Rnone), ty2)
1033 | _, Tclass ((p, _), [ty1; ty2]) -> (p, ty1, ty2)
1034 | _ -> assert false in
1035 (* Set id as dynamic if the foreach loop was dynamic *)
1036 let env, eloop_ty = Env.expand_type env loop_ty in
1037 let ty1, ty2 = if TUtils.is_dynamic env eloop_ty then
1038 (fst ty1, Tdynamic), (fst ty2, Tdynamic) else ty1, ty2 in
1039 match aexpr with
1040 | As_v ev ->
1041 let env, te, _ = assign p env ev ty2 in
1042 env, T.As_v te
1043 | Await_as_v (p, ev) ->
1044 let env, te, _ = assign p env ev ty2 in
1045 env, T.Await_as_v(p, te)
1046 | As_kv ((p, Lvar ((_, k) as id)), ev) ->
1047 let env, ty1' = set_valid_rvalue p env k ty1 in
1048 let env, te, _ = assign p env ev ty2 in
1049 env, T.As_kv(T.make_typed_expr p ty1' (T.Lvar id), te)
1050 | Await_as_kv (p, (p1, Lvar ((_, k) as id)), ev) ->
1051 let env, ty1' = set_valid_rvalue p env k ty1 in
1052 let env, te, _ = assign p env ev ty2 in
1053 env, T.Await_as_kv(p, T.make_typed_expr p1 ty1' (T.Lvar id), te)
1054 | _ -> (* TODO Probably impossible, should check that *)
1055 assert false
1057 and expr
1058 ?expected
1059 ?(accept_using_var = false)
1060 ?(is_using_clause = false)
1061 ?is_func_arg
1062 ?forbid_uref
1063 env e =
1064 begin match expected with
1065 | None -> ()
1066 | Some (_, r, ty) ->
1067 Typing_log.log_types 1 (fst e) env
1068 [Typing_log.Log_sub ("Typing.expr " ^ Typing_reason.string_of_ureason r,
1069 [Typing_log.Log_type ("expected_ty", ty)])] end;
1070 raw_expr ~accept_using_var ~is_using_clause ?is_func_arg ?forbid_uref ?expected ~in_cond:false env e
1072 and raw_expr
1073 ~in_cond
1074 ?(accept_using_var = false)
1075 ?(is_using_clause = false)
1076 ?expected
1077 ?lhs_of_null_coalesce
1078 ?is_func_arg
1079 ?forbid_uref
1080 ?valkind:(valkind=`other)
1081 env e =
1082 debug_last_pos := fst e;
1083 let env, te, ty =
1084 expr_ ~in_cond ~accept_using_var ~is_using_clause ?expected
1085 ?lhs_of_null_coalesce ?is_func_arg ?forbid_uref
1086 ~valkind env e in
1087 let () = match !expr_hook with
1088 | Some f -> f e (Typing_expand.fully_expand env ty)
1089 | None -> () in
1090 env, te, ty
1092 and lvalue env e =
1093 let valkind = `lvalue in
1094 expr_ ~in_cond:false ~valkind env e
1096 and is_pseudo_function s =
1097 s = SN.PseudoFunctions.hh_show ||
1098 s = SN.PseudoFunctions.hh_show_env ||
1099 s = SN.PseudoFunctions.hh_log_level
1101 (* $x ?? 0 is handled similarly to $x ?: 0, except that the latter will also
1102 * look for sketchy null checks in the condition. *)
1103 (* TODO TAST: type refinement should be made explicit in the typed AST *)
1104 and eif env ~expected ~coalesce ~in_cond p c e1 e2 =
1105 let condition = condition ~lhs_of_null_coalesce:coalesce in
1106 let env, tc, tyc = raw_expr ~in_cond ~lhs_of_null_coalesce:coalesce env c in
1107 let parent_lenv = env.Env.lenv in
1108 let c = if coalesce then (p, Binop (Ast.Diff2, c, (p, Null))) else c in
1109 let env = condition env true c in
1110 let env, te1, ty1 = match e1 with
1111 | None ->
1112 let env, ty = TUtils.non_null env tyc in
1113 env, None, ty
1114 | Some e1 ->
1115 let env, te1, ty1 = expr ?expected env e1 in
1116 env, Some te1, ty1
1118 let lenv1 = env.Env.lenv in
1119 let env = { env with Env.lenv = parent_lenv } in
1120 let env = condition env false c in
1121 let env, te2, ty2 = expr ?expected env e2 in
1122 let lenv2 = env.Env.lenv in
1123 let fake_members =
1124 LEnv.intersect_fake lenv1.Env.fake_members lenv2.Env.fake_members in
1125 (* we restore the locals to their parent state so as not to leak the
1126 * effects of the `condition` calls above *)
1127 let env = { env with Env.lenv =
1128 { parent_lenv with Env.fake_members = fake_members } } in
1129 (* This is a shortened form of what we do in Typing_lenv.intersect. The
1130 * latter takes local environments as arguments, but our types here
1131 * aren't assigned to local variables in an environment *)
1132 (* TODO: Omit if expected type is present and checked in calls to expr *)
1133 let env, ty1 = TUtils.unresolved env ty1 in
1134 let env, ty2 = TUtils.unresolved env ty2 in
1135 let env, ty = Unify.unify env ty1 ty2 in
1136 let te = if coalesce then T.NullCoalesce(tc, te2) else T.Eif(tc, te1, te2) in
1137 env, T.make_typed_expr p ty te, ty
1139 and check_escaping_var env (pos, x) =
1140 if Env.is_using_var env x
1141 then
1142 if x = this
1143 then Errors.escaping_this pos
1144 else
1145 if Option.is_some (Local_id.Map.get x (Env.get_params env))
1146 then Errors.escaping_disposable_parameter pos
1147 else Errors.escaping_disposable pos
1148 else ()
1150 and exprs ?(accept_using_var = false) ?is_func_arg ?expected env el =
1151 match el with
1152 | [] ->
1153 env, [], []
1155 | e::el ->
1156 let env, te, ty = expr ~accept_using_var ?is_func_arg ?expected env e in
1157 let env, tel, tyl = exprs ~accept_using_var ?is_func_arg ?expected env el in
1158 env, te::tel, ty::tyl
1160 and exprs_expected (pos, ur, expected_tyl) env el =
1161 match el, expected_tyl with
1162 | [], _ ->
1163 env, [], []
1164 | e::el, expected_ty::expected_tyl ->
1165 let env, te, ty = expr ~expected:(pos, ur, expected_ty) env e in
1166 let env, tel, tyl = exprs_expected (pos, ur, expected_tyl) env el in
1167 env, te::tel, ty::tyl
1168 | el, [] ->
1169 exprs env el
1171 and expr_
1172 ?expected
1173 ~in_cond
1174 ?(accept_using_var = false)
1175 ?(is_using_clause = false)
1176 ?lhs_of_null_coalesce
1177 ?(is_func_arg=false)
1178 ?(forbid_uref=false)
1179 ~(valkind: [> `lvalue | `lvalue_subexpr | `other ])
1180 env (p, e) =
1181 let make_result env te ty =
1182 env, T.make_typed_expr p ty te, ty in
1185 * Given a list of types, computes their supertype. If any of the types are
1186 * unknown (e.g., comes from PHP), the supertype will be Typing_utils.tany env.
1188 let compute_supertype ~expected env tys =
1189 let env, supertype =
1190 match expected with
1191 | None -> Env.fresh_unresolved_type env
1192 | Some (_, _, ty) -> env, ty in
1193 let has_unknown = List.exists tys (fun (_, ty) -> ty = Typing_utils.tany env) in
1194 let env, tys = List.map_env env tys TUtils.unresolved in
1195 let subtype_value env ty =
1196 Type.sub_type p Reason.URarray_value env ty supertype in
1197 if has_unknown then
1198 (* If one of the values comes from PHP land, we have to be conservative
1199 * and consider that we don't know what the type of the values are. *)
1200 env, (Reason.Rwitness p, Typing_utils.tany env)
1201 else
1202 let env = List.fold_left tys ~init:env ~f:subtype_value in
1203 env, supertype in
1206 * Given a 'a list and a method to extract an expr and its ty from a 'a, this
1207 * function extracts a list of exprs from the list, and computes the supertype
1208 * of all of the expressions' tys.
1210 let compute_exprs_and_supertype ~expected env l extract_expr_and_ty =
1211 let env, exprs_and_tys = List.map_env env l (extract_expr_and_ty ~expected) in
1212 let exprs, tys = List.unzip exprs_and_tys in
1213 let env, supertype = compute_supertype ~expected env tys in
1214 env, exprs, supertype in
1216 let shape_and_tuple_arrays_enabled =
1217 not @@
1218 TypecheckerOptions.experimental_feature_enabled
1219 (Env.get_options env)
1220 TypecheckerOptions.experimental_disable_shape_and_tuple_arrays in
1222 let check_call ~is_using_clause ~expected env p call_type e hl el uel ~in_suspend=
1223 let env, te, result =
1224 dispatch_call ~is_using_clause ~expected p env call_type e hl el uel ~in_suspend in
1225 let env = Env.forget_members env p in
1226 env, te, result in
1228 match e with
1229 | Any -> expr_error env p (Reason.Rwitness p)
1230 | Array [] ->
1231 (* TODO: use expected type to determine expected element type *)
1232 make_result env (T.Array []) (Reason.Rwitness p, Tarraykind AKempty)
1233 | Array l
1234 (* TODO: use expected type to determine expected element type *)
1235 when Typing_arrays.is_shape_like_array env l &&
1236 shape_and_tuple_arrays_enabled ->
1237 let env, (tafl, fdm) = List.fold_left_env env l
1238 ~init:([], ShapeMap.empty)
1239 ~f:begin fun env (tafl,fdm) x ->
1240 let env, taf, (key, value) = akshape_field env x in
1241 env, (taf::tafl, Nast.ShapeMap.add key value fdm)
1242 end in
1243 make_result env (T.Array(List.rev tafl))
1244 (Reason.Rwitness p, Tarraykind (AKshape fdm))
1246 | Array (x :: rl as l) ->
1247 (* True if all fields are values, or all fields are key => value *)
1248 let fields_consistent = check_consistent_fields x rl in
1249 let is_vec = match x with
1250 | Nast.AFvalue _ -> true
1251 | Nast.AFkvalue _ -> false in
1252 if fields_consistent && is_vec then
1253 (* Use expected type to determine expected element type *)
1254 let env, elem_expected =
1255 match expand_expected env expected with
1256 | env, Some (pos, ur, ety) ->
1257 begin match get_akvec_inst ety with
1258 | Some vty -> env, Some (pos, ur, vty)
1259 | None -> env, None
1261 | _ ->
1262 env, None in
1263 let env, tel, arraykind =
1264 if shape_and_tuple_arrays_enabled then
1265 let env, tel, fields =
1266 List.foldi l ~f:begin fun index (env, tel, acc) e ->
1267 let env, te, ty = aktuple_field env e in
1268 env, te::tel, IMap.add index ty acc
1269 end ~init:(env, [], IMap.empty) in
1270 env, tel, AKtuple fields
1271 else
1272 let env, tel, value_ty =
1273 compute_exprs_and_supertype ~expected:elem_expected env l array_field_value in
1274 env, tel, AKvec value_ty in
1275 make_result env
1276 (T.Array (List.map tel (fun e -> T.AFvalue e)))
1277 (Reason.Rwitness p, Tarraykind arraykind)
1278 else
1280 (* TODO TAST: produce a typed expression here *)
1281 if is_vec
1282 then
1283 (* Use expected type to determine expected element type *)
1284 let env, vexpected =
1285 match expand_expected env expected with
1286 | env, Some (pos, ur, ety) ->
1287 begin match get_akvec_inst ety with
1288 | Some vty -> env, Some (pos, ur, vty)
1289 | None -> env, None
1291 | _ ->
1292 env, None in
1293 let env, _value_exprs, value_ty =
1294 compute_exprs_and_supertype ~expected:vexpected env l array_field_value in
1295 make_result env T.Any
1296 (Reason.Rwitness p, Tarraykind (AKvec value_ty))
1297 else
1298 (* Use expected type to determine expected element type *)
1299 let env, kexpected, vexpected =
1300 match expand_expected env expected with
1301 | env, Some (pos, ur, ety) ->
1302 begin match get_akmap_inst ety with
1303 | Some (kty, vty) -> env, Some (pos, ur, kty), Some (pos, ur, vty)
1304 | None -> env, None, None
1306 | _ ->
1307 env, None, None in
1308 let env, key_exprs, key_ty =
1309 compute_exprs_and_supertype ~expected:kexpected env l array_field_key in
1310 let env, value_exprs, value_ty =
1311 compute_exprs_and_supertype ~expected:vexpected env l array_field_value in
1312 make_result env
1313 (T.Array (List.map (List.zip_exn key_exprs value_exprs)
1314 (fun (tek, tev) -> T.AFkvalue (tek, tev))))
1315 (Reason.Rwitness p, Tarraykind (AKmap (key_ty, value_ty)))
1317 | Darray l ->
1318 (* Use expected type to determine expected key and value types *)
1319 let env, kexpected, vexpected =
1320 match expand_expected env expected with
1321 | env, Some (pos, ur, ety) ->
1322 begin match get_darray_inst ety with
1323 | Some (kty, vty) ->
1324 env, Some (pos, ur, kty), Some (pos, ur, vty)
1325 | None ->
1326 env, None, None
1328 | _ ->
1329 env, None, None in
1330 let keys, values = List.unzip l in
1332 let env, value_exprs, value_ty =
1333 compute_exprs_and_supertype ~expected:vexpected env values array_value in
1334 let env, key_exprs, key_ty =
1335 compute_exprs_and_supertype ~expected:kexpected env keys array_value in
1337 let field_exprs = List.zip_exn key_exprs value_exprs in
1338 make_result env
1339 (T.Darray field_exprs)
1340 (Reason.Rwitness p, Tarraykind (AKdarray (key_ty, value_ty)))
1342 | Varray values ->
1343 (* Use expected type to determine expected element type *)
1344 let env, elem_expected =
1345 match expand_expected env expected with
1346 | env, Some (pos, ur, ety) ->
1347 begin match get_varray_inst ety with
1348 | Some vty ->
1349 env, Some (pos, ur, vty)
1350 | _ ->
1351 env, None
1353 | _ ->
1354 env, None
1356 let env, value_exprs, value_ty =
1357 compute_exprs_and_supertype ~expected:elem_expected env values array_value in
1358 make_result env
1359 (T.Varray value_exprs)
1360 (Reason.Rwitness p, Tarraykind (AKvarray value_ty))
1362 | ValCollection (kind, el) ->
1363 (* Use expected type to determine expected element type *)
1364 let env, elem_expected =
1365 match expand_expected env expected with
1366 | env, Some (pos, ur, ety) ->
1367 begin match get_vc_inst kind ety with
1368 | Some vty ->
1369 env, Some (pos, ur, vty)
1370 | None ->
1371 env, None
1373 | _ -> env, None in
1374 let env, tel, tyl = exprs ?expected:elem_expected env el in
1375 let env, tyl = List.map_env env tyl Typing_env.unbind in
1376 let env, elem_ty, tyl =
1377 match elem_expected with
1378 | Some (_, _, ty) -> env, ty, tyl
1379 | None ->
1380 let env, elem_ty = Env.fresh_unresolved_type env in
1381 let env, tyl = List.map_env env tyl TUtils.unresolved in
1382 env, elem_ty, tyl in
1383 let subtype_val env ty =
1384 Type.sub_type p Reason.URvector env ty elem_ty in
1385 let env =
1386 List.fold_left tyl ~init:env ~f:subtype_val in
1387 let tvector = Tclass ((p, vc_kind_to_name kind), [elem_ty]) in
1388 let ty = Reason.Rwitness p, tvector in
1389 make_result env (T.ValCollection (kind, tel)) ty
1390 | KeyValCollection (kind, l) ->
1391 (* Use expected type to determine expected key and value types *)
1392 let env, kexpected, vexpected =
1393 match expand_expected env expected with
1394 | env, Some (pos, ur, ety) ->
1395 begin match get_kvc_inst kind ety with
1396 | Some (kty, vty) ->
1397 env, Some (pos, ur, kty), Some (pos, ur, vty)
1398 | None ->
1399 env, None, None
1401 | _ -> env, None, None in
1402 let kl, vl = List.unzip l in
1403 let env, tkl, kl = exprs ?expected:kexpected env kl in
1404 let env, tvl, vl = exprs ?expected:vexpected env vl in
1405 let env, kl = List.map_env env kl Typing_env.unbind in
1406 let env, k, kl =
1407 match kexpected with
1408 | Some (_, _, k) -> env, k, kl
1409 | None ->
1410 let env, k = Env.fresh_unresolved_type env in
1411 let env, kl = List.map_env env kl TUtils.unresolved in
1412 env, k, kl in
1413 let env, vl = List.map_env env vl Typing_env.unbind in
1414 let env, v, vl =
1415 match vexpected with
1416 | Some (_, _, v) -> env, v, vl
1417 | None ->
1418 let env, v = Env.fresh_unresolved_type env in
1419 let env, vl = List.map_env env vl TUtils.unresolved in
1420 env, v, vl in
1421 let subtype_key env ty = Type.sub_type p Reason.URkey env ty k in
1422 let env =
1423 List.fold_left kl ~init:env ~f:subtype_key in
1424 let subtype_val env ty = Type.sub_type p Reason.URvalue env ty v in
1425 let env =
1426 List.fold_left vl ~init:env ~f:subtype_val in
1427 let ty = Tclass ((p, kvc_kind_to_name kind), [k; v])
1429 make_result env (T.KeyValCollection (kind, List.zip_exn tkl tvl))
1430 (Reason.Rwitness p, ty)
1431 | Clone e ->
1432 let env, te, ty = expr env e in
1433 (* Clone only works on objects; anything else fatals at runtime *)
1434 let tobj = (Reason.Rwitness p, Tobject) in
1435 let env = Type.sub_type p Reason.URclone env ty tobj in
1436 make_result env (T.Clone te) ty
1437 | This when Env.is_static env ->
1438 Errors.this_in_static p;
1439 expr_error env p (Reason.Rwitness p)
1440 | This when valkind = `lvalue ->
1441 Errors.this_lvalue p;
1442 expr_error env p (Reason.Rwitness p)
1443 | This ->
1444 let r, _ = Env.get_self env in
1445 if r = Reason.Rnone
1446 then Errors.this_var_outside_class p;
1447 if not accept_using_var
1448 then check_escaping_var env (p,this);
1449 let (_, ty) = Env.get_local env this in
1450 let r = Reason.Rwitness p in
1451 let ty = (r, ty) in
1452 let ty = r, TUtils.this_of ty in
1453 (* '$this' always refers to the late bound static type *)
1454 let env, new_ty = ExprDepTy.make env CIstatic ty in
1455 make_result env T.This (new_ty)
1456 | Assert (AE_assert e) ->
1457 let env, te, _ = expr env e in
1458 let env = condition env true e in
1459 make_result env (T.Assert (T.AE_assert te)) (Reason.Rwitness p, Tprim Tvoid)
1460 | True ->
1461 make_result env T.True (Reason.Rwitness p, Tprim Tbool)
1462 | False ->
1463 make_result env T.False (Reason.Rwitness p, Tprim Tbool)
1464 (* TODO TAST: consider checking that the integer is in range. Right now
1465 * it's possible for HHVM to fail on well-typed Hack code
1467 | Int s ->
1468 make_result env (T.Int s) (Reason.Rwitness p, Tprim Tint)
1469 | Float s ->
1470 make_result env (T.Float s) (Reason.Rwitness p, Tprim Tfloat)
1471 (* TODO TAST: consider introducing a "null" type, and defining ?t to
1472 * be null | t
1474 | Null ->
1475 let ty = Env.fresh_type() in
1476 make_result env T.Null (Reason.Rwitness p, Toption ty)
1477 | String s ->
1478 make_result env (T.String s) (Reason.Rwitness p, Tprim Tstring)
1479 | String2 idl ->
1480 let env, tel = string2 env idl in
1481 make_result env (T.String2 tel) (Reason.Rwitness p, Tprim Tstring)
1482 | Fun_id x ->
1483 Typing_hooks.dispatch_id_hook x env;
1484 let env, fty = fun_type_of_id env x [] in
1485 begin match fty with
1486 | _, Tfun fty -> check_deprecated (fst x) fty;
1487 | _ -> ()
1488 end;
1489 make_result env (T.Fun_id x) fty
1490 | Id ((cst_pos, cst_name) as id) ->
1491 Typing_hooks.dispatch_id_hook id env;
1492 Typing_hooks.dispatch_global_const_hook id;
1493 (match Env.get_gconst env cst_name with
1494 | None when Env.is_strict env ->
1495 Errors.unbound_global cst_pos;
1496 let ty = (Reason.Rwitness cst_pos, Typing_utils.terr env) in
1497 let te = T.make_implicitly_typed_expr cst_pos (T.Id id) in
1498 env, te, ty
1499 | None ->
1500 make_result env (T.Id id) (Reason.Rwitness cst_pos, Typing_utils.tany env)
1501 | Some (ty, _) ->
1502 if cst_name = SN.HH.rx_is_enabled
1503 then begin
1504 if Env.is_checking_lambda ()
1505 then Errors.rx_enabled_in_lambdas cst_pos
1506 else if Env.env_reactivity env = Nonreactive
1507 then Errors.rx_enabled_in_non_rx_context cst_pos
1508 end;
1509 let env, ty =
1510 Phase.localize_with_self env ty in
1511 make_result env (T.Id id) ty
1513 | Method_id (instance, meth) ->
1514 (* Method_id is used when creating a "method pointer" using the magic
1515 * inst_meth function.
1517 * Typing this is pretty simple, we just need to check that instance->meth
1518 * is public+not static and then return its type.
1520 Typing_hooks.dispatch_fun_id_hook (p, "\\"^SN.SpecialFunctions.inst_meth);
1521 let env, te, ty1 = expr env instance in
1522 let env, result, vis =
1523 obj_get_with_visibility ~is_method:true ~nullsafe:None ~valkind:`other ~pos_params:None env
1524 ty1 (CIexpr instance) meth (fun x -> x) in
1525 let has_lost_info = Env.FakeMembers.is_invalid env instance (snd meth) in
1526 if has_lost_info
1527 then
1528 let name = "the method "^snd meth in
1529 let env, result = Env.lost_info name env result in
1530 make_result env (T.Method_id (te, meth)) result
1531 else
1532 begin
1533 (match result with
1534 | _, Tfun fty -> check_deprecated p fty
1535 | _ -> ());
1536 (match vis with
1537 | Some (method_pos, Vprivate _) ->
1538 Errors.private_inst_meth method_pos p
1539 | Some (method_pos, Vprotected _) ->
1540 Errors.protected_inst_meth method_pos p
1541 | _ -> ()
1543 make_result env (T.Method_id (te, meth)) result
1545 | Method_caller ((pos, class_name) as pos_cname, meth_name) ->
1546 (* meth_caller('X', 'foo') desugars to:
1547 * $x ==> $x->foo()
1549 Typing_hooks.dispatch_fun_id_hook (p, "\\"^SN.SpecialFunctions.meth_caller);
1550 let class_ = Env.get_class env class_name in
1551 (match class_ with
1552 | None -> unbound_name env pos_cname
1553 | Some class_ ->
1554 (* Create a class type for the given object instantiated with unresolved
1555 * types for its type parameters.
1557 let env, tvarl =
1558 List.map_env env class_.tc_tparams TUtils.unresolved_tparam in
1559 let params = List.map class_.tc_tparams begin fun (_, (p, n), _) ->
1560 Reason.Rwitness p, Tgeneric n
1561 end in
1562 let obj_type = Reason.Rwitness p, Tapply (pos_cname, params) in
1563 let ety_env = {
1564 (Phase.env_with_self env) with
1565 substs = Subst.make class_.tc_tparams tvarl;
1566 } in
1567 let env, local_obj_ty = Phase.localize ~ety_env env obj_type in
1568 let env, fty =
1569 obj_get ~is_method:true ~nullsafe:None env local_obj_ty
1570 (CI ((pos, class_name), [])) meth_name (fun x -> x) in
1571 (match fty with
1572 | reason, Tfun fty ->
1573 check_deprecated p fty;
1574 (* We are creating a fake closure:
1575 * function(Class $x, arg_types_of(Class::meth_name))
1576 : return_type_of(Class::meth_name)
1578 let ety_env = {
1579 ety_env with substs = Subst.make class_.tc_tparams tvarl
1580 } in
1581 let env =
1582 Phase.check_tparams_constraints ~use_pos:p ~ety_env env class_.tc_tparams in
1583 let env, local_obj_ty = Phase.localize ~ety_env env obj_type in
1584 let local_obj_fp = TUtils.default_fun_param local_obj_ty in
1585 let fty = { fty with
1586 ft_params = local_obj_fp :: fty.ft_params } in
1587 let fun_arity = match fty.ft_arity with
1588 | Fstandard (min, max) -> Fstandard (min + 1, max + 1)
1589 | Fvariadic (min, x) -> Fvariadic (min + 1, x)
1590 | Fellipsis min -> Fellipsis (min + 1) in
1591 let caller = {
1592 ft_pos = pos;
1593 ft_deprecated = None;
1594 ft_abstract = false;
1595 (* propagate 'is_coroutine' from the method being called*)
1596 ft_is_coroutine = fty.ft_is_coroutine;
1597 ft_arity = fun_arity;
1598 ft_tparams = fty.ft_tparams;
1599 ft_where_constraints = fty.ft_where_constraints;
1600 ft_params = fty.ft_params;
1601 ft_ret = fty.ft_ret;
1602 ft_ret_by_ref = fty.ft_ret_by_ref;
1603 ft_reactive = fty.ft_reactive;
1604 ft_mutable = fty.ft_mutable;
1605 ft_returns_mutable = fty.ft_returns_mutable;
1606 ft_return_disposable = fty.ft_return_disposable;
1607 ft_decl_errors = None;
1608 } in
1609 make_result env (T.Method_caller(pos_cname, meth_name))
1610 (reason, Tfun caller)
1611 | _ ->
1612 (* This can happen if the method lives in PHP *)
1613 make_result env (T.Method_caller(pos_cname, meth_name))
1614 (Reason.Rwitness pos, Typing_utils.tany env)
1617 | Smethod_id (c, meth) ->
1618 (* Smethod_id is used when creating a "method pointer" using the magic
1619 * class_meth function.
1621 * Typing this is pretty simple, we just need to check that c::meth is
1622 * public+static and then return its type.
1624 Typing_hooks.dispatch_fun_id_hook (p, "\\"^SN.SpecialFunctions.class_meth);
1625 let class_ = Env.get_class env (snd c) in
1626 (match class_ with
1627 | None ->
1628 (* The class given as a static string was not found. *)
1629 unbound_name env c
1630 | Some class_ ->
1631 let smethod = Env.get_static_member true env class_ (snd meth) in
1632 (match smethod with
1633 | None -> (* The static method wasn't found. *)
1634 smember_not_found p ~is_const:false ~is_method:true class_ (snd meth);
1635 expr_error env p Reason.Rnone
1636 | Some { ce_type = lazy ty; ce_visibility; _ } ->
1637 let cid = CI (c, []) in
1638 let env, _te, cid_ty = static_class_id (fst c) env cid in
1639 let ety_env = {
1640 type_expansions = [];
1641 substs = SMap.empty;
1642 this_ty = cid_ty;
1643 from_class = Some cid;
1644 } in
1645 match ty with
1646 | (r, Tfun ft) ->
1647 begin
1648 let env, ft = Phase.localize_ft ~use_pos:p ~ety_env env ft in
1649 let ty = r, Tfun ft in
1650 check_deprecated p ft;
1651 match ce_visibility with
1652 | Vpublic ->
1653 make_result env (T.Smethod_id(c, meth)) ty
1654 | Vprivate _ ->
1655 Errors.private_class_meth (Reason.to_pos r) p;
1656 expr_error env p r
1657 | Vprotected _ ->
1658 Errors.protected_class_meth (Reason.to_pos r) p;
1659 expr_error env p r
1661 | (r, _) ->
1662 Errors.internal_error p "We have a method which isn't callable";
1663 expr_error env p r
1666 | Lplaceholder p ->
1667 let r = Reason.Rplaceholder p in
1668 let ty = r, Tprim Tvoid in
1669 make_result env (T.Lplaceholder p) ty
1670 | Dollardollar _ when valkind = `lvalue ->
1671 Errors.dollardollar_lvalue p;
1672 expr_error env p (Reason.Rwitness p)
1673 | Dollardollar ((_, x) as id) ->
1674 let ty = Env.get_local env x in
1675 make_result env (T.Dollardollar id) ty
1676 | Lvar ((_, x) as id) ->
1677 Typing_hooks.dispatch_lvar_hook id env;
1678 let local_id = Local_id.to_string x in
1679 if SN.Superglobals.is_superglobal local_id
1680 then Env.error_if_reactive_context env @@ begin fun () ->
1681 Errors.superglobal_in_reactive_context p local_id;
1682 end;
1683 if not accept_using_var
1684 then check_escaping_var env id;
1685 let ty = Env.get_local env x in
1686 make_result env (T.Lvar id) ty
1687 | Dollar e ->
1688 let env, te, _ty = expr env e in
1689 (** Can't easily track any typing information for variable variable. *)
1690 make_result env (T.Dollar te) (Reason.Rwitness p, Typing_utils.tany env)
1691 | List el ->
1692 let env, expected = expand_expected env expected in
1693 let env, tel, tyl =
1694 match expected with
1695 | Some (pos, ur, (_, Ttuple expected_tyl)) ->
1696 exprs_expected (pos, ur, expected_tyl) env el
1697 | _ ->
1698 exprs env el
1700 (* TODO TAST: figure out role of unbind here *)
1701 let env, tyl = List.map_env env tyl Typing_env.unbind in
1702 let ty = Reason.Rwitness p, Ttuple tyl in
1703 make_result env (T.List tel) ty
1704 | Pair (e1, e2) ->
1705 (* Use expected type to determine expected element types *)
1706 let env, expected1, expected2 =
1707 match expand_expected env expected with
1708 | env, Some (pos, ur, (_, Tclass ((_, k), [ty1; ty2]))) when k = SN.Collections.cPair ->
1709 env, Some (pos, ur, ty1), Some (pos, ur, ty2)
1710 | _ -> env, None, None in
1711 let env, te1, ty1 = expr ?expected:expected1 env e1 in
1712 let env, ty1 = Typing_env.unbind env ty1 in
1713 let env, te2, ty2 = expr ?expected:expected2 env e2 in
1714 let env, ty2 = Typing_env.unbind env ty2 in
1715 let ty =
1716 Reason.Rwitness p, Tclass ((p, SN.Collections.cPair), [ty1; ty2]) in
1717 make_result env (T.Pair (te1, te2)) ty
1718 | Expr_list el ->
1719 (* TODO: use expected type to determine tuple component types *)
1720 let env, tel, tyl = exprs env el in
1721 let ty = Reason.Rwitness p, Ttuple tyl in
1722 make_result env (T.Expr_list tel) ty
1723 | Array_get (e, None) ->
1724 let env, te1, ty1 = update_array_type p env e None valkind in
1725 let env, ty = array_append p env ty1 in
1726 make_result env (T.Array_get(te1, None)) ty
1727 | Array_get (e1, Some e2) ->
1728 let env, te1, ty1 =
1729 update_array_type ?lhs_of_null_coalesce p env e1 (Some e2) valkind in
1730 let env, ty1 = TUtils.fold_unresolved env ty1 in
1731 let env, te2, ty2 = expr env e2 in
1732 let is_lvalue = (valkind == `lvalue) in
1733 let env, ty =
1734 array_get ?lhs_of_null_coalesce is_lvalue p env ty1 e2 ty2 in
1735 make_result env (T.Array_get(te1, Some te2)) ty
1736 | Call (Cnormal, (pos_id, Id ((_, s) as id)), hl, el, [])
1737 when is_pseudo_function s ->
1738 let env, tel, tys = exprs ~accept_using_var:true env el in
1739 if s = SN.PseudoFunctions.hh_show
1740 then List.iter tys (Typing_log.hh_show p env)
1741 else
1742 if s = SN.PseudoFunctions.hh_show_env
1743 then Typing_log.hh_show_env p env
1744 else
1745 if s = SN.PseudoFunctions.hh_log_level
1746 then match el with
1747 | [(_, Int (_, level_str))] ->
1748 Typing_log.hh_log_level (int_of_string level_str)
1749 | _ -> ()
1750 else ();
1751 make_result env
1752 (T.Call(
1753 Cnormal,
1754 T.make_implicitly_typed_expr pos_id (T.Id id),
1756 tel,
1757 [])) (Env.fresh_type())
1758 | Call (call_type, e, hl, el, uel) ->
1759 let env, te, ty = check_call ~is_using_clause ~expected env p call_type e hl el uel ~in_suspend:false in
1760 Typing_mutability.enforce_mutable_call env te;
1761 env, te, ty
1762 (* For example, e1 += e2. This is typed and translated as if
1763 * written e1 = e1 + e2.
1764 * TODO TAST: is this right? e1 will get evaluated more than once
1766 | Binop (Ast.Eq (Some op), e1, e2) ->
1767 let e_fake = (p, Binop (Ast.Eq None, e1, (p, Binop (op, e1, e2)))) in
1768 let env, te_fake, ty = raw_expr in_cond env e_fake in
1769 begin match snd te_fake with
1770 | T.Binop (_, te1, (_, T.Binop (_, _, te2))) ->
1771 let te = T.Binop (Ast.Eq (Some op), te1, te2) in
1772 make_result env te ty
1773 | _ -> assert false
1775 | Binop (Ast.Eq None, e1, e2) ->
1776 let forbid_uref = match e1, e2 with
1777 | (_, Array_get _), (_, Unop (Ast.Uref, _))
1778 | _, (_, Unop (Ast.Uref, (_, Array_get _))) -> true
1779 | _ -> false in
1780 let env, te2, ty2 = raw_expr ~in_cond ~forbid_uref env e2 in
1781 let env, te1, ty = assign p env e1 ty2 in
1782 let env =
1783 if Env.env_local_reactive env then
1784 Typing_mutability.handle_assignment_mutability env te1 te2
1785 else env
1787 (* If we are assigning a local variable to another local variable then
1788 * the expression ID associated with e2 is transferred to e1
1790 (match e1, e2 with
1791 | (_, Lvar (_, x1)), (_, Lvar (_, x2)) ->
1792 let eid2 = Env.get_local_expr_id env x2 in
1793 let env =
1794 Option.value_map
1795 eid2 ~default:env
1796 ~f:(Env.set_local_expr_id env x1) in
1797 make_result env (T.Binop(Ast.Eq None, te1, te2)) ty
1798 | _ ->
1799 make_result env (T.Binop(Ast.Eq None, te1, te2)) ty
1801 | Binop ((Ast.AMpamp | Ast.BArbar as bop), e1, e2) ->
1802 let c = bop = Ast.AMpamp in
1803 let lenv = env.Env.lenv in
1804 let env, te1, ty1 = expr env e1 in
1805 let env = condition env c e1 in
1806 let env, te2, ty2 = raw_expr in_cond env e2 in
1807 let env = { env with Env.lenv = lenv } in
1808 Typing_hooks.dispatch_binop_hook p bop ty1 ty2;
1809 make_result env (T.Binop(bop, te1, te2))
1810 (Reason.Rlogic_ret p, Tprim Tbool)
1811 | Binop (bop, e1, e2) when Env.is_strict env
1812 && (snd e1 = Nast.Null || snd e2 = Nast.Null)
1813 && (bop = Ast.EQeqeq || bop = Ast.Diff2) ->
1814 let e, ne = if snd e2 = Nast.Null then e1, e2 else e2, e1 in
1815 let _, te, ty = raw_expr in_cond env e in
1816 if not in_cond
1817 then Typing_equality_check.assert_nullable p bop env ty;
1818 let tne = T.make_typed_expr (fst ne) ty T.Null in
1819 let te1, te2 = if snd e2 = Nast.Null then te, tne else tne, te in
1820 make_result env (T.Binop(bop, te1, te2))
1821 (Reason.Rcomp p, Tprim Tbool)
1822 | Binop (bop, e1, e2) ->
1823 let env, te1, ty1 = raw_expr in_cond env e1 in
1824 let env, te2, ty2 = raw_expr in_cond env e2 in
1825 let env, te3, ty =
1826 binop in_cond p env bop (fst e1) te1 ty1 (fst e2) te2 ty2 in
1827 Typing_hooks.dispatch_binop_hook p bop ty1 ty2;
1828 env, te3, ty
1829 | Pipe (e0, e1, e2) ->
1830 let env, te1, ty = expr env e1 in
1831 (** id is the ID of the $$ that is implicitly declared by the pipe.
1832 * Set the local type for the $$ in the RHS. *)
1833 let env = set_local env e0 ty in
1834 let env, te2, ty2 = expr env e2 in
1836 * Return ty2 since the type of the pipe expression is the type of the
1837 * RHS.
1839 * Note: env does have the type of this Pipe's $$, but it doesn't
1840 * override the outer one since they have different ID's.
1842 * For example:
1843 * a() |> ( inner1($$) |> inner2($$) ) + $$
1845 * The rightmost $$ refers to the result of a()
1847 make_result env (T.Pipe(e0, te1, te2)) ty2
1848 | Unop (uop, e) ->
1849 let env, te, ty = raw_expr in_cond env e in
1850 unop ~is_func_arg ~forbid_uref p env uop te ty
1851 | Eif (c, e1, e2) -> eif env ~expected ~coalesce:false ~in_cond p c e1 e2
1852 | NullCoalesce (e1, e2) -> eif env ~expected ~coalesce:true ~in_cond p e1 None e2
1853 | Typename sid ->
1854 begin match Env.get_typedef env (snd sid) with
1855 | Some {td_tparams = tparaml; _} ->
1856 (* Typedef type parameters cannot have constraints *)
1857 let params = List.map ~f:begin fun (_, (p, x), _) ->
1858 Reason.Rwitness p, Tgeneric x
1859 end tparaml in
1860 let tdef = Reason.Rwitness (fst sid), Tapply (sid, params) in
1861 let typename =
1862 Reason.Rwitness p, Tapply((p, SN.Classes.cTypename), [tdef]) in
1863 let env, tparams = List.map_env env tparaml begin fun env _ ->
1864 Env.fresh_unresolved_type env
1865 end in
1866 let ety_env = { (Phase.env_with_self env) with
1867 substs = Subst.make tparaml tparams } in
1868 let env = Phase.check_tparams_constraints ~use_pos:p ~ety_env env tparaml in
1869 let env, ty = Phase.localize ~ety_env env typename in
1870 make_result env (T.Typename sid) ty
1871 | None ->
1872 (* Should never hit this case since we only construct this AST node
1873 * if in the expression Foo::class, Foo is a type def.
1875 expr_error env p (Reason.Rwitness p)
1877 | Class_const (cid, mid) -> class_const env p (cid, mid)
1878 | Class_get (((), x), (py, y))
1879 when Env.FakeMembers.get_static env x y <> None ->
1880 Env.error_if_reactive_context env @@ begin fun () ->
1881 Errors.static_property_in_reactive_context p
1882 end;
1883 let env, local = Env.FakeMembers.make_static p env x y in
1884 let local = p, Lvar (p, local) in
1885 let env, _, ty = expr env local in
1886 let env, te, _ = static_class_id p env x in
1887 make_result env (T.Class_get (te, (py, y))) ty
1888 | Class_get (((), cid), mid) ->
1889 Env.error_if_reactive_context env @@ begin fun () ->
1890 Errors.static_property_in_reactive_context p
1891 end;
1892 TUtils.process_static_find_ref cid mid;
1893 let env, te, cty = static_class_id p env cid in
1894 let env, ty, _ =
1895 class_get ~is_method:false ~is_const:false env cty mid cid in
1896 if Env.FakeMembers.is_static_invalid env cid (snd mid)
1897 then
1898 let fake_name = Env.FakeMembers.make_static_id cid (snd mid) in
1899 let env, ty = Env.lost_info fake_name env ty in
1900 make_result env (T.Class_get (te, mid)) ty
1901 else
1902 make_result env (T.Class_get (te, mid)) ty
1903 (* Fake member property access. For example:
1904 * if ($x->f !== null) { ...$x->f... }
1906 | Obj_get (e, (pid, Id (py, y)), nf)
1907 when Env.FakeMembers.get env e y <> None ->
1908 let env, local = Env.FakeMembers.make p env e y in
1909 let local = p, Lvar (p, local) in
1910 let env, _, ty = expr env local in
1911 let env, t_lhs, _ = expr ~accept_using_var:true env e in
1912 let t_rhs = T.make_typed_expr pid ty (T.Id (py, y)) in
1913 make_result env (T.Obj_get (t_lhs, t_rhs, nf)) ty
1914 (* Statically-known instance property access e.g. $x->f *)
1915 | Obj_get (e1, (pm, Id m), nullflavor) ->
1916 let nullsafe =
1917 (match nullflavor with
1918 | OG_nullthrows -> None
1919 | OG_nullsafe -> Some p
1920 ) in
1921 let env, te1, ty1 = expr ~accept_using_var:true env e1 in
1922 let env, result =
1923 obj_get ~is_method:false ~nullsafe ~valkind env ty1 (CIexpr e1) m (fun x -> x) in
1924 let has_lost_info = Env.FakeMembers.is_invalid env e1 (snd m) in
1925 let env, result =
1926 if has_lost_info
1927 then
1928 let name = "the member " ^ snd m in
1929 Env.lost_info name env result
1930 else
1931 env, result
1933 make_result env (T.Obj_get(te1,
1934 T.make_implicitly_typed_expr pm (T.Id m), nullflavor)) result
1935 (* Dynamic instance property access e.g. $x->$f *)
1936 | Obj_get (e1, e2, nullflavor) ->
1937 let env, te1, ty1 = expr ~accept_using_var:true env e1 in
1938 let env, te2, _ = expr env e2 in
1939 let ty = if TUtils.is_dynamic env ty1 then
1940 (Reason.Rwitness p, Tdynamic) else
1941 begin
1942 if Env.is_strict env then
1943 begin
1944 Errors.dynamic_method_call (fst e2);
1945 (Reason.Rwitness p, Typing_utils.terr env)
1947 else
1948 (Reason.Rwitness p, Typing_utils.tany env)
1949 end in
1950 make_result env (T.Obj_get(te1, te2, nullflavor)) ty
1951 | Yield_break ->
1952 make_result env T.Yield_break (Reason.Rwitness p, Typing_utils.tany env)
1953 | Yield af ->
1954 let env, (taf, opt_key, value) = array_field env af in
1955 let send = Env.fresh_type () in
1956 let env, key = match af, opt_key with
1957 | Nast.AFvalue (p, _), None ->
1958 let result_ty =
1959 match Env.get_fn_kind env with
1960 | Ast.FCoroutine
1961 | Ast.FSync
1962 | Ast.FAsync ->
1963 Errors.internal_error p "yield found in non-generator";
1964 Reason.Rwitness p, Typing_utils.tany env
1965 | Ast.FGenerator ->
1966 (Reason.Rwitness p, Tprim Tint)
1967 | Ast.FAsyncGenerator ->
1968 (Reason.Ryield_asyncnull p,
1969 Toption (Env.fresh_type ()))
1971 env, result_ty
1972 | _, Some x ->
1973 env, x
1974 | _, _ -> assert false in
1975 let rty = match Env.get_fn_kind env with
1976 | Ast.FCoroutine ->
1977 (* yield in coroutine is already reported as error in NastCheck *)
1978 let _, _, ty = expr_error env p (Reason.Rwitness p) in
1980 | Ast.FGenerator ->
1981 Reason.Ryield_gen p,
1982 Tclass ((p, SN.Classes.cGenerator), [key; value; send])
1983 | Ast.FAsyncGenerator ->
1984 Reason.Ryield_asyncgen p,
1985 Tclass ((p, SN.Classes.cAsyncGenerator), [key; value; send])
1986 | Ast.FSync | Ast.FAsync ->
1987 failwith "Parsing should never allow this" in
1988 let Typing_env_return_info.{ return_type = expected_return; _ } = Env.get_return env in
1989 let env =
1990 Type.sub_type p (Reason.URyield) env rty expected_return in
1991 let env = Env.forget_members env p in
1992 make_result env (T.Yield taf) (Reason.Ryield_send p, Toption send)
1993 | Await e ->
1994 (* Await is permitted in a using clause e.g. using (await make_handle()) *)
1995 let env, te, rty = expr ~is_using_clause env e in
1996 let env, ty = Async.overload_extract_from_awaitable env p rty in
1997 make_result env (T.Await te) ty
1998 | Suspend (e) ->
1999 let env, te, ty =
2000 match e with
2001 | _, Call (call_type, e, hl, el, uel) ->
2002 check_call ~is_using_clause ~expected env p call_type e hl el uel ~in_suspend:true
2003 | (epos, _) ->
2004 let env, te, (r, ty) = expr env e in
2005 (* not a call - report an error *)
2006 Errors.non_call_argument_in_suspend
2007 epos
2008 (Reason.to_string ("This is " ^ Typing_print.error ty) r);
2009 env, te, (r, ty) in
2010 make_result env (T.Suspend te) ty
2012 | Special_func func -> special_func env p func
2013 | New (((), c), el, uel) ->
2014 Typing_hooks.dispatch_new_id_hook c env p;
2015 TUtils.process_static_find_ref c (p, SN.Members.__construct);
2016 let env, tc, tel, tuel, ty =
2017 new_object ~expected ~is_using_clause ~check_parent:false ~check_not_abstract:true
2018 p env c el uel in
2019 let env = Env.forget_members env p in
2020 make_result env (T.New(tc, tel, tuel)) ty
2021 | Cast ((_, Harray (None, None)), _)
2022 when Env.is_strict env
2023 || TCO.migration_flag_enabled (Env.get_tcopt env) "array_cast" ->
2024 Errors.array_cast p;
2025 expr_error env p (Reason.Rwitness p)
2026 | Cast (hint, e) ->
2027 let env, te, ty2 = expr env e in
2028 Async.enforce_not_awaitable env (fst e) ty2;
2029 if (TypecheckerOptions.experimental_feature_enabled
2030 (Env.get_options env)
2031 TypecheckerOptions.experimental_forbid_nullable_cast)
2032 && TUtils.is_option_non_mixed env ty2
2033 then begin
2034 let (r, ty2) = ty2 in
2035 Errors.nullable_cast p (Typing_print.error ty2) (Reason.to_pos r)
2036 end;
2037 let env, ty = Phase.hint_locl env hint in
2038 make_result env (T.Cast (hint, te)) ty
2039 | InstanceOf (e, ((), cid)) ->
2040 let env, te, _ = expr env e in
2041 TUtils.process_class_id cid;
2042 let env, te2, _class = instantiable_cid p env cid in
2043 make_result env (T.InstanceOf (te, te2)) (Reason.Rwitness p, Tprim Tbool)
2044 | Is (e, hint) ->
2045 if not (TypecheckerOptions.experimental_feature_enabled
2046 (Env.get_options env)
2047 TypecheckerOptions.experimental_is_expression)
2048 then begin
2049 Errors.experimental_feature p "is expression";
2050 expr_error env p (Reason.Rwitness p)
2051 end else begin
2052 let env, te, _ = expr env e in
2053 let env, hint_ty = Phase.hint_locl env hint in
2054 match (IsAsExprHint.validate hint_ty) with
2055 | IsAsExprHint.Valid ->
2056 make_result env (T.Is (te, hint)) (Reason.Rwitness p, Tprim Tbool)
2057 | IsAsExprHint.Partial (r, ty_) ->
2058 Errors.partially_valid_is_as_expression_hint (Reason.to_pos r) "is"
2059 (IsAsExprHint.print ty_);
2060 make_result env (T.Is (te, hint)) (Reason.Rwitness p, Tprim Tbool)
2061 | IsAsExprHint.Invalid (r, ty_) ->
2062 Errors.invalid_is_as_expression_hint (Reason.to_pos r) "is"
2063 (IsAsExprHint.print ty_);
2064 expr_error env p (Reason.Rwitness p)
2066 | As (e, hint, is_nullable) ->
2067 if not (TypecheckerOptions.experimental_feature_enabled
2068 (Env.get_options env)
2069 TypecheckerOptions.experimental_as_expression)
2070 then begin
2071 Errors.experimental_feature p "as expression";
2072 expr_error env p (Reason.Rnone)
2073 end else begin
2074 let env, _, _ = expr env e in
2075 let env, hint_ty = Phase.hint_locl env hint in
2076 let hint_ty =
2077 if not is_nullable then hint_ty else
2078 match hint_ty with
2079 (* Dont create ??hint *)
2080 | _ , Toption _ -> hint_ty
2081 | _ -> Reason.Rwitness p, Toption (hint_ty) in
2082 let env, te, ty = assign p env e hint_ty in
2083 match (IsAsExprHint.validate hint_ty) with
2084 | IsAsExprHint.Valid ->
2085 make_result env (T.As (te, hint, is_nullable)) ty
2086 | IsAsExprHint.Partial (r, ty_) ->
2087 Errors.partially_valid_is_as_expression_hint (Reason.to_pos r) "as"
2088 (IsAsExprHint.print ty_);
2089 make_result env (T.As (te, hint, is_nullable)) ty
2090 | IsAsExprHint.Invalid (r, ty_) ->
2091 Errors.invalid_is_as_expression_hint (Reason.to_pos r) "as"
2092 (IsAsExprHint.print ty_);
2093 expr_error env p (Reason.Rwitness p)
2095 | Efun (f, idl) ->
2096 (* This is the function type as declared on the lambda itself.
2097 * If type hints are absent then use Tany instead. *)
2098 let declared_ft = Decl.fun_decl_in_env env.Env.decl_env f in
2099 (* When creating a closure, the 'this' type will mean the late bound type
2100 * of the current enclosing class
2102 let ety_env =
2103 { (Phase.env_with_self env) with from_class = Some CIstatic } in
2104 let env, declared_ft = Phase.localize_ft ~use_pos:p ~ety_env env declared_ft in
2105 List.iter idl (check_escaping_var env);
2106 (* Ensure lambda arity is not Fellipsis in strict mode *)
2107 begin match declared_ft.ft_arity with
2108 | Fellipsis _ when Env.is_strict env ->
2109 Errors.ellipsis_strict_mode ~require:`Param_name p
2110 | _ -> ()
2111 end;
2112 (* Is the return type declared? *)
2113 let is_explicit_ret = Option.is_some f.f_ret in
2114 let check_body_under_known_params ?ret_ty ft =
2115 let (is_coroutine, _counter, _, anon) = anon_make env p f ft idl in
2116 let ft = { ft with ft_is_coroutine = is_coroutine } in
2117 let ft = { ft with ft_reactive = Nonreactive } in
2118 let is_reactive, (env, tefun, ty) =
2119 Env.check_lambda_reactive (fun () -> anon ?ret_ty env ft.ft_params ft.ft_arity) in
2120 let ft = { ft with ft_reactive = is_reactive } in
2121 let inferred_ty =
2122 if is_explicit_ret
2123 then (Reason.Rwitness p, Tfun { ft with ft_ret = declared_ft.ft_ret })
2124 else (Reason.Rwitness p, Tfun { ft with ft_ret = ty }) in
2125 Typing_log.log_types 1 p env
2126 [Typing_log.Log_sub
2127 ("Typing.check_body_under_known_params",
2128 [Typing_log.Log_type ("ft", (Reason.Rwitness p, Tfun ft));
2129 Typing_log.Log_type ("inferred_ty", inferred_ty)])];
2130 env, tefun, inferred_ty in
2131 let env, eexpected = expand_expected env expected in
2132 begin match eexpected with
2133 | Some (_pos, _ur, (_, Tfun expected_ft)) ->
2134 (* First check that arities match up *)
2135 check_lambda_arity p expected_ft.ft_pos declared_ft.ft_arity expected_ft.ft_arity;
2136 (* Use declared types for parameters in preference to those determined
2137 * by the context: they might be more general. *)
2138 let rec replace_non_declared_types params declared_ft_params expected_ft_params =
2139 match params, declared_ft_params, expected_ft_params with
2140 | param::params, declared_ft_param::declared_ft_params,
2141 expected_ft_param::expected_ft_params ->
2142 let rest = replace_non_declared_types params declared_ft_params expected_ft_params in
2143 let resolved_ft_param = if Option.is_some param.param_hint
2144 then declared_ft_param
2145 else { declared_ft_param with fp_type = expected_ft_param.fp_type } in
2146 resolved_ft_param :: rest
2147 | _, _, _ ->
2148 (* This means the expected_ft params list can have more parameters
2149 * than declared parameters in the lambda. For variadics, this is OK,
2150 * for non-variadics, this will be caught elsewhere in arity checks.
2152 expected_ft_params
2154 let replace_non_declared_arity variadic declared_arity expected_arity =
2155 match variadic with
2156 | FVvariadicArg {param_hint = Some(_); _} -> declared_arity
2157 | FVvariadicArg _ ->
2158 begin
2159 match declared_arity, expected_arity with
2160 | Fvariadic (min_arity, declared), Fvariadic (_, expected) ->
2161 Fvariadic (min_arity, { declared with fp_type = expected.fp_type})
2162 | _, _ -> declared_arity
2164 | _ -> declared_arity
2166 let expected_ft = { expected_ft with ft_arity =
2167 replace_non_declared_arity
2168 f.f_variadic declared_ft.ft_arity expected_ft.ft_arity } in
2169 let expected_ft = { expected_ft with ft_params =
2170 replace_non_declared_types f.f_params declared_ft.ft_params expected_ft.ft_params } in
2171 (* Don't bother passing in `void` if there is no explicit return *)
2172 let ret_ty =
2173 match expected_ft.ft_ret with
2174 | _, Tprim Tvoid when not is_explicit_ret -> None
2175 | _ -> Some expected_ft.ft_ret in
2176 Measure.sample "Lambda [contextual params]" 1.0;
2177 check_body_under_known_params ?ret_ty expected_ft
2178 | _ ->
2179 let explicit_variadic_param_or_non_variadic =
2180 begin match f.f_variadic with
2181 | FVvariadicArg {param_hint; _} -> Option.is_some param_hint
2182 | FVellipsis -> false
2183 | _ -> true
2186 (* If all parameters are annotated with explicit types, then type-check
2187 * the body under those assumptions and pick up the result type *)
2188 let all_explicit_params =
2189 List.for_all f.f_params (fun param -> Option.is_some param.param_hint) in
2190 if all_explicit_params && explicit_variadic_param_or_non_variadic
2191 then begin
2192 if List.is_empty f.f_params
2193 then Measure.sample "Lambda [no params]" 1.0
2194 else Measure.sample "Lambda [explicit params]" 1.0;
2195 check_body_under_known_params declared_ft
2197 else begin
2198 match expected with
2199 | Some (_, _, (_, Tany)) ->
2200 (* If the expected type is Tany env then we're passing a lambda to an untyped
2201 * function and we just assume every parameter has type Tany env *)
2202 Measure.sample "Lambda [untyped context]" 1.0;
2203 check_body_under_known_params declared_ft
2204 | Some _ ->
2205 (* If the expected type is something concrete but not a function
2206 * then we should reject in strict mode. Check body anyway *)
2207 if Env.is_strict env && TypecheckerOptions.experimental_feature_enabled (Env.get_options env)
2208 TypecheckerOptions.experimental_disallow_untyped_lambda_as_non_function_type
2209 then Errors.untyped_lambda_strict_mode p;
2210 Measure.sample "Lambda [non-function typed context]" 1.0;
2211 check_body_under_known_params declared_ft
2212 | _ ->
2213 Measure.sample "Lambda [unknown params]" 1.0;
2214 Typing_log.log_types 1 p env
2215 [Typing_log.Log_sub
2216 ("Typing.expr Efun unknown params",
2217 [Typing_log.Log_type ("declared_ft", (Reason.Rwitness p, Tfun declared_ft))])];
2218 (* check for recursive function calls *)
2219 let is_coroutine, counter, pos, anon = anon_make env p f declared_ft idl in
2220 let env, tefun, _, anon_id = Errors.try_with_error
2221 (fun () ->
2222 let reactivity, (_, tefun, ty) =
2223 Env.check_lambda_reactive
2224 (fun () -> anon env declared_ft.ft_params declared_ft.ft_arity) in
2225 let anon_fun = reactivity, is_coroutine, counter, pos, anon in
2226 let env, anon_id = Env.add_anonymous env anon_fun in
2227 env, tefun, ty, anon_id)
2228 (fun () ->
2229 (* If the anonymous function declaration has errors itself, silence
2230 them in any subsequent usages. *)
2231 let anon_ign ?el:_ ?ret_ty:_ env fun_params =
2232 Errors.ignore_ (fun () -> (anon env fun_params)) in
2233 let reactivity, (_, tefun, ty)
2234 = Env.check_lambda_reactive (fun () -> anon_ign env declared_ft.ft_params declared_ft.ft_arity) in
2235 let anon_fun = reactivity, is_coroutine, counter, pos, anon in
2236 let env, anon_id = Env.add_anonymous env anon_fun in
2237 env, tefun, ty, anon_id) in
2238 env, tefun, (Reason.Rwitness p, Tanon (declared_ft.ft_arity, anon_id))
2241 | Xml (sid, attrl, el) ->
2242 let cid = CI (sid, []) in
2243 let env, _te, classes = class_id_for_new p env cid in
2244 let class_info = match classes with
2245 | [] -> None
2246 (* OK to ignore rest of list; class_info only used for errors, and
2247 * cid = CI sid cannot produce a union of classes anyhow *)
2248 | (_, class_info, _)::_ -> Some class_info
2250 let env, _te, obj = expr env (fst sid, New (((), cid), [], [])) in
2251 let env, typed_attrs, attr_types = xhp_attribute_exprs env class_info attrl in
2252 let env, tel = List.fold_left el ~init:(env, []) ~f:fold_xhp_body_elements in
2253 let txml = T.Xml (sid, typed_attrs, List.rev tel) in
2254 (match class_info with
2255 | None -> make_result env txml (Reason.Runknown_class p, Tobject)
2256 | Some class_info ->
2257 let env = List.fold_left attr_types ~f:begin fun env attr ->
2258 let namepstr, valpty = attr in
2259 let valp, valty = valpty in
2260 let env, declty =
2261 obj_get ~is_method:false ~nullsafe:None env obj cid
2262 namepstr (fun x -> x) in
2263 let ureason = Reason.URxhp (class_info.tc_name, snd namepstr) in
2264 Type.sub_type valp ureason env valty declty
2265 end ~init:env in
2266 make_result env txml obj
2268 | Callconv (kind, e) ->
2269 let env, te, ty = expr env e in
2270 let rec check_types env = function
2271 | _, T.Lvar _ -> ()
2272 | _, T.Array_get (((_, Some ty1), _) as te1, Some _) ->
2273 let rec iter = function
2274 | _, Tany -> true
2275 | _, Tprim Tstring -> true
2276 | _, (Tarraykind _ | Ttuple _ | Tshape _) -> true
2277 | _, Tclass ((_, cn), _)
2278 when cn = SN.Collections.cDict
2279 || cn = SN.Collections.cKeyset
2280 || cn = SN.Collections.cVec -> true
2281 | _, Tunresolved tyl -> List.for_all ~f:iter tyl
2282 | _ -> false in
2283 let env, ety1 = Env.expand_type env ty1 in
2284 if iter ety1 then check_types env te1 else begin
2285 let ty_str = Typing_print.error (snd ety1) in
2286 let msgl = Reason.to_string ("This is " ^ ty_str) (fst ety1) in
2287 Errors.inout_argument_bad_type (fst e) msgl
2289 | _, T.Array_get (te2, Some _) ->
2290 check_types env te2
2291 (* Other invalid expressions are caught in NastCheck. *)
2292 | _ -> ()
2294 check_types env te;
2295 make_result env (T.Callconv (kind, te)) ty
2296 (* TODO TAST: change AST so that order of shape expressions is preserved.
2297 * At present, evaluation order is unspecified in TAST *)
2298 | Shape fdm ->
2299 let env, fdm_with_expected =
2300 match expand_expected env expected with
2301 | env, Some (pos, ur, (_, Tshape (_, expected_fdm))) ->
2302 let fdme =
2303 ShapeMap.mapi
2304 (fun k v ->
2305 match ShapeMap.get k expected_fdm with
2306 | None -> (v, None)
2307 | Some sft -> (v, Some (pos, ur, sft.sft_ty))) fdm in
2308 env, fdme
2309 | _ ->
2310 env, ShapeMap.map (fun v -> (v, None)) fdm in
2312 (* allow_inter adds a type-variable *)
2313 let env, tfdm =
2314 ShapeMap.map_env
2315 (fun env (e, expected) ->
2316 let env, te, ty = expr ?expected env e in env, (te,ty))
2317 env fdm_with_expected in
2318 let env, fdm =
2319 let convert_expr_and_type_to_shape_field_type env (_, ty) =
2320 let env, sft_ty = TUtils.unresolved env ty in
2321 (* An expression evaluation always corresponds to a shape_field_type
2322 with sft_optional = false. *)
2323 env, { sft_optional = false; sft_ty } in
2324 ShapeMap.map_env convert_expr_and_type_to_shape_field_type env tfdm in
2325 let env = check_shape_keys_validity env p (ShapeMap.keys fdm) in
2326 (* Fields are fully known, because this shape is constructed
2327 * using shape keyword and we know exactly what fields are set. *)
2328 make_result env (T.Shape (ShapeMap.map (fun (te,_) -> te) tfdm))
2329 (Reason.Rwitness p, Tshape (FieldsFullyKnown, fdm))
2331 and class_const ?(incl_tc=false) env p (((), cid), mid) =
2332 TUtils.process_static_find_ref cid mid;
2333 let env, ce, cty = static_class_id p env cid in
2334 let env, const_ty, cc_abstract_info =
2335 class_get ~is_method:false ~is_const:true ~incl_tc env cty mid cid in
2336 match cc_abstract_info with
2337 | Some (cc_pos, cc_name) ->
2338 let () = match cid with
2339 | CIstatic | CIexpr _ -> ();
2340 | _ -> Errors.abstract_const_usage p cc_pos cc_name; ()
2341 in env, T.make_typed_expr p const_ty (T.Class_const (ce, mid)), const_ty
2342 | None ->
2343 env, T.make_typed_expr p const_ty (T.Class_const (ce, mid)), const_ty
2345 and anon_sub_type pos ur env ty_sub ty_super =
2346 Errors.try_add_err pos (Reason.string_of_ureason ur)
2347 (fun () -> SubType.sub_type env ty_sub ty_super)
2348 (fun () -> env)
2350 (*****************************************************************************)
2351 (* XHP attribute/body helpers. *)
2352 (*****************************************************************************)
2354 * Process a spread operator by computing the intersection of XHP attributes
2355 * between the spread expression and the XHP constructor onto which we're
2356 * spreading.
2358 and xhp_spread_attribute env c_onto valexpr =
2359 let (p, _) = valexpr in
2360 let env, te, valty = expr env valexpr in
2361 (* Build the typed attribute node *)
2362 let typed_attr = T.Xhp_spread te in
2363 let env, attr_ptys = match c_onto with
2364 | None -> env, []
2365 | Some class_info -> Typing_xhp.get_spread_attributes env p class_info valty
2366 in env, typed_attr, attr_ptys
2369 * Simple XHP attributes (attr={expr} form) are simply interpreted as a member
2370 * variable prefixed with a colon, the types of which will be validated later
2372 and xhp_simple_attribute env id valexpr =
2373 let (p, _) = valexpr in
2374 let env, te, valty = expr env valexpr in
2375 (* This converts the attribute name to a member name. *)
2376 let name = ":"^(snd id) in
2377 let attr_pty = ((fst id, name), (p, valty)) in
2378 let typed_attr = T.Xhp_simple (id, te) in
2379 env, typed_attr, [attr_pty]
2383 * Typecheck the attribute expressions - this just checks that the expressions are
2384 * valid, not that they match the declared type for the attribute and,
2385 * in case of spreads, makes sure they are XHP.
2387 and xhp_attribute_exprs env cid attrl =
2388 let handle_attr (env, typed_attrl, attr_ptyl) attr =
2389 let env, typed_attr, attr_ptys = match attr with
2390 | Xhp_simple (id, valexpr) -> xhp_simple_attribute env id valexpr
2391 | Xhp_spread valexpr -> xhp_spread_attribute env cid valexpr
2393 env, typed_attr::typed_attrl, attr_ptys @ attr_ptyl
2395 let env, typed_attrl, attr_ptyl = List.fold_left ~f:handle_attr ~init:(env, [], []) attrl in
2396 env, List.rev typed_attrl, List.rev attr_ptyl
2398 and check_xhp_children env pos ty =
2399 let tys = match ty with
2400 | _, Tunresolved ts -> ts
2401 | _ -> [ty] in
2402 if List.for_all ~f:(Typing_xhp.xhp_child env pos) tys then env
2403 else begin
2404 let ty_str = Typing_print.error (snd ty) in
2405 let msgl = Reason.to_string ("This is "^ty_str) (fst ty) in
2406 Errors.illegal_xhp_child pos msgl; env
2409 and fold_xhp_body_elements (env, tel) body =
2410 let expr_pos = fst body in
2411 let env, te, ty = expr env body in
2412 let env, ty = Env.expand_type env ty in
2413 let env, ty = TUtils.fold_unresolved env ty in
2414 let env = check_xhp_children env expr_pos ty in
2415 env, te::tel
2417 (*****************************************************************************)
2418 (* Anonymous functions. *)
2419 (*****************************************************************************)
2420 and anon_bind_param params (env, t_params) ty : Env.env * Tast.fun_param list =
2421 match !params with
2422 | [] ->
2423 (* This code cannot be executed normally, because the arity is wrong
2424 * and it will error later. Bind as many parameters as we can and carry
2425 * on. *)
2426 env, t_params
2427 | param :: paraml ->
2428 params := paraml;
2429 match param.param_hint with
2430 | Some h ->
2432 let h = Decl_hint.hint env.Env.decl_env h in
2433 (* When creating a closure, the 'this' type will mean the
2434 * late bound type of the current enclosing class
2436 let ety_env =
2437 { (Phase.env_with_self env) with from_class = Some CIstatic } in
2438 let env, h = Phase.localize ~ety_env env h in
2439 let pos = Reason.to_pos (fst ty) in
2440 (* Don't use Type.sub_type as it resets env.Env.pos unnecessarily *)
2441 let env = anon_sub_type pos Reason.URparam env ty h in
2442 (* Closures are allowed to have explicit type-hints. When
2443 * that is the case we should check that the argument passed
2444 * is compatible with the type-hint.
2445 * The body of the function should be type-checked with the
2446 * hint and not the type of the argument passed.
2447 * Otherwise it leads to strange results where
2448 * foo(?string $x = null) is called with a string and fails to
2449 * type-check. If $x is a string instead of ?string, null is not
2450 * subtype of string ...
2452 let env, t_param = bind_param env (h, param) in
2453 env, t_params @ [t_param]
2454 | None ->
2455 let env, t_param = bind_param env (ty, param) in
2456 env, t_params @ [t_param]
2458 and anon_bind_variadic env vparam variadic_ty =
2459 let env, ty, pos =
2460 match vparam.param_hint with
2461 | None ->
2462 (* if the hint is missing, use the type we expect *)
2463 env, variadic_ty, Reason.to_pos (fst variadic_ty)
2464 | Some hint ->
2465 let h = Decl_hint.hint env.Env.decl_env hint in
2466 let ety_env =
2467 { (Phase.env_with_self env) with from_class = Some CIstatic; } in
2468 let env, h = Phase.localize ~ety_env env h in
2469 let pos = Reason.to_pos (fst variadic_ty) in
2470 let env = anon_sub_type pos Reason.URparam env variadic_ty h in
2471 env, h, vparam.param_pos
2473 let r = Reason.Rvar_param pos in
2474 let arr_values = r, (snd ty) in
2475 let akind =
2476 if TypecheckerOptions.experimental_feature_enabled
2477 (Env.get_options env)
2478 TypecheckerOptions.experimental_darray_and_varray
2479 then AKvarray arr_values
2480 else AKvec arr_values in
2481 let ty = r, Tarraykind akind in
2482 let env, t_variadic = bind_param env (ty, vparam) in
2483 env, t_variadic
2486 and anon_bind_opt_param env param : Env.env =
2487 match param.param_expr with
2488 | None ->
2489 let ty = Reason.Rwitness param.param_pos, Typing_utils.tany env in
2490 let env, _ = bind_param env (ty, param) in
2492 | Some default ->
2493 let env, _te, ty = expr env default in
2494 Typing_sequencing.sequence_check_expr default;
2495 let env, _ = bind_param env (ty, param) in
2498 and anon_check_param env param =
2499 match param.param_hint with
2500 | None -> env
2501 | Some hty ->
2502 let env, hty = Phase.hint_locl env hty in
2503 let paramty = Env.get_local env (Local_id.get param.param_name) in
2504 let hint_pos = Reason.to_pos (fst hty) in
2505 let env = Type.sub_type hint_pos Reason.URhint env paramty hty in
2508 (* Make a type-checking function for an anonymous function. *)
2509 and anon_make tenv p f ft idl =
2510 let anon_lenv = tenv.Env.lenv in
2511 let is_typing_self = ref false in
2512 let nb = Nast.assert_named_body f.f_body in
2513 let is_coroutine = f.f_fun_kind = Ast.FCoroutine in
2514 is_coroutine,
2515 ref 0,
2517 (* Here ret_ty should include Awaitable wrapper *)
2518 fun ?el ?ret_ty env supplied_params supplied_arity ->
2519 if !is_typing_self
2520 then begin
2521 Errors.anonymous_recursive p;
2522 expr_error env p (Reason.Rwitness p)
2524 else begin
2525 is_typing_self := true;
2526 Env.anon anon_lenv env begin fun env ->
2527 let env = Env.clear_params env in
2528 let make_variadic_arg env varg tyl =
2529 let remaining_types =
2530 (* It's possible the variadic arg will capture the variadic
2531 * parameter of the supplied arity (if arity is Fvariadic)
2532 * and additional supplied params.
2534 * For example in cases such as:
2535 * lambda1 = (int $a, string...$c) ==> {};
2536 * lambda1(1, "hello", ...$y); (where $y is a variadic string)
2537 * lambda1(1, "hello", "world");
2538 * then ...$c will contain "hello" and everything in $y in the first
2539 * example, and "hello" and "world" in the second example.
2541 * To account for a mismatch in arity, we take the remaining supplied
2542 * parameters and return a list of all their types. We'll use this
2543 * to create a union type when creating the typed variadic arg.
2545 let remaining_params = List.drop supplied_params (List.length f.f_params) in
2546 List.map ~f:(fun param -> param.fp_type) remaining_params
2548 let r = Reason.Rvar_param (varg.param_pos) in
2549 let union = Tunresolved (tyl @ remaining_types) in
2550 let env, t_param = anon_bind_variadic env varg (r, union) in
2551 env, T.FVvariadicArg t_param
2553 let env, t_variadic =
2554 begin match f.f_variadic, supplied_arity with
2555 | FVvariadicArg arg, Fvariadic (_, variadic) ->
2556 make_variadic_arg env arg [variadic.fp_type]
2557 | FVvariadicArg arg, Fstandard _ ->
2558 make_variadic_arg env arg []
2559 | _, _ -> env, T.FVnonVariadic
2560 end in
2561 let params = ref f.f_params in
2562 let env, t_params = List.fold_left ~f:(anon_bind_param params) ~init:(env, [])
2563 (List.map supplied_params (fun x -> x.fp_type)) in
2564 let env = List.fold_left ~f:anon_bind_opt_param ~init:env !params in
2565 let env = List.fold_left ~f:anon_check_param ~init:env f.f_params in
2566 let env = match el with
2567 | None ->
2568 iter2_shortest Unify.unify_param_modes ft.ft_params supplied_params;
2570 | Some x ->
2571 iter2_shortest param_modes ft.ft_params x;
2572 wfold_left2 inout_write_back env ft.ft_params x in
2573 let env = Env.set_fn_kind env f.f_fun_kind in
2574 let env, hret =
2575 match f.f_ret with
2576 | None ->
2577 (* Do we have a contextual return type? *)
2578 begin match ret_ty with
2579 | None ->
2580 let env, ret_ty = Env.fresh_unresolved_type env in
2581 env, Typing_return.wrap_awaitable env p ret_ty
2582 | Some ret_ty ->
2583 (* We might need to force it to be Awaitable if it is a type variable *)
2584 Typing_return.force_awaitable env p ret_ty
2586 | Some x ->
2587 let ret = TI.instantiable_hint env x in
2588 (* If a 'this' type appears it needs to be compatible with the
2589 * late static type
2591 let ety_env =
2592 { (Phase.env_with_self env) with
2593 from_class = Some CIstatic } in
2594 Phase.localize ~ety_env env ret in
2595 let env = Env.set_return env
2596 (Typing_return.make_info f.f_fun_kind [] env
2597 ~is_explicit:(Option.is_some ret_ty)
2598 ~is_by_ref:f.f_ret_by_ref
2599 hret) in
2600 let env, tb = block env nb.fnb_nast in
2601 let env =
2602 if Nast_terminality.Terminal.block tenv nb.fnb_nast
2603 || nb.fnb_unsafe || !auto_complete
2604 then env
2605 else fun_implicit_return env p hret f.f_fun_kind
2607 (* We don't want the *uses* of the function to affect its return type *)
2608 let env, hret = Env.unbind env hret in
2609 is_typing_self := false;
2610 let tfun_ = {
2611 T.f_annotation = save_env env;
2612 T.f_mode = f.f_mode;
2613 T.f_ret = f.f_ret;
2614 T.f_name = f.f_name;
2615 T.f_tparams = f.f_tparams;
2616 T.f_where_constraints = f.f_where_constraints;
2617 T.f_fun_kind = f.f_fun_kind;
2618 T.f_user_attributes = List.map f.f_user_attributes (user_attribute env);
2619 T.f_body = T.NamedBody {
2620 T.fnb_nast = tb;
2621 T.fnb_unsafe = nb.fnb_unsafe;
2623 T.f_params = t_params;
2624 T.f_variadic = t_variadic; (* TODO TAST: Variadic efuns *)
2625 T.f_ret_by_ref = f.f_ret_by_ref;
2626 } in
2627 let ty = (Reason.Rwitness p, Tfun ft) in
2628 let te = T.make_typed_expr p ty (T.Efun (tfun_, idl)) in
2629 env, te, hret
2633 (*****************************************************************************)
2634 (* End of anonymous functions. *)
2635 (*****************************************************************************)
2637 and special_func env p func =
2638 let env, tfunc, ty = (match func with
2639 | Gena e ->
2640 let env, te, ety = expr env e in
2641 let env, ty = Async.gena env p ety in
2642 env, T.Gena te, ty
2643 | Genva el ->
2644 let env, tel, etyl = exprs env el in
2645 let env, ty = Async.genva env p etyl in
2646 env, T.Genva tel, ty
2647 | Gen_array_rec e ->
2648 let env, te, ety = expr env e in
2649 let env, ty = Async.gen_array_rec env p ety in
2650 env, T.Gen_array_rec te, ty
2651 ) in
2652 let result_ty =
2653 (Reason.Rwitness p, Tclass ((p, SN.Classes.cAwaitable), [ty])) in
2654 env, T.make_typed_expr p result_ty (T.Special_func tfunc), result_ty
2656 and requires_consistent_construct = function
2657 | CIstatic -> true
2658 | CIexpr _ -> true
2659 | CIparent -> false
2660 | CIself -> false
2661 | CI _ -> false
2663 (* Caller will be looking for a particular form of expected type
2664 * e.g. a function type (when checking lambdas) or tuple type (when checking
2665 * tuples). First expand the expected type and elide single union; also
2666 * strip nullables, so ?t becomes t, as context will always accept a t if a ?t
2667 * is expected.
2669 and expand_expected env expected =
2670 match expected with
2671 | None ->
2672 env, None
2673 | Some (p, ur, ty) ->
2674 let env, ty = Env.expand_type env ty in
2675 match ty with
2676 | _, Tany -> env, None
2677 | _, Tunresolved [ty] -> env, Some (p, ur, ty)
2678 | _, Toption ty -> env, Some (p, ur, ty)
2679 | _ -> env, Some (p, ur, ty)
2681 (* Do a subtype check of inferred type against expected type *)
2682 and check_expected_ty message env inferred_ty expected =
2683 match expected with
2684 | None ->
2686 | Some (p, ur, expected_ty) ->
2687 Typing_log.log_types 1 p env
2688 [Typing_log.Log_sub
2689 (Printf.sprintf "Typing.check_expected_ty %s" message,
2690 [Typing_log.Log_type ("inferred_ty", inferred_ty);
2691 Typing_log.Log_type ("expected_ty", expected_ty)])];
2692 Type.sub_type p ur env inferred_ty expected_ty
2694 and new_object ~expected ~check_parent ~check_not_abstract ~is_using_clause p env cid el uel =
2695 (* Obtain class info from the cid expression. We get multiple
2696 * results with a CIexpr that has a union type *)
2697 let env, tcid, classes = instantiable_cid p env cid in
2698 let finish env tcid tel tuel ty =
2699 let env, new_ty =
2700 if check_parent then env, ty
2701 else ExprDepTy.make env cid ty in
2702 env, tcid, tel, tuel, new_ty in
2703 let rec gather env tel tuel res classes =
2704 match classes with
2705 | [] ->
2706 begin
2707 match res with
2708 | [] ->
2709 let _ = exprs env el in
2710 finish env tcid tel tuel (Reason.Runknown_class p, Tobject)
2711 | [ty] -> finish env tcid tel tuel ty
2712 | tyl -> finish env tcid tel tuel (Reason.Rwitness p, Tunresolved tyl)
2715 | (cname, class_info, c_ty)::classes ->
2716 if check_not_abstract && class_info.tc_abstract
2717 && not (requires_consistent_construct cid) then
2718 uninstantiable_error p cid class_info.tc_pos class_info.tc_name p c_ty;
2719 let env, obj_ty_, params =
2720 match cid, snd c_ty with
2721 | CI (_, _::_), Tclass(_, tyl) -> env, (snd c_ty), tyl
2722 | _, _ ->
2723 let env, params = List.map_env env class_info.tc_tparams
2724 (fun env _ -> Env.fresh_unresolved_type env) in
2725 env, (Tclass (cname, params)), params in
2726 if not check_parent && not is_using_clause && class_info.tc_is_disposable
2727 then Errors.invalid_new_disposable p;
2728 let r_witness = Reason.Rwitness p in
2729 let obj_ty = (r_witness, obj_ty_) in
2730 let c_ty =
2731 match cid with
2732 | CIstatic -> (r_witness, TUtils.this_of obj_ty)
2733 | CIexpr _ -> (r_witness, snd c_ty)
2734 | _ -> obj_ty in
2735 let env, new_ty =
2736 if check_parent
2737 then env, c_ty
2738 else ExprDepTy.make env cid c_ty in
2739 let env, _tcid, tel, tuel =
2740 let env = check_expected_ty "New" env new_ty expected in
2741 call_construct p env class_info params el uel cid in
2742 if not (snd class_info.tc_construct) then
2743 (match cid with
2744 | CIstatic -> Errors.new_inconsistent_construct p cname `static
2745 | CIexpr _ -> Errors.new_inconsistent_construct p cname `classname
2746 | _ -> ());
2747 match cid with
2748 | CIparent ->
2749 (match (fst class_info.tc_construct) with
2750 | Some {ce_type = lazy ty; _ } ->
2751 let ety_env = {
2752 type_expansions = [];
2753 substs = SMap.empty;
2754 this_ty = obj_ty;
2755 from_class = None;
2756 } in
2757 let _, ce_type = Phase.localize ~ety_env env ty in
2758 ignore (check_abstract_parent_meth SN.Members.__construct p ce_type)
2759 | None -> ());
2760 gather env tel tuel (obj_ty::res) classes
2761 | CIstatic | CI _ | CIself -> gather env tel tuel (c_ty::res) classes
2762 | CIexpr _ ->
2763 (* When constructing from a (classname) variable, the variable
2764 * dictates what the constructed object is going to be. This allows
2765 * for generic and dependent types to be correctly carried
2766 * through the 'new $foo()' iff the constructed obj_ty is a
2767 * supertype of the variable-dictated c_ty *)
2768 let env = SubType.sub_type env c_ty obj_ty in
2769 gather env tel tuel (c_ty::res) classes
2771 gather env [] [] [] classes
2773 (* FIXME: we need to separate our instantiability into two parts. Currently,
2774 * all this function is doing is checking if a given type is inhabited --
2775 * that is, whether there are runtime values of type T. However,
2776 * instantiability should be the stricter notion that T has a runtime
2777 * constructor; that is, `new T()` should be valid. In particular, interfaces
2778 * are inhabited, but not instantiable.
2779 * To make this work with classname, we likely need to add something like
2780 * concrete_classname<T>, where T cannot be an interface.
2781 * *)
2782 and instantiable_cid p env cid =
2783 let env, te, classes = class_id_for_new p env cid in
2784 begin
2785 List.iter classes begin fun ((pos, name), class_info, c_ty) ->
2786 if class_info.tc_kind = Ast.Ctrait || class_info.tc_kind = Ast.Cenum
2787 then
2788 match cid with
2789 | CIexpr _ | CI _ ->
2790 uninstantiable_error p cid class_info.tc_pos name pos c_ty
2791 | CIstatic | CIparent | CIself -> ()
2792 else if class_info.tc_kind = Ast.Cabstract && class_info.tc_final
2793 then
2794 uninstantiable_error p cid class_info.tc_pos name pos c_ty
2795 else () end;
2796 env, te, classes
2799 and uninstantiable_error reason_pos cid c_tc_pos c_name c_usage_pos c_ty =
2800 let reason_msgl = match cid with
2801 | CIexpr _ ->
2802 let ty_str = "This would be "^Typing_print.error (snd c_ty) in
2803 [(reason_pos, ty_str)]
2804 | _ -> [] in
2805 Errors.uninstantiable_class c_usage_pos c_tc_pos c_name reason_msgl
2807 and exception_ty pos env ty =
2808 let exn_ty = Reason.Rthrow pos, Tclass ((pos, SN.Classes.cThrowable), []) in
2809 Type.sub_type pos (Reason.URthrow) env ty exn_ty
2811 and shape_field_pos = function
2812 | Ast.SFlit (p, _) -> p
2813 | Ast.SFclass_const ((cls_pos, _), (mem_pos, _)) -> Pos.btw cls_pos mem_pos
2815 and check_shape_keys_validity env pos keys =
2816 (* If the key is a class constant, get its class name and type. *)
2817 let get_field_info env key =
2818 let key_pos = shape_field_pos key in
2819 (* Empty strings or literals that start with numbers are not
2820 permitted as shape field names. *)
2821 (match key with
2822 | Ast.SFlit (_, key_name) ->
2823 if (String.length key_name = 0) then
2824 (Errors.invalid_shape_field_name_empty key_pos)
2825 else if (key_name.[0] >= '0' && key_name.[0] <='9') then
2826 (Errors.invalid_shape_field_name_number key_pos);
2827 env, key_pos, None
2828 | Ast.SFclass_const (_, cls as x, y) ->
2829 let env, _te, ty = class_const env pos (((), CI (x, [])), y) in
2830 let env = Typing_enum.check_valid_array_key_type
2831 Errors.invalid_shape_field_type ~allow_any:false
2832 env key_pos ty in
2833 env, key_pos, Some (cls, ty))
2836 let check_field witness_pos witness_info env key =
2837 let env, key_pos, key_info = get_field_info env key in
2838 (match witness_info, key_info with
2839 | Some _, None ->
2840 Errors.invalid_shape_field_literal key_pos witness_pos; env
2841 | None, Some _ ->
2842 Errors.invalid_shape_field_const key_pos witness_pos; env
2843 | None, None -> env
2844 | Some (cls1, ty1), Some (cls2, ty2) ->
2845 if cls1 <> cls2 then
2846 Errors.shape_field_class_mismatch
2847 key_pos witness_pos (strip_ns cls2) (strip_ns cls1);
2848 (* We want to use our own error message here instead of the normal
2849 * unification one. *)
2850 Errors.try_
2851 (fun () -> Unify.iunify env ty1 ty2)
2852 (fun _ ->
2853 Errors.shape_field_type_mismatch
2854 key_pos witness_pos
2855 (Typing_print.error (snd ty2)) (Typing_print.error (snd ty1));
2856 env))
2859 (* Sort the keys by their positions since the error messages will make
2860 * more sense if we take the one that appears first as canonical and if
2861 * they are processed in source order. *)
2862 let cmp_keys x y = Pos.compare (shape_field_pos x) (shape_field_pos y) in
2863 let keys = List.sort cmp_keys keys in
2865 match keys with
2866 | [] -> env
2867 | witness :: rest_keys ->
2868 let env, pos, info = get_field_info env witness in
2869 List.fold_left ~f:(check_field pos info) ~init:env rest_keys
2871 and check_valid_rvalue p env ty =
2872 let rec iter_over_types env tyl =
2873 match tyl with
2874 | [] ->
2875 env, ty
2877 | ty::tyl ->
2878 let env, ety = Env.expand_type env ty in
2879 match ety with
2880 | r, Tprim Tnoreturn ->
2881 Errors.noreturn_usage p
2882 (Reason.to_string "A noreturn function always throws or exits" r);
2883 env, (r, Typing_utils.terr env)
2885 | r, Tprim Tvoid ->
2886 Errors.void_usage p
2887 (Reason.to_string "A void function doesn't return a value" r);
2888 env, (r, Typing_utils.terr env)
2890 | _, Tunresolved tyl2 ->
2891 iter_over_types env (tyl2 @ tyl)
2893 | _, _ ->
2894 iter_over_types env tyl in
2895 iter_over_types env [ty]
2897 and set_valid_rvalue p env x ty =
2898 let env, ty = check_valid_rvalue p env ty in
2899 let env = set_local env (p, x) ty in
2900 (* We are assigning a new value to the local variable, so we need to
2901 * generate a new expression id
2903 let env = Env.set_local_expr_id env x (Ident.tmp()) in
2904 env, ty
2906 (* Deal with assignment of a value of type ty2 to lvalue e1 *)
2907 and assign p env e1 ty2 : _ * T.expr * T.ty =
2908 assign_ p Reason.URassign env e1 ty2
2910 and assign_ p ur env e1 ty2 =
2911 let make_result env te1 ty1 = (env, T.make_typed_expr (fst e1) ty1 te1, ty1) in
2912 match e1 with
2913 | (_, Lvar ((_, x) as id)) ->
2914 let env, ty1 = set_valid_rvalue p env x ty2 in
2915 make_result env (T.Lvar id) ty1
2916 | (_, Lplaceholder id) ->
2917 let placeholder_ty = Reason.Rplaceholder p, (Tprim Tvoid) in
2918 make_result env (T.Lplaceholder id) placeholder_ty
2919 | (_, List el) ->
2920 let env, folded_ty2 = TUtils.fold_unresolved env ty2 in
2921 let resl =
2922 try_over_concrete_supertypes env folded_ty2
2923 begin fun env ty2 ->
2924 match ty2 with
2925 (* Vector<t> or ImmVector<t> or ConstVector<t> or vec<T> *)
2926 | (_, Tclass ((_, x), [elt_type]))
2927 when x = SN.Collections.cVector
2928 || x = SN.Collections.cImmVector
2929 || x = SN.Collections.cVec
2930 || x = SN.Collections.cConstVector ->
2931 (* Vector assignment is illegal in a reactive context
2932 but vec assignment is okay *)
2933 if x <> SN.Collections.cVec
2934 then Env.error_if_reactive_context env @@ begin fun () ->
2935 Errors.nonreactive_append p
2936 end;
2937 let env, tel = List.map_env env el begin fun env e ->
2938 let env, te, _ = assign (fst e) env e elt_type in
2939 env, te
2940 end in
2941 make_result env (T.List tel) ty2
2942 (* array<t> or varray<t> *)
2943 | (_, Tarraykind (AKvec elt_type))
2944 | (_, Tarraykind (AKvarray elt_type)) ->
2945 let env, tel = List.map_env env el begin fun env e ->
2946 let env, te, _ = assign (fst e) env e elt_type in
2947 env, te
2948 end in
2949 make_result env (T.List tel) ty2
2950 (* array or empty array or Tany *)
2951 | (r, (Tarraykind (AKany | AKempty) | Tany)) ->
2952 let env, tel = List.map_env env el begin fun env e ->
2953 let env, te, _ = assign (fst e) env e (r, Typing_utils.tany env) in
2954 env, te
2955 end in
2956 make_result env (T.List tel) ty2
2957 | (r, (Tdynamic)) ->
2958 let env, tel = List.map_env env el begin fun env e ->
2959 let env, te, _ = assign (fst e) env e (r, Tdynamic) in
2960 env, te
2961 end in
2962 make_result env (T.List tel) ty2
2963 (* Pair<t1,t2> *)
2964 | ((r, Tclass ((_, coll), [ty1; ty2])) as folded_ety2)
2965 when coll = SN.Collections.cPair ->
2966 (match el with
2967 | [x1; x2] ->
2968 let env, te1, _ = assign p env x1 ty1 in
2969 let env, te2, _ = assign p env x2 ty2 in
2970 make_result env (T.List [te1; te2]) folded_ety2
2971 | _ ->
2972 Errors.pair_arity p;
2973 make_result env T.Any (r, Typing_utils.terr env))
2974 (* tuple-like array *)
2975 | (r, Tarraykind (AKtuple fields)) ->
2976 let p1 = fst e1 in
2977 let p2 = Reason.to_pos r in
2978 let tyl = List.rev (IMap.values fields) in
2979 let size1 = List.length el in
2980 let size2 = List.length tyl in
2981 if size1 <> size2
2982 then begin
2983 Errors.tuple_arity p2 size2 p1 size1;
2984 make_result env T.Any (r, Typing_utils.terr env)
2986 else
2987 let env, reversed_tel =
2988 List.fold2_exn el tyl ~f:begin fun (env,tel) lvalue ty2 ->
2989 let env, te, _ = assign p env lvalue ty2 in
2990 env, te::tel
2991 end ~init:(env,[]) in
2992 make_result env (T.List (List.rev reversed_tel)) ty2
2993 (* Other, including tuples. Create a tuple type for the left hand
2994 * side and attempt subtype against it. In particular this deals with
2995 * types such as (string,int) | (int,bool) *)
2996 | _ ->
2997 let env, tyl =
2998 List.map_env env el (fun env _ -> Env.fresh_unresolved_type env) in
2999 let env = Type.sub_type p ur env folded_ty2
3000 (Reason.Rwitness (fst e1), Ttuple tyl) in
3001 let env, reversed_tel =
3002 List.fold2_exn el tyl ~init:(env,[]) ~f:(fun (env,tel) lvalue ty2 ->
3003 let env, te, _ = assign p env lvalue ty2 in
3004 env, te::tel) in
3005 make_result env (T.List (List.rev reversed_tel)) ty2
3006 end in
3007 begin match resl with
3008 | [res] -> res
3009 | _ -> assign_simple p ur env e1 ty2
3012 | _, Class_get _
3013 | _, Obj_get _ ->
3014 Env.not_lambda_reactive ();
3015 let lenv = env.Env.lenv in
3016 let no_fakes = LEnv.env_with_empty_fakes env in
3017 (* In this section, we check that the assignment is compatible with
3018 * the real type of a member. Remember that members can change
3019 * type (cf fake_members). But when we assign a value to $this->x,
3020 * we want to make sure that the type assign to $this->x is compatible
3021 * with the actual type hint. In this portion of the code, type-check
3022 * the assignment in an environment without fakes, and therefore
3023 * check that the assignment is compatible with the type of
3024 * the member.
3026 let env, te1, real_type = lvalue no_fakes e1 in
3027 let env, exp_real_type = Env.expand_type env real_type in
3028 let env = { env with Env.lenv = lenv } in
3029 let env, ety2 = Env.expand_type env ty2 in
3030 let real_type_list =
3031 match exp_real_type with
3032 | _, Tunresolved tyl -> tyl
3033 | ty -> [ty]
3035 let env = List.fold_left real_type_list ~f:begin fun env real_type ->
3036 Type.sub_type p ur env ety2 real_type
3037 end ~init:env in
3038 (match e1 with
3039 | _, Obj_get ((_, This | _, Lvar _ as obj),
3040 (_, Id (_, member_name)),
3041 _) ->
3042 let env, local = Env.FakeMembers.make p env obj member_name in
3043 let () = (match obj with
3044 | _, This ->
3045 Typing_suggest.save_member member_name env exp_real_type ty2
3046 | _ -> ()
3047 ) in
3048 let env, ty = set_valid_rvalue p env local ty2 in
3049 env, te1, ty
3050 | _, Class_get (((), x), (_, y)) ->
3051 let env, local = Env.FakeMembers.make_static p env x y in
3052 let env, ty3 = set_valid_rvalue p env local ty2 in
3053 (match x with
3054 | CIself
3055 | CIstatic ->
3056 Typing_suggest.save_member y env exp_real_type ty2;
3057 | _ -> ());
3058 env, te1, ty3
3059 | _ -> env, te1, ty2
3061 | _, Array_get ((_, Lvar (_, lvar)) as shape, ((Some _) as e2)) ->
3062 let access_type = Typing_arrays.static_array_access env e2 in
3063 (* In the case of an assignment of the form $x['new_field'] = ...;
3064 * $x could be a shape where the field 'new_field' is not yet defined.
3065 * When that is the case we want to add the field to its type.
3067 let env, _te, shape_ty = expr env shape in
3068 let env, shape_ty = Typing_arrays.update_array_type_on_lvar_assignment
3069 p access_type env shape_ty in
3070 let env, _ = set_valid_rvalue p env lvar shape_ty in
3071 (* We still need to call assign_simple in order to bind the freshly
3072 * created variable in added shape field. Moreover, it's needed because
3073 * shape_ty could be more than just a shape. It could be an unresolved
3074 * type where some elements are shapes and some others are not.
3076 assign_simple p ur env e1 ty2
3077 | _, This ->
3078 Errors.this_lvalue p;
3079 make_result env T.Any (Reason.Rwitness p, Typing_utils.terr env)
3080 | pref, Unop (Ast.Uref, e1') ->
3081 (* references can be "lvalues" in foreach bindings *)
3082 if TypecheckerOptions.experimental_feature_enabled
3083 (Env.get_options env)
3084 TypecheckerOptions.experimental_disallow_refs_in_array
3085 then Errors.binding_ref_in_array pref
3086 else if Env.is_strict env
3087 then Errors.reference_expr pref;
3088 let env, texpr, ty = assign p env e1' ty2 in
3089 make_result env (T.Unop (Ast.Uref, texpr)) ty
3090 | _ ->
3091 assign_simple p ur env e1 ty2
3093 and assign_simple pos ur env e1 ty2 =
3094 let env, te1, ty1 = lvalue env e1 in
3095 let env, ty2 = check_valid_rvalue pos env ty2 in
3096 let env, ty2 = TUtils.unresolved env ty2 in
3097 let env = Type.sub_type pos ur env ty2 ty1 in
3098 env, te1, ty2
3100 and array_field env = function
3101 | Nast.AFvalue ve ->
3102 let env, tve, tv = expr env ve in
3103 let env, tv = Typing_env.unbind env tv in
3104 env, (T.AFvalue tve, None, tv)
3105 | Nast.AFkvalue (ke, ve) ->
3106 let env, tke, tk = expr env ke in
3107 let env, tve, tv = expr env ve in
3108 let env, tv = Typing_env.unbind env tv in
3109 env, (T.AFkvalue (tke, tve), Some tk, tv)
3111 and array_value ~expected env x =
3112 let env, te, ty = expr ?expected ~forbid_uref:true env x in
3113 let env, ty = Typing_env.unbind env ty in
3114 env, (te, ty)
3116 and array_field_value ~expected env = function
3117 | Nast.AFvalue x
3118 | Nast.AFkvalue (_, x) ->
3119 array_value ~expected env x
3121 and array_field_key ~expected env = function
3122 (* This shouldn't happen *)
3123 | Nast.AFvalue (p, _) ->
3124 env, (T.make_implicitly_typed_expr p T.Any,
3125 (Reason.Rwitness p, Tprim Tint))
3126 | Nast.AFkvalue (x, _) ->
3127 array_value ~expected env x
3129 and akshape_field env = function
3130 | Nast.AFkvalue (k, v) ->
3131 let env, tek, tk = expr env k in
3132 let env, tk = Typing_env.unbind env tk in
3133 let env, tk = TUtils.unresolved env tk in
3134 let env, tev, tv = expr env v in
3135 let env, tv = Typing_env.unbind env tv in
3136 let env, tv = TUtils.unresolved env tv in
3137 let field_name =
3138 match TUtils.shape_field_name env Pos.none (snd k) with
3139 | Some field_name -> field_name
3140 | None -> assert false in (* Typing_arrays.is_shape_like_array
3141 * should have prevented this *)
3142 env, T.AFkvalue (tek, tev), (field_name, (tk, tv))
3143 | Nast.AFvalue _ -> assert false (* Typing_arrays.is_shape_like_array
3144 * should have prevented this *)
3146 and aktuple_afvalue env v =
3147 let env, tev, tv = expr env v in
3148 let env, tv = Typing_env.unbind env tv in
3149 let env, ty = TUtils.unresolved env tv in
3150 env, tev, ty
3152 and aktuple_field env = function
3153 | Nast.AFvalue v -> aktuple_afvalue env v
3154 | Nast.AFkvalue _ -> assert false (* check_consistent_fields
3155 * should have prevented this *)
3156 and check_parent_construct pos env el uel env_parent =
3157 let check_not_abstract = false in
3158 let env, env_parent = Phase.localize_with_self env env_parent in
3159 let env, _tcid, tel, tuel, parent =
3160 new_object ~expected:None ~check_parent:true ~check_not_abstract
3161 ~is_using_clause:false
3162 pos env CIparent el uel in
3163 let env, _ = Type.unify pos (Reason.URnone) env env_parent parent in
3164 env, tel, tuel, (Reason.Rwitness pos, Tprim Tvoid)
3166 and call_parent_construct pos env el uel =
3167 let parent = Env.get_parent env in
3168 match parent with
3169 | _, Tapply _ ->
3170 check_parent_construct pos env el uel parent
3171 | _,
3173 Tany
3174 | Tdynamic
3175 | Tmixed
3176 | Tnonnull
3177 | Tarray (_, _)
3178 | Tdarray (_, _)
3179 | Tvarray _
3180 | Tvarray_or_darray _
3181 | Tgeneric _
3182 | Toption _
3183 | Tprim _
3184 | Terr
3185 | Tfun _
3186 | Ttuple _
3187 | Tshape _
3188 | Taccess (_, _)
3189 | Tthis
3190 ) -> (* continue here *)
3191 let default = env, [], [], (Reason.Rwitness pos, Typing_utils.tany env) in
3192 match Env.get_self env with
3193 | _, Tclass ((_, self), _) ->
3194 (match Env.get_class env self with
3195 | Some ({tc_kind = Ast.Ctrait; _}
3196 as trait) ->
3197 (match trait_most_concrete_req_class trait env with
3198 | None -> Errors.parent_in_trait pos; default
3199 | Some (_, parent_ty) ->
3200 check_parent_construct pos env el uel parent_ty
3202 | Some self_tc ->
3203 if not self_tc.tc_members_fully_known
3204 then () (* Don't know the hierarchy, assume it's correct *)
3205 else Errors.undefined_parent pos;
3206 default
3207 | None -> assert false)
3208 | _, (Terr | Tany | Tmixed | Tnonnull | Tarraykind _ | Toption _
3209 | Tprim _ | Tfun _ | Ttuple _ | Tshape _ | Tvar _ | Tdynamic
3210 | Tabstract (_, _) | Tanon (_, _) | Tunresolved _ | Tobject
3211 ) ->
3212 Errors.parent_outside_class pos;
3213 env, [], [], (Reason.Rwitness pos, Typing_utils.terr env)
3215 (* parent::method() in a class definition invokes the specific parent
3216 * version of the method ... it better be callable *)
3217 and check_abstract_parent_meth mname pos fty =
3218 if is_abstract_ft fty
3219 then Errors.parent_abstract_call mname pos (Reason.to_pos (fst fty));
3222 and is_abstract_ft fty = match fty with
3223 | _r, Tfun { ft_abstract = true; _ } -> true
3224 | _r, (Terr | Tany | Tmixed | Tnonnull | Tarraykind _ | Toption _ | Tprim _
3225 | Tvar _ | Tfun _ | Tclass (_, _) | Tabstract (_, _) | Ttuple _
3226 | Tanon _ | Tunresolved _ | Tobject | Tshape _ | Tdynamic
3228 -> false
3230 (* Depending on the kind of expression we are dealing with
3231 * The typing of call is different.
3234 and dispatch_call ~expected ~is_using_clause p env call_type
3235 (fpos, fun_expr as e) hl el uel ~in_suspend =
3236 let make_call env te thl tel tuel ty =
3237 env, T.make_typed_expr p ty (T.Call (call_type, te, thl, tel, tuel)), ty in
3238 let make_call_special env id tel ty =
3239 make_call env (T.make_implicitly_typed_expr fpos (T.Id id)) [] tel [] ty in
3241 let check_coroutine_call env fty =
3242 let () = if is_return_disposable_fun_type env fty && not is_using_clause
3243 then Errors.invalid_new_disposable p else () in
3244 (* returns
3245 - Some true if type is definitely a coroutine
3246 - Some false if type is definitely not a coroutine
3247 - None if type is Tunresolved that contains
3248 both coroutine and non-coroutine constituents *)
3249 let rec is_coroutine ty =
3250 match snd ty with
3251 | Tfun { ft_is_coroutine = true; _ } ->
3252 Some true
3253 | Tanon (_, id) ->
3254 Some (Option.value_map (Env.get_anonymous env id) ~default:false ~f:(fun (_,b,_,_,_) -> b) )
3255 | Tunresolved ts ->
3256 begin match List.map ts ~f:is_coroutine with
3257 | None :: _ -> None
3258 | Some x :: xs ->
3259 (*if rest of the list has the same value as the first element
3260 return value of the first element or None otherwise*)
3261 if List.for_all xs ~f:(Option.value_map ~default:false ~f:((=)x))
3262 then Some x
3263 else None
3264 | _ -> Some false
3266 | _ ->
3267 Some false in
3268 match in_suspend, is_coroutine fty with
3269 | true, Some true
3270 | false, Some false -> ()
3271 | true, _ ->
3272 (* non-coroutine call in suspend *)
3273 Errors.non_coroutine_call_in_suspend
3274 fpos
3275 (Reason.to_string ("This is " ^ Typing_print.error (snd fty)) (fst fty));
3276 | false, _ ->
3277 (*coroutine call outside of suspend *)
3278 Errors.coroutine_call_outside_of_suspend p; in
3280 let check_function_in_suspend name =
3281 if in_suspend
3282 then Errors.function_is_not_coroutine fpos name in
3284 let check_class_function_in_suspend class_name function_name =
3285 check_function_in_suspend (class_name ^ "::" ^ function_name) in
3287 match fun_expr with
3288 (* Special function `echo` *)
3289 | Id ((p, pseudo_func) as id) when pseudo_func = SN.SpecialFunctions.echo ->
3290 check_function_in_suspend SN.SpecialFunctions.echo;
3291 Env.error_if_shallow_reactive_context env @@ begin fun () ->
3292 Errors.echo_in_reactive_context p;
3293 end;
3294 let env, tel, _ = exprs ~accept_using_var:true env el in
3295 make_call_special env id tel (Reason.Rwitness p, Tprim Tvoid)
3296 (* Special function `empty` *)
3297 | Id ((_, pseudo_func) as id) when pseudo_func = SN.PseudoFunctions.empty ->
3298 check_function_in_suspend SN.PseudoFunctions.empty;
3299 let env, tel, _ = exprs ~accept_using_var:true env el in
3300 if uel <> [] then
3301 Errors.unpacking_disallowed_builtin_function p pseudo_func;
3302 if Env.is_strict env then
3303 Errors.empty_in_strict p;
3304 make_call_special env id tel (Reason.Rwitness p, Tprim Tbool)
3305 (* Special function `isset` *)
3306 | Id ((_, pseudo_func) as id) when pseudo_func = SN.PseudoFunctions.isset ->
3307 check_function_in_suspend SN.PseudoFunctions.isset;
3308 let env, tel, _ = exprs ~accept_using_var:true env el in
3309 if uel <> [] then
3310 Errors.unpacking_disallowed_builtin_function p pseudo_func;
3311 if Env.is_strict env then
3312 Errors.isset_in_strict p;
3313 make_call_special env id tel (Reason.Rwitness p, Tprim Tbool)
3314 (* Special function `unset` *)
3315 | Id ((_, pseudo_func) as id) when pseudo_func = SN.PseudoFunctions.unset ->
3316 check_function_in_suspend SN.PseudoFunctions.unset;
3317 let env, tel, _ = exprs env el in
3318 if uel <> [] then
3319 Errors.unpacking_disallowed_builtin_function p pseudo_func;
3320 let env = if Env.is_strict env then
3321 (match el, uel with
3322 | [(_, Array_get ((_, Class_const _), Some _))], [] ->
3323 Errors.const_mutation p Pos.none "";
3325 | [(_, Array_get (ea, Some _))], [] ->
3326 let env, _te, ty = expr env ea in
3327 let tany = Typing_utils.tany env in
3328 if List.exists ~f:(fun super -> SubType.is_sub_type env ty super) [
3329 (Reason.Rnone, (Tclass ((Pos.none, SN.Collections.cDict),
3330 [(Reason.Rnone, tany); (Reason.Rnone, tany)])));
3331 (Reason.Rnone, (Tclass ((Pos.none, SN.Collections.cKeyset),
3332 [(Reason.Rnone, tany)])));
3333 (Reason.Rnone, Tarraykind AKany)
3334 ] then env
3335 else begin
3336 let env, (r, ety) = Env.expand_type env ty in
3337 Errors.unset_nonidx_in_strict
3339 (Reason.to_string ("This is " ^ Typing_print.error ety) r);
3342 | _ -> Errors.unset_nonidx_in_strict p []; env)
3343 else env in
3344 (match el with
3345 | [(p, Obj_get (_, _, OG_nullsafe))] ->
3346 begin
3347 Errors.nullsafe_property_write_context p;
3348 make_call_special env id tel (Reason.Rwitness p, Typing_utils.terr env)
3349 end;
3350 | _ ->
3351 make_call_special env id tel (Reason.Rwitness p, Tprim Tvoid))
3352 (* Special function `freeze` *)
3353 | Id ((_, pseudo_func) as id) when pseudo_func = SN.PseudoFunctions.freeze ->
3354 check_function_in_suspend SN.PseudoFunctions.freeze;
3355 let env, tel, _ = exprs env el in
3356 if uel <> [] then
3357 Errors.unpacking_disallowed_builtin_function p pseudo_func;
3358 if not (Env.env_local_reactive env) then
3359 Errors.freeze_in_nonreactive_context p;
3360 let env = Typing_mutability.freeze_local p env tel in
3361 make_call_special env id tel (Reason.Rwitness p, Tprim Tvoid)
3362 (* Pseudo-function `get_called_class` *)
3363 | Id (cp, get_called_class) when
3364 get_called_class = SN.StdlibFunctions.get_called_class
3365 && el = [] && uel = [] ->
3366 check_function_in_suspend SN.StdlibFunctions.get_called_class;
3367 (* get_called_class fetches the late-bound class *)
3368 if Env.is_outside_class env then Errors.static_outside_class p;
3369 class_const env p (((), CIstatic), (cp, SN.Members.mClass))
3370 (* Special function `array_filter` *)
3371 | Id ((_, array_filter) as id)
3372 when array_filter = SN.StdlibFunctions.array_filter && el <> [] && uel = [] ->
3373 check_function_in_suspend SN.StdlibFunctions.array_filter;
3374 (* dispatch the call to typecheck the arguments *)
3375 let env, fty = fun_type_of_id env id hl in
3376 let env, tel, tuel, res = call ~expected p env fty el uel in
3377 (* but ignore the result and overwrite it with custom return type *)
3378 let x = List.hd_exn el in
3379 let env, _tx, ty = expr env x in
3380 let explain_array_filter (r, t) =
3381 (Reason.Rarray_filter (p, r), t) in
3382 let get_value_type env tv =
3383 let env, tv =
3384 if List.length el > 1
3385 then env, tv
3386 else TUtils.non_null env tv in
3387 env, explain_array_filter tv in
3388 let rec get_array_filter_return_type env ty =
3389 let env, ety = Env.expand_type env ty in
3390 (match ety with
3391 | (_, Tarraykind (AKany | AKempty)) as array_type ->
3392 env, array_type
3393 | (_, Tarraykind (AKtuple _)) ->
3394 let env, ty = Typing_arrays.downcast_aktypes env ty in
3395 get_array_filter_return_type env ty
3396 | (r, Tarraykind (AKvec tv | AKvarray tv)) ->
3397 let env, tv = get_value_type env tv in
3398 env, (r, Tarraykind (AKvec tv))
3399 | (r, Tunresolved x) ->
3400 let env, x = List.map_env env x get_array_filter_return_type in
3401 env, (r, Tunresolved x)
3402 | (r, Tany) ->
3403 env, (r, Typing_utils.tany env)
3404 | (r, Terr) ->
3405 env, (r, Typing_utils.terr env)
3406 | (r, _) ->
3407 let tk, tv = Env.fresh_type (), Env.fresh_type () in
3408 Errors.try_
3409 (fun () ->
3410 let keyed_container = (
3411 Reason.Rnone,
3412 Tclass (
3413 (Pos.none, SN.Collections.cKeyedContainer), [tk; tv]
3415 ) in
3416 let env = SubType.sub_type env ety keyed_container in
3417 let env, tv = get_value_type env tv in
3418 env, (r, Tarraykind (AKmap (
3419 (explain_array_filter tk),
3422 (fun _ -> Errors.try_
3423 (fun () ->
3424 let container = (
3425 Reason.Rnone,
3426 Tclass (
3427 (Pos.none, SN.Collections.cContainer), [tv]
3429 ) in
3430 let env = SubType.sub_type env ety container in
3431 let env, tv = get_value_type env tv in
3432 env, (r, Tarraykind (AKmap (
3433 (explain_array_filter (r, Tprim Tarraykey)),
3434 tv))))
3435 (fun _ -> env, res)))
3436 in let env, rty = get_array_filter_return_type env ty in
3437 make_call env (T.make_implicitly_typed_expr fpos (T.Id id)) hl tel tuel rty
3438 (* Special function `type_structure` *)
3439 | Id (p, type_structure)
3440 when type_structure = SN.StdlibFunctions.type_structure
3441 && (List.length el = 2) && uel = [] ->
3442 check_function_in_suspend SN.StdlibFunctions.type_structure;
3443 (match el with
3444 | [e1; e2] ->
3445 (match e2 with
3446 | p, Nast.String cst ->
3447 (* find the class constant implicitly defined by the typeconst *)
3448 let cid = (match e1 with
3449 | _, Class_const (cid, (_, x))
3450 | _, Class_get (cid, (_, x)) when x = SN.Members.mClass -> cid
3451 | _ -> ((), Nast.CIexpr e1)) in
3452 class_const ~incl_tc:true env p (cid, cst)
3453 | _ ->
3454 Errors.illegal_type_structure p "second argument is not a string";
3455 expr_error env p (Reason.Rwitness p))
3456 | _ -> assert false)
3457 (* Special function `array_map` *)
3458 | Id ((_, array_map) as x)
3459 when array_map = SN.StdlibFunctions.array_map && el <> [] && uel = [] ->
3460 check_function_in_suspend SN.StdlibFunctions.array_map;
3461 let env, fty = fun_type_of_id env x [] in
3462 let env, fty = Env.expand_type env fty in
3463 let env, fty = match fty, el with
3464 | ((r_fty, Tfun fty), _::args) when args <> [] ->
3465 let arity = List.length args in
3467 Builds a function with signature:
3469 function<T1, ..., Tn, Tr>(
3470 (function(T1, ..., Tn):Tr),
3471 Container<T1>,
3472 ...,
3473 Container<Tn>,
3474 ): R
3476 where R is constructed by build_output_container applied to Tr
3478 let build_function env build_output_container =
3479 let env, vars, tr =
3480 (* If T1, ... Tn, Tr are provided explicitly, instantiate the function parameters with
3481 * those directly. *)
3482 if List.length hl = 0
3483 then
3484 let env, tr = Env.fresh_unresolved_type env in
3485 env, List.map args (fun _ -> Env.fresh_type()), tr
3486 else if List.length hl <> List.length args + 1 then begin
3487 let env, tr = Env.fresh_unresolved_type env in
3488 Errors.expected_tparam fty.ft_pos (1 + (List.length args));
3489 env, List.map args (fun _ -> Env.fresh_type()), tr end
3490 else
3491 let env, vars_and_tr = List.map_env env hl Phase.hint_locl in
3492 let vars, trl = List.split_n vars_and_tr (List.length vars_and_tr - 1) in
3493 (* Since we split the arguments and return type at the last index and the length is
3494 non-zero this is safe. *)
3495 let tr = List.hd_exn trl in
3496 env, vars, tr
3498 let f = TUtils.default_fun_param (
3499 r_fty,
3500 Tfun {
3501 ft_pos = fty.ft_pos;
3502 ft_deprecated = None;
3503 ft_abstract = false;
3504 ft_is_coroutine = false;
3505 ft_arity = Fstandard (arity, arity);
3506 ft_tparams = [];
3507 ft_where_constraints = [];
3508 ft_params = List.map vars TUtils.default_fun_param;
3509 ft_ret = tr;
3510 ft_ret_by_ref = fty.ft_ret_by_ref;
3511 ft_reactive = fty.ft_reactive;
3512 ft_mutable = fty.ft_mutable;
3513 ft_returns_mutable = fty.ft_returns_mutable;
3514 ft_return_disposable = fty.ft_return_disposable;
3515 ft_decl_errors = None;
3517 ) in
3518 let containers = List.map vars (fun var ->
3519 let tc = Tclass ((fty.ft_pos, SN.Collections.cContainer), [var]) in
3520 TUtils.default_fun_param (r_fty, tc)
3521 ) in
3522 env, (r_fty, Tfun {fty with
3523 ft_arity = Fstandard (arity+1, arity+1);
3524 ft_params = f::containers;
3525 ft_ret = build_output_container tr;
3530 Takes a Container type and returns a function that can "pack" a type
3531 into an array of appropriate shape, preserving the key type, i.e.:
3532 array -> f, where f R = array
3533 array<X> -> f, where f R = array<R>
3534 array<X, Y> -> f, where f R = array<X, R>
3535 Vector<X> -> f where f R = array<R>
3536 KeyedContainer<X, Y> -> f, where f R = array<X, R>
3537 Container<X> -> f, where f R = array<arraykey, R>
3538 X -> f, where f R = Y
3540 let rec build_output_container
3541 (env:Env.env) (x:locl ty) : (Env.env * (locl ty -> locl ty)) =
3542 let env, x = Env.expand_type env x in (match x with
3543 | (_, Tarraykind (AKany | AKempty)) as array_type ->
3544 env, (fun _ -> array_type)
3545 | (_, Tarraykind (AKtuple _ )) ->
3546 let env, x = Typing_arrays.downcast_aktypes env x in
3547 build_output_container env x
3548 | (r, Tarraykind (AKvec _ | AKvarray _)) ->
3549 env, (fun tr -> (r, Tarraykind (AKvec(tr))) )
3550 | (r, Tany) ->
3551 env, (fun _ -> (r, Typing_utils.tany env))
3552 | (r, Terr) ->
3553 env, (fun _ -> (r, Typing_utils.terr env))
3554 | (r, Tunresolved x) ->
3555 let env, x = List.map_env env x build_output_container in
3556 env, (fun tr -> (r, Tunresolved (List.map x (fun f -> f tr))))
3557 | (r, _) ->
3558 let tk, tv = Env.fresh_type(), Env.fresh_type() in
3559 let try_vector env =
3560 let vector = (
3561 r_fty,
3562 Tclass (
3563 (fty.ft_pos, SN.Collections.cConstVector), [tv]
3565 ) in
3566 let env = SubType.sub_type env x vector in
3567 env, (fun tr -> (r, Tarraykind (
3568 AKvec(tr)
3569 ))) in
3570 let try_keyed_container env =
3571 let keyed_container = (
3572 r_fty,
3573 Tclass (
3574 (fty.ft_pos, SN.Collections.cKeyedContainer), [tk; tv]
3576 ) in
3577 let env = SubType.sub_type env x keyed_container in
3578 env, (fun tr -> (r, Tarraykind (AKmap (
3581 )))) in
3582 let try_container env =
3583 let container = (
3584 r_fty,
3585 Tclass (
3586 (fty.ft_pos, SN.Collections.cContainer), [tv]
3588 ) in
3589 let env = SubType.sub_type env x container in
3590 env, (fun tr -> (r, Tarraykind (AKmap (
3591 (r, Tprim Tarraykey),
3592 tr)))) in
3593 Errors.try_
3594 (fun () ->
3595 try_vector env)
3596 (fun _ -> Errors.try_
3597 (fun () ->
3598 try_keyed_container env)
3599 (fun _ -> Errors.try_
3600 (fun () ->
3601 try_container env)
3602 (fun _ -> env, (fun _ -> (Reason.Rwitness p, Typing_utils.tany env)))))) in
3604 Single argument calls preserve the key type, multi argument
3605 calls always return an array<Tr>
3607 (match args with
3608 | [x] ->
3609 let env, _tx, x = expr env x in
3610 let env, output_container = build_output_container env x in
3611 build_function env output_container
3612 | _ ->
3613 build_function env (fun tr ->
3614 (r_fty, Tarraykind (AKvec(tr)))))
3615 | _ -> env, fty in
3616 let env, tel, _tuel, ty = call ~expected p env fty el [] in
3617 make_call_special env x tel ty
3618 (* Special function `idx` *)
3619 | Id ((_, idx) as id) when idx = SN.FB.idx ->
3620 check_function_in_suspend SN.FB.idx;
3621 (* Directly call get_fun so that we can muck with the type before
3622 * instantiation -- much easier to work in terms of Tgeneric Tk/Tv than
3623 * trying to figure out which Tvar is which. *)
3624 Typing_hooks.dispatch_fun_id_hook (p, SN.FB.idx);
3625 (match Env.get_fun env (snd id) with
3626 | Some fty ->
3627 let param1, param2, param3 =
3628 match fty.ft_params with
3629 | [param1; param2; param3] -> param1, param2, param3
3630 | _ -> assert false in
3631 let { fp_type = (r2, _); _ } = param2 in
3632 let { fp_type = (r3, _); _ } = param3 in
3633 let params, ret = match List.length el with
3634 | 2 ->
3635 let ty1 = match param1.fp_type with
3636 | (r11, Toption (r12, Tapply (coll, [tk; (r13, _) as tv]))) ->
3637 (r11, Toption (r12, Tapply (coll, [tk; (r13, Toption tv)])))
3638 | _ -> assert false in
3639 let param1 = { param1 with fp_type = ty1 } in
3640 let ty2 = (r2, Toption (r2, Tgeneric "Tk")) in
3641 let param2 = { param2 with fp_type = ty2 } in
3642 let rret = fst fty.ft_ret in
3643 let ret = (rret, Toption (rret, Tgeneric "Tv")) in
3644 [param1; param2], ret
3645 | 3 ->
3646 let param2 = { param2 with fp_type = (r2, Tgeneric "Tk") } in
3647 let param3 = { param3 with fp_type = (r3, Tgeneric "Tv") } in
3648 let ret = (fst fty.ft_ret, Tgeneric "Tv") in
3649 [param1; param2; param3], ret
3650 | _ -> fty.ft_params, fty.ft_ret in
3651 let fty = { fty with ft_params = params; ft_ret = ret } in
3652 let ety_env = Phase.env_with_self env in
3653 let env, fty = Phase.localize_ft ~use_pos:p ~ety_env env fty in
3654 let tfun = Reason.Rwitness fty.ft_pos, Tfun fty in
3655 let env, tel, _tuel, ty = call ~expected p env tfun el [] in
3656 let env, ty = match ty with
3657 | r, Toption ty ->
3658 let env, ty = TUtils.non_null env ty in
3659 env, (r, Toption ty)
3660 | _ -> env, ty in
3661 make_call_special env id tel ty
3662 | None -> unbound_name env id)
3664 (* Special function `Shapes::idx` *)
3665 | Class_const (((), CI((_, shapes), _)) as class_id, ((_, idx) as method_id))
3666 when shapes = SN.Shapes.cShapes && idx = SN.Shapes.idx ->
3667 check_class_function_in_suspend SN.Shapes.cShapes SN.Shapes.idx;
3668 overload_function p env class_id method_id el uel
3669 begin fun env fty res el -> match el with
3670 | [shape; field] ->
3671 let env, _ts, shape_ty = expr env shape in
3672 Typing_shapes.idx env p fty shape_ty field None
3673 | [shape; field; default] ->
3674 let env, _ts, shape_ty = expr env shape in
3675 let env, _td, default_ty = expr env default in
3676 Typing_shapes.idx env p fty shape_ty field
3677 (Some ((fst default), default_ty))
3678 | _ -> env, res
3680 (* Special function `Shapes::keyExists` *)
3681 | Class_const (((), CI((_, shapes), _)) as class_id, ((_, key_exists) as method_id))
3682 when shapes = SN.Shapes.cShapes && key_exists = SN.Shapes.keyExists ->
3683 check_class_function_in_suspend SN.Shapes.cShapes SN.Shapes.keyExists;
3684 overload_function p env class_id method_id el uel
3685 begin fun env fty res el -> match el with
3686 | [shape; field] ->
3687 let env, _te, shape_ty = expr env shape in
3688 (* try accessing the field, to verify existence, but ignore
3689 * the returned type and keep the one coming from function
3690 * return type hint *)
3691 let env, _ = Typing_shapes.idx env p fty shape_ty field None in
3692 env, res
3693 | _ -> env, res
3695 (* Special function `Shapes::removeKey` *)
3696 | Class_const (((), CI((_, shapes), _)) as class_id, ((_, remove_key) as method_id))
3697 when shapes = SN.Shapes.cShapes && remove_key = SN.Shapes.removeKey ->
3698 check_class_function_in_suspend SN.Shapes.cShapes SN.Shapes.removeKey;
3699 overload_function p env class_id method_id el uel
3700 begin fun env _ res el -> match el with
3701 | [shape; field] -> begin match shape with
3702 | (_, Lvar (_, lvar))
3703 | (_, Unop (Ast.Uref, (_, Lvar (_, lvar)))) ->
3704 let env, _te, shape_ty = expr ~is_func_arg:true env shape in
3705 let env, shape_ty =
3706 Typing_shapes.remove_key p env shape_ty field in
3707 let env, _ = set_valid_rvalue p env lvar shape_ty in
3708 env, res
3709 | _ ->
3710 Errors.invalid_shape_remove_key (fst shape);
3711 env, res
3713 | _ -> env, res
3715 (* Special function `Shapes::toArray` *)
3716 | Class_const (((), CI((_, shapes), _)) as class_id, ((_, to_array) as method_id))
3717 when shapes = SN.Shapes.cShapes && to_array = SN.Shapes.toArray ->
3718 check_class_function_in_suspend SN.Shapes.cShapes SN.Shapes.toArray;
3719 overload_function p env class_id method_id el uel
3720 begin fun env _ res el -> match el with
3721 | [shape] ->
3722 let env, _te, shape_ty = expr env shape in
3723 Typing_shapes.to_array env shape_ty res
3724 | _ -> env, res
3727 (* Special function `parent::__construct` *)
3728 | Class_const (((), CIparent), ((callee_pos, construct) as id))
3729 when construct = SN.Members.__construct ->
3730 check_class_function_in_suspend "parent" SN.Members.__construct;
3731 Typing_hooks.dispatch_parent_construct_hook env callee_pos;
3732 let env, tel, tuel, ty = call_parent_construct p env el uel in
3733 make_call env (T.make_implicitly_typed_expr fpos
3734 (T.Class_const ((Some ty, T.CIparent), id))) hl tel tuel ty
3736 (* Calling parent method *)
3737 | Class_const (((), CIparent), m) ->
3738 let env, _te, ty1 = static_class_id p env CIparent in
3739 if Env.is_static env
3740 then begin
3741 (* in static context, you can only call parent::foo() on static
3742 * methods *)
3743 let env, fty, _ =
3744 class_get ~is_method:true ~is_const:false ~explicit_tparams:hl env ty1 m CIparent in
3745 let fty = check_abstract_parent_meth (snd m) p fty in
3746 check_coroutine_call env fty;
3747 let env, tel, tuel, ty = call ~expected ~receiver_type:ty1 p env fty el uel in
3748 make_call env (T.make_typed_expr fpos fty
3749 (T.Class_const ((Some ty1, T.CIparent), m))) hl tel tuel ty
3751 else begin
3752 (* in instance context, you can call parent:foo() on static
3753 * methods as well as instance methods *)
3754 if not(class_contains_smethod env ty1 m)
3755 then
3756 (* parent::nonStaticFunc() is really weird. It's calling a method
3757 * defined on the parent class, but $this is still the child class.
3758 * We can deal with this by hijacking the continuation that
3759 * calculates the SN.Typehints.this type *)
3760 let env, this_ty = ExprDepTy.make env CIstatic
3761 (Reason.Rwitness fpos, TUtils.this_of (Env.get_self env)) in
3762 let k_lhs _ = this_ty in
3763 let env, method_, _ =
3764 obj_get_ ~is_method:true ~nullsafe:None ~pos_params:(Some el) ~valkind:`other env ty1
3765 CIparent m
3766 begin fun (env, fty, _) ->
3767 let fty = check_abstract_parent_meth (snd m) p fty in
3768 check_coroutine_call env fty;
3769 let env, _tel, _tuel, method_ = call ~expected
3770 ~receiver_type:ty1 p env fty el uel in
3771 env, method_, None
3773 k_lhs
3775 make_call env (T.make_typed_expr fpos this_ty
3776 (T.Class_const ((Some ty1, T.CIparent), m))) hl [] [] method_
3777 else
3778 let env, fty, _ =
3779 class_get ~is_method:true ~is_const:false ~explicit_tparams:hl env ty1 m CIparent in
3780 let fty = check_abstract_parent_meth (snd m) p fty in
3781 check_coroutine_call env fty;
3782 let env, tel, tuel, ty = call ~expected ~receiver_type:ty1 p env fty el uel in
3783 make_call env (T.make_typed_expr fpos fty
3784 (T.Class_const ((Some ty1, T.CIparent), m))) hl tel tuel ty
3786 (* Call class method *)
3787 | Class_const(((), e1), m) ->
3788 TUtils.process_static_find_ref e1 m;
3789 let env, te1, ty1 = static_class_id p env e1 in
3790 let env, fty, _ =
3791 class_get ~is_method:true ~is_const:false ~explicit_tparams:hl
3792 ~pos_params:el env ty1 m e1 in
3793 let () = match e1 with
3794 | CIself when is_abstract_ft fty ->
3795 (match Env.get_self env with
3796 | _, Tclass ((_, self), _) ->
3797 (* at runtime, self:: in a trait is a call to whatever
3798 * self:: is in the context of the non-trait "use"-ing
3799 * the trait's code *)
3800 (match Env.get_class env self with
3801 | Some { tc_kind = Ast.Ctrait; _ } -> ()
3802 | _ -> Errors.self_abstract_call (snd m) p (Reason.to_pos (fst fty))
3804 | _ -> ())
3805 | CI (c, _) when is_abstract_ft fty ->
3806 Errors.classname_abstract_call (snd c) (snd m) p (Reason.to_pos (fst fty))
3807 | CI ((_, classname), _) ->
3808 (match Typing_heap.Classes.get classname with
3809 | Some class_def ->
3810 let (_, method_name) = m in
3811 (match SMap.get method_name class_def.tc_smethods with
3812 | None -> ()
3813 | Some elt ->
3814 if elt.ce_synthesized then
3815 Errors.static_synthetic_method classname (snd m) p (Reason.to_pos (fst fty)))
3816 | None ->
3817 (* This technically should be an error, but if we throw here we'll break a ton of our
3818 tests since they reference classes that only exist in www, and any missing classes will
3819 get caught elsewhere in the pipeline. *)
3821 | _ -> () in
3822 check_coroutine_call env fty;
3823 let env, tel, tuel, ty = call ~expected ~receiver_type:ty1 p env fty el uel in
3824 make_call env (T.make_typed_expr fpos fty
3825 (T.Class_const(te1, m))) hl tel tuel ty
3827 (* Call instance method *)
3828 | Obj_get(e1, (pos_id, Id m), nullflavor) ->
3829 let is_method = call_type = Cnormal in
3830 let env, te1, ty1 = expr ~accept_using_var:true env e1 in
3831 let nullsafe =
3832 (match nullflavor with
3833 | OG_nullthrows -> None
3834 | OG_nullsafe -> Some p
3835 ) in
3836 let tel = ref [] and tuel = ref [] and tftyl = ref [] in
3837 let fn = (fun (env, fty, _) ->
3838 check_coroutine_call env fty;
3839 let env, tel_, tuel_, method_ = call ~expected ~receiver_type:ty1 p env fty el uel in
3840 tel := tel_; tuel := tuel_;
3841 tftyl := fty :: !tftyl;
3842 env, method_, None) in
3843 let env, ty = obj_get ~is_method ~nullsafe ~pos_params:el
3844 ~explicit_tparams:hl env ty1 (CIexpr e1) m fn in
3845 let tfty =
3846 match !tftyl with
3847 | [fty] -> fty
3848 | tftyl -> (Reason.none, Tunresolved tftyl)
3850 make_call env (T.make_typed_expr fpos tfty (T.Obj_get(te1,
3851 T.make_implicitly_typed_expr pos_id (T.Id m), nullflavor))) hl !tel !tuel ty
3853 (* Function invocation *)
3854 | Fun_id x ->
3855 Typing_hooks.dispatch_id_hook x env;
3856 let env, fty = fun_type_of_id env x hl in
3857 check_coroutine_call env fty;
3858 let env, tel, tuel, ty = call ~expected p env fty el uel in
3859 make_call env (T.make_typed_expr fpos fty (T.Fun_id x)) hl tel tuel ty
3860 | Id x ->
3861 Typing_hooks.dispatch_id_hook x env;
3862 let env, fty = fun_type_of_id env x hl in
3863 check_coroutine_call env fty;
3864 let env, tel, tuel, ty = call ~expected p env fty el uel in
3865 make_call env (T.make_typed_expr fpos fty (T.Id x)) hl tel tuel ty
3866 | _ ->
3867 let env, te, fty = expr env e in
3868 check_coroutine_call env fty;
3869 let env, tel, tuel, ty = call ~expected p env fty el uel in
3870 make_call env te hl tel tuel ty
3872 and fun_type_of_id env x hl =
3873 Typing_hooks.dispatch_fun_id_hook x;
3874 let env, fty =
3875 match Env.get_fun env (snd x) with
3876 | None -> let env, _, ty = unbound_name env x in env, ty
3877 | Some fty ->
3878 let ety_env = Phase.env_with_self env in
3879 let env, fty = Phase.localize_ft ~use_pos:(fst x) ~explicit_tparams:hl ~ety_env env fty in
3880 env, (Reason.Rwitness fty.ft_pos, Tfun fty)
3882 env, fty
3884 (*****************************************************************************)
3885 (* Function type-checking expressions accessing an array (example: $x[...]).
3886 * The parameter is_lvalue is true when the expression is on the left hand
3887 * side of an assignment (example: $x[...] = 0).
3889 (*****************************************************************************)
3890 and array_get ?(lhs_of_null_coalesce=false) is_lvalue p env ty1 e2 ty2 =
3891 (* This is a little weird -- we enforce the right arity when you use certain
3892 * collections, even in partial mode (where normally completely omitting the
3893 * type parameter list is admitted). Basically the "omit type parameter"
3894 * hole was for compatibility with certain interfaces like ArrayAccess, not
3895 * for collections! But it's hard to go back on now, so since we've always
3896 * errored (with an inscrutable error message) when you try to actually use
3897 * a collection with omitted type parameters, we can continue to error and
3898 * give a more useful error message. *)
3899 let env, ety1 = Env.expand_type env ty1 in
3900 let arity_error (_, name) =
3901 Errors.array_get_arity p name (Reason.to_pos (fst ety1)) in
3902 match snd ety1 with
3903 | Tunresolved tyl ->
3904 let env, tyl = List.map_env env tyl begin fun env ty1 ->
3905 array_get ~lhs_of_null_coalesce is_lvalue p env ty1 e2 ty2
3906 end in
3907 env, (fst ety1, Tunresolved tyl)
3908 | Tarraykind (AKvarray ty | AKvec ty) ->
3909 let ty1 = Reason.Ridx (fst e2, fst ety1), Tprim Tint in
3910 let env = Type.sub_type p Reason.index_array env ty2 ty1 in
3911 env, ty
3912 | Tarraykind (AKvarray_or_darray ty) ->
3913 let ty1 = Reason.Rvarray_or_darray_key p, Tprim Tarraykey in
3914 let env = Type.sub_type p Reason.index_array env ty2 ty1 in
3915 env, ty
3916 | Tclass ((_, cn) as id, argl)
3917 when cn = SN.Collections.cVector
3918 || cn = SN.Collections.cVec ->
3919 let ty = match argl with
3920 | [ty] -> ty
3921 | _ -> arity_error id; err_witness env p in
3922 let ty1 = Reason.Ridx_vector (fst e2), Tprim Tint in
3923 let env = Type.sub_type p (Reason.index_class cn) env ty2 ty1 in
3924 env, ty
3925 | Tclass ((_, cn) as id, argl)
3926 when cn = SN.Collections.cMap
3927 || cn = SN.Collections.cStableMap
3928 || cn = SN.Collections.cDict
3929 || cn = SN.Collections.cKeyset ->
3930 if cn = SN.Collections.cKeyset && is_lvalue then begin
3931 Errors.keyset_set p (Reason.to_pos (fst ety1));
3932 env, (Reason.Rwitness p, Typing_utils.terr env)
3933 end else
3934 let (k, v) = match argl with
3935 | [t] when cn = SN.Collections.cKeyset -> (t, t)
3936 | [k; v] when cn <> SN.Collections.cKeyset -> (k, v)
3937 | _ ->
3938 arity_error id;
3939 let any = err_witness env p in
3940 any, any
3942 let env, ty2 = TUtils.unresolved env ty2 in
3943 let env = Type.sub_type p (Reason.index_class cn) env ty2 k in
3944 env, v
3945 (* Certain container/collection types are intended to be immutable/const,
3946 * thus they should never appear as a lvalue when indexing i.e.
3948 * $x[0] = 100; // ERROR
3949 * $x[0]; // OK
3951 | Tclass ((_, cn) as id, argl)
3952 when cn = SN.Collections.cConstMap
3953 || cn = SN.Collections.cImmMap
3954 || cn = SN.Collections.cIndexish
3955 || cn = SN.Collections.cKeyedContainer ->
3956 if is_lvalue then
3957 error_const_mutation env p ety1
3958 else
3959 let (k, v) = match argl with
3960 | [k; v] -> (k, v)
3961 | _ ->
3962 arity_error id;
3963 let any = err_witness env p in
3964 any, any
3966 let env = Type.sub_type p (Reason.index_class cn) env ty2 k in
3967 env, v
3968 | Tclass ((_, cn) as id, argl)
3969 when not is_lvalue &&
3970 (cn = SN.Collections.cConstVector || cn = SN.Collections.cImmVector) ->
3971 let ty = match argl with
3972 | [ty] -> ty
3973 | _ -> arity_error id; err_witness env p in
3974 let ty1 = Reason.Ridx (fst e2, fst ety1), Tprim Tint in
3975 let env = Type.sub_type p (Reason.index_class cn) env ty2 ty1 in
3976 env, ty
3977 | Tclass ((_, cn), _)
3978 when is_lvalue &&
3979 (cn = SN.Collections.cConstVector || cn = SN.Collections.cImmVector) ->
3980 error_const_mutation env p ety1
3981 | Tarraykind (AKdarray (k, v) | AKmap (k, v)) ->
3982 let env, ty2 = TUtils.unresolved env ty2 in
3983 let env = Type.sub_type p Reason.index_array env ty2 k in
3984 env, v
3985 | Tarraykind ((AKshape _ | AKtuple _) as akind) ->
3986 let key = Typing_arrays.static_array_access env (Some e2) in
3987 let env, result = match key, akind with
3988 | Typing_arrays.AKtuple_index index, AKtuple fields ->
3989 begin match IMap.get index fields with
3990 | Some ty ->
3991 let ty1 = Reason.Ridx (fst e2, fst ety1), Tprim Tint in
3992 let env = Type.sub_type p Reason.index_array env ty2 ty1 in
3993 env, Some ty
3994 | None -> env, None
3996 | Typing_arrays.AKshape_key field_name, AKshape fdm ->
3997 begin match Nast.ShapeMap.get field_name fdm with
3998 | Some (k, v) ->
3999 let env, ty2 = TUtils.unresolved env ty2 in
4000 let env = Type.sub_type p Reason.index_array env ty2 k in
4001 env, Some v
4002 | None -> env, None
4004 | _ -> env, None in
4005 begin match result with
4006 | Some ty -> env, ty
4007 | None ->
4008 (* Key is dynamic, or static and not in the array - treat it as
4009 regular map or vec like array *)
4010 let env, ty1 = Typing_arrays.downcast_aktypes env ety1 in
4011 array_get is_lvalue p env ty1 e2 ty2
4013 | Terr -> env, (Reason.Rwitness p, Typing_utils.terr env)
4014 | Tdynamic -> env, ety1
4015 | Tany | Tarraykind (AKany | AKempty) ->
4016 env, (Reason.Rnone, Typing_utils.tany env)
4017 | Tprim Tstring ->
4018 let ty = Reason.Rwitness p, Tprim Tstring in
4019 let int = Reason.Ridx (fst e2, fst ety1), Tprim Tint in
4020 let env = Type.sub_type p Reason.index_array env ty2 int in
4021 env, ty
4022 | Ttuple tyl ->
4023 (match e2 with
4024 | p, Int n ->
4025 (try
4026 let idx = int_of_string (snd n) in
4027 let nth = List.nth_exn tyl idx in
4028 env, nth
4029 with _ ->
4030 Errors.typing_error p (Reason.string_of_ureason Reason.index_tuple);
4031 env, (Reason.Rwitness p, Typing_utils.terr env)
4033 | p, _ ->
4034 Errors.typing_error p (Reason.string_of_ureason Reason.URtuple_access);
4035 env, (Reason.Rwitness p, Typing_utils.terr env)
4037 | Tclass ((_, cn) as id, argl) when cn = SN.Collections.cPair ->
4038 let (ty1, ty2) = match argl with
4039 | [ty1; ty2] -> (ty1, ty2)
4040 | _ ->
4041 arity_error id;
4042 let any = err_witness env p in
4043 any, any
4045 (match e2 with
4046 | p, Int n ->
4047 (try
4048 let idx = int_of_string (snd n) in
4049 let nth = List.nth_exn [ty1; ty2] idx in
4050 env, nth
4051 with _ ->
4052 Errors.typing_error p @@
4053 Reason.string_of_ureason (Reason.index_class cn);
4054 env, (Reason.Rwitness p, Typing_utils.terr env)
4056 | p, _ ->
4057 Errors.typing_error p (Reason.string_of_ureason Reason.URpair_access);
4058 env, (Reason.Rwitness p, Typing_utils.terr env)
4060 | Tshape (_, fdm) ->
4061 let p, e2' = e2 in
4062 (match TUtils.shape_field_name env p e2' with
4063 | None ->
4064 (* there was already an error in shape_field name,
4065 don't report another one for a missing field *)
4066 env, (Reason.Rwitness p, Typing_utils.terr env)
4067 | Some field -> (match ShapeMap.get field fdm with
4068 | None ->
4069 Errors.undefined_field
4070 ~use_pos:p
4071 ~name:(TUtils.get_printable_shape_field_name field)
4072 ~shape_type_pos:(Reason.to_pos (fst ety1));
4073 env, (Reason.Rwitness p, Typing_utils.terr env)
4074 | Some { sft_optional = true; _ }
4075 when not is_lvalue && not lhs_of_null_coalesce ->
4076 let declared_field =
4077 List.find_exn
4078 ~f:(fun x -> Ast.ShapeField.compare field x = 0)
4079 (ShapeMap.keys fdm) in
4080 let declaration_pos = match declared_field with
4081 | Ast.SFlit (p, _) | Ast.SFclass_const ((p, _), _) -> p in
4082 Errors.array_get_with_optional_field
4084 declaration_pos
4085 (TUtils.get_printable_shape_field_name field);
4086 env, (Reason.Rwitness p, Typing_utils.terr env)
4087 | Some { sft_optional = _; sft_ty } -> env, sft_ty)
4089 | Toption tyl when lhs_of_null_coalesce ->
4090 (* Normally, we would not allow indexing into a nullable container,
4091 however, because the pattern shows up so frequently, we are allowing
4092 indexing into a nullable container as long as it is on the lhs of a
4093 null coalesce *)
4094 array_get ~lhs_of_null_coalesce is_lvalue p env tyl e2 ty2
4095 | Toption _ ->
4096 Errors.null_container p
4097 (Reason.to_string
4098 "This is what makes me believe it can be null"
4099 (fst ety1)
4101 env, (Reason.Rwitness p, Typing_utils.terr env)
4102 | Tobject ->
4103 if Env.is_strict env
4104 then error_array env p ety1
4105 else env, (Reason.Rwitness p, Typing_utils.tany env)
4106 | Tabstract (AKnewtype (ts, [ty]), Some (r, Tshape (fk, fields)))
4107 when ts = SN.FB.cTypeStructure ->
4108 let env, fields = TS.transform_shapemap env ty fields in
4109 let ty = r, Tshape (fk, fields) in
4110 array_get ~lhs_of_null_coalesce is_lvalue p env ty e2 ty2
4111 | Tabstract _ ->
4112 let resl =
4113 try_over_concrete_supertypes env ety1
4114 begin fun env ty ->
4115 array_get ~lhs_of_null_coalesce is_lvalue p env ty e2 ty2
4116 end in
4117 begin match resl with
4118 | [res] -> res
4119 | res::rest
4120 when List.for_all rest ~f:(fun x -> ty_equal (snd x) (snd res)) -> res
4121 | _ -> error_array env p ety1
4123 | Tmixed | Tnonnull | Tprim _ | Tvar _ | Tfun _
4124 | Tclass (_, _) | Tanon (_, _) ->
4125 error_array env p ety1
4127 and array_append p env ty1 =
4128 let env, ty1 = TUtils.fold_unresolved env ty1 in
4129 let resl = try_over_concrete_supertypes env ty1
4130 begin fun env ty ->
4131 match ty with
4132 | (r, ty_) ->
4133 match ty_ with
4134 | Tany | Tarraykind (AKany | AKempty) ->
4135 env, (r, Typing_utils.tany env)
4137 | Terr ->
4138 env, (r, Typing_utils.terr env)
4139 (* No reactive append on vector and set *)
4140 | Tclass ((_, n), [ty])
4141 when (n = SN.Collections.cVector
4142 || n = SN.Collections.cSet) ->
4143 Env.error_if_reactive_context env @@ begin fun () ->
4144 Errors.nonreactive_append p;
4145 end;
4146 env, ty
4148 | Tclass ((_, n), [ty])
4149 when n = SN.Collections.cVec || n = SN.Collections.cKeyset ->
4150 env, ty
4151 | Tclass ((_, n), [])
4152 when n = SN.Collections.cVector || n = SN.Collections.cSet ->
4153 (* Handle the case where "Vector" or "Set" was used as a typehint
4154 without type parameters *)
4155 env, (r, Typing_utils.tany env)
4156 | Tclass ((_, n), [tkey; tvalue]) when n = SN.Collections.cMap ->
4157 Env.error_if_reactive_context env @@ begin fun () ->
4158 Errors.nonreactive_append p;
4159 end;
4160 (* You can append a pair to a map *)
4161 env, (Reason.Rmap_append p, Tclass ((p, SN.Collections.cPair),
4162 [tkey; tvalue]))
4163 | Tclass ((_, n), []) when n = SN.Collections.cMap ->
4164 Env.error_if_reactive_context env @@ begin fun () ->
4165 Errors.nonreactive_append p;
4166 end;
4167 (* Handle the case where "Map" was used as a typehint without
4168 type parameters *)
4169 env, (Reason.Rmap_append p,
4170 Tclass ((p, SN.Collections.cPair), []))
4171 | Tarraykind (AKvec ty | AKvarray ty) ->
4172 env, ty
4173 | Tdynamic -> env, ty
4174 | Tobject ->
4175 if Env.is_strict env
4176 then error_array_append env p ty1
4177 else env, (Reason.Rwitness p, Typing_utils.tany env)
4178 | Tmixed | Tnonnull | Tarraykind _ | Toption _ | Tprim _
4179 | Tvar _ | Tfun _ | Tclass (_, _) | Ttuple _
4180 | Tanon (_, _) | Tunresolved _ | Tshape _ | Tabstract _ ->
4181 error_array_append env p ty1
4182 end in
4183 match resl with
4184 | [res] -> res
4185 | _ -> error_array_append env p ty1
4188 and error_array env p (r, ty) =
4189 Errors.array_access p (Reason.to_pos r) (Typing_print.error ty);
4190 env, err_witness env p
4192 and error_array_append env p (r, ty) =
4193 Errors.array_append p (Reason.to_pos r) (Typing_print.error ty);
4194 env, err_witness env p
4196 and error_const_mutation env p (r, ty) =
4197 Errors.const_mutation p (Reason.to_pos r) (Typing_print.error ty);
4198 env, err_witness env p
4201 * Checks if a class (given by cty) contains a given static method.
4203 * We could refactor this + class_get
4205 and class_contains_smethod env cty (_pos, mid) =
4206 let lookup_member ty =
4207 match ty with
4208 | _, Tclass ((_, c), _) ->
4209 (match Env.get_class env c with
4210 | None -> false
4211 | Some class_ ->
4212 Option.is_some @@ Env.get_static_member true env class_ mid
4214 | _ -> false in
4215 let _env, tyl = TUtils.get_concrete_supertypes env cty in
4216 List.exists tyl ~f:lookup_member
4218 and class_get ~is_method ~is_const ?(explicit_tparams=[]) ?(incl_tc=false)
4219 ?(pos_params : expr list option) env cty (p, mid) cid =
4220 let env, this_ty =
4221 if is_method then
4222 this_for_method env cid cty
4223 else
4224 env, cty in
4225 let ety_env = {
4226 type_expansions = [];
4227 this_ty = this_ty;
4228 substs = SMap.empty;
4229 from_class = Some cid;
4230 } in
4231 class_get_ ~is_method ~is_const ~ety_env ~explicit_tparams ~incl_tc
4232 ~pos_params env cid cty (p, mid)
4234 and class_get_ ~is_method ~is_const ~ety_env ?(explicit_tparams=[])
4235 ?(incl_tc=false) ~pos_params env cid cty
4236 (p, mid) =
4237 let env, cty = Env.expand_type env cty in
4238 match cty with
4239 | r, Tany -> env, (r, Typing_utils.tany env), None
4240 | r, Terr -> env, err_witness env (Reason.to_pos r), None
4241 | _, Tdynamic -> env, cty, None
4242 | _, Tunresolved tyl ->
4243 let env, tyl = List.map_env env tyl begin fun env ty ->
4244 let env, ty, _ =
4245 class_get_ ~is_method ~is_const ~ety_env ~explicit_tparams ~incl_tc
4246 ~pos_params env cid ty (p, mid)
4247 in env, ty
4248 end in
4249 let env, method_ = TUtils.in_var env (fst cty, Tunresolved tyl) in
4250 env, method_, None
4252 | _, Tabstract _ ->
4253 begin match TUtils.get_concrete_supertypes env cty with
4254 | env, [cty] ->
4255 class_get_ ~is_method ~is_const ~ety_env ~explicit_tparams ~incl_tc
4256 ~pos_params env cid cty (p, mid)
4257 | env, _ ->
4258 env, (Reason.Rwitness p, Typing_utils.tany env), None
4260 | _, Tclass ((_, c), paraml) ->
4261 let class_ = Env.get_class env c in
4262 (match class_ with
4263 | None -> env, (Reason.Rwitness p, Typing_utils.tany env), None
4264 | Some class_ ->
4265 Typing_hooks.dispatch_smethod_hook class_ paraml ~pos_params (p, mid)
4266 env ety_env.from_class ~is_method ~is_const;
4267 (* We need to instantiate generic parameters in the method signature *)
4268 let ety_env =
4269 { ety_env with
4270 substs = Subst.make class_.tc_tparams paraml } in
4271 if is_const then begin
4272 let const =
4273 if incl_tc then Env.get_const env class_ mid else
4274 match Env.get_typeconst env class_ mid with
4275 | Some _ ->
4276 Errors.illegal_typeconst_direct_access p;
4277 None
4278 | None ->
4279 Env.get_const env class_ mid
4281 match const with
4282 | None ->
4283 smember_not_found p ~is_const ~is_method class_ mid;
4284 env, (Reason.Rnone, Typing_utils.terr env), None
4285 | Some { cc_type; cc_abstract; cc_pos; _ } ->
4286 let env, cc_type = Phase.localize ~ety_env env cc_type in
4287 env, cc_type,
4288 (if cc_abstract
4289 then Some (cc_pos, class_.tc_name ^ "::" ^ mid)
4290 else None)
4291 end else begin
4292 let smethod = Env.get_static_member is_method env class_ mid in
4293 match smethod with
4294 | None ->
4295 (match Env.get_static_member is_method env class_
4296 SN.Members.__callStatic with
4297 | None ->
4298 smember_not_found p ~is_const ~is_method class_ mid;
4299 env, (Reason.Rnone, Typing_utils.terr env), None
4300 | Some {ce_visibility = vis; ce_type = lazy (r, Tfun ft); _} ->
4301 let p_vis = Reason.to_pos r in
4302 TVis.check_class_access p env (p_vis, vis) cid class_;
4303 let env, ft =
4304 Phase.localize_ft ~use_pos:p ~ety_env ~explicit_tparams:explicit_tparams env ft in
4305 let ft = { ft with
4306 ft_arity = Fellipsis 0;
4307 ft_tparams = []; ft_params = [];
4308 } in
4309 env, (r, Tfun ft), None
4310 | _ -> assert false)
4311 | Some { ce_visibility = vis; ce_type = lazy method_; _ } ->
4312 let p_vis = Reason.to_pos (fst method_) in
4313 TVis.check_class_access p env (p_vis, vis) cid class_;
4314 let env, method_ =
4315 begin match method_ with
4316 (* We special case Tfun here to allow passing in explicit tparams to localize_ft. *)
4317 | r, Tfun ft ->
4318 let env, ft =
4319 Phase.localize_ft ~use_pos:p ~ety_env ~explicit_tparams:explicit_tparams env ft
4320 in env, (r, Tfun ft)
4321 | _ -> Phase.localize ~ety_env env method_
4322 end in
4323 env, method_, None
4326 | _, (Tmixed | Tnonnull | Tarraykind _ | Toption _
4327 | Tprim _ | Tvar _ | Tfun _ | Ttuple _ | Tanon (_, _) | Tobject
4328 | Tshape _) ->
4329 (* should never happen; static_class_id takes care of these *)
4330 env, (Reason.Rnone, Typing_utils.tany env), None
4332 and smember_not_found pos ~is_const ~is_method class_ member_name =
4333 let kind =
4334 if is_const then `class_constant
4335 else if is_method then `static_method
4336 else `class_variable in
4337 let error hint =
4338 let cid = (class_.tc_pos, class_.tc_name) in
4339 Errors.smember_not_found kind pos cid member_name hint
4341 match Env.suggest_static_member is_method class_ member_name with
4342 | None ->
4343 (match Env.suggest_member is_method class_ member_name with
4344 | None when not class_.tc_members_fully_known ->
4345 (* no error in this case ... the member might be present
4346 * in one of the parents of class_ that the typing cannot see *)
4348 | None ->
4349 error `no_hint
4350 | Some (pos2, v) ->
4351 error (`closest (pos2, v))
4353 | Some (pos2, v) ->
4354 error (`did_you_mean (pos2, v))
4356 and member_not_found pos ~is_method class_ member_name r =
4357 let kind = if is_method then `method_ else `member in
4358 let cid = class_.tc_pos, class_.tc_name in
4359 let reason = Reason.to_string
4360 ("This is why I think it is an object of type "^strip_ns class_.tc_name) r
4362 let error hint =
4363 Errors.member_not_found kind pos cid member_name hint reason in
4364 match Env.suggest_member is_method class_ member_name with
4365 | None ->
4366 (match Env.suggest_static_member is_method class_ member_name with
4367 | None when not class_.tc_members_fully_known ->
4368 (* no error in this case ... the member might be present
4369 * in one of the parents of class_ that the typing cannot see *)
4371 | None ->
4372 error `no_hint
4373 | Some (def_pos, v) ->
4374 error (`closest (def_pos, v))
4376 | Some (def_pos, v) ->
4377 error (`did_you_mean (def_pos, v))
4379 and obj_get ~is_method ~nullsafe ?(valkind = `other) ?(explicit_tparams=[])
4380 ?(pos_params: expr list option) env ty1 cid id k =
4381 let env =
4382 match nullsafe with
4383 | Some p when not (type_could_be_null env ty1) ->
4384 let env, (r, _) = Env.expand_type env ty1 in
4385 Errors.nullsafe_not_needed p
4386 (Reason.to_string
4387 "This is what makes me believe it cannot be null" r);
4389 | _ -> env in
4390 let env, method_, _ =
4391 obj_get_with_visibility ~is_method ~nullsafe ~valkind ~pos_params
4392 ~explicit_tparams env ty1 cid id k in
4393 env, method_
4395 and obj_get_with_visibility ~is_method ~nullsafe ~valkind ~pos_params
4396 ?(explicit_tparams=[]) env ty1 cid id k =
4397 obj_get_ ~is_method ~nullsafe ~valkind ~pos_params ~explicit_tparams env ty1
4398 cid id k (fun ty -> ty)
4400 (* We know that the receiver is a concrete class: not a generic with
4401 * bounds, or a Tunresolved. *)
4402 and obj_get_concrete_ty ~is_method ~valkind ~pos_params ?(explicit_tparams=[])
4403 env concrete_ty class_id (id_pos, id_str as id) k_lhs =
4404 let default () = env, (Reason.Rwitness id_pos, Typing_utils.tany env), None in
4405 match concrete_ty with
4406 | (r, Tclass(x, paraml)) ->
4407 begin
4408 match Env.get_class env (snd x) with
4409 | None ->
4410 default ()
4412 | Some class_info when not is_method
4413 && not (Env.is_strict env)
4414 && class_info.tc_name = SN.Classes.cStdClass ->
4415 default ()
4417 | Some class_info ->
4418 let paraml =
4419 if List.length paraml = 0
4420 then List.map class_info.tc_tparams
4421 (fun _ -> Reason.Rwitness id_pos, Typing_utils.tany env)
4422 else paraml in
4423 let member_info = Env.get_member is_method env class_info id_str in
4424 Typing_hooks.dispatch_cmethod_hook class_info paraml ~pos_params id
4425 env (Some class_id) ~is_method;
4427 match member_info with
4428 | None when not is_method ->
4429 if not (SN.Members.is_special_xhp_attribute id_str)
4430 then member_not_found id_pos ~is_method class_info id_str r;
4431 default ()
4433 | None ->
4434 begin
4435 match Env.get_member is_method env class_info SN.Members.__call with
4436 | None ->
4437 member_not_found id_pos ~is_method class_info id_str r;
4438 default ()
4440 | Some {ce_visibility = vis; ce_type = lazy (r, Tfun ft); _} ->
4441 let mem_pos = Reason.to_pos r in
4442 TVis.check_obj_access id_pos env (mem_pos, vis);
4444 (* the return type of __call can depend on the
4445 * class params or be this *)
4446 let this_ty = k_lhs (r, (Tclass(x, paraml))) in
4447 let ety_env = {
4448 type_expansions = [];
4449 this_ty = this_ty;
4450 substs = Subst.make class_info.tc_tparams paraml;
4451 from_class = Some class_id;
4452 } in
4453 let env, ft = Phase.localize_ft ~use_pos:id_pos ~ety_env env ft in
4455 (* we change the params of the underlying
4456 * declaration to act as a variadic function
4457 * ... this transform cannot be done when
4458 * processing the declaration of call because
4459 * direct calls to $inst->__call are also
4460 * valid. *)
4461 let ft = {ft with
4462 ft_arity = Fellipsis 0; ft_tparams = []; ft_params = []; } in
4464 let member_ty = (r, Tfun ft) in
4465 env, member_ty, Some (mem_pos, vis)
4467 | _ -> assert false
4470 | Some ({ce_visibility = vis; ce_type = lazy member_; ce_const; _ } as member_ce) ->
4471 let mem_pos = Reason.to_pos (fst member_) in
4472 TVis.check_obj_access id_pos env (mem_pos, vis);
4473 let member_ty = Typing_enum.member_type env member_ce in
4474 let this_ty = k_lhs (r, (Tclass(x, paraml))) in
4475 let ety_env = {
4476 type_expansions = [];
4477 this_ty = this_ty;
4478 substs = Subst.make class_info.tc_tparams paraml;
4479 from_class = Some class_id;
4480 } in
4481 let env, member_ty =
4482 begin match member_ty with
4483 | (r, Tfun ft) ->
4484 (* We special case function types here to be able to pass explicit type parameters. *)
4485 let (env, ft) = Phase.localize_ft ~use_pos:id_pos ~explicit_tparams ~ety_env env ft in
4486 (env, (r, Tfun ft))
4487 | _ -> Phase.localize ~ety_env env member_ty
4488 end in
4490 if ce_const && valkind = `lvalue then
4491 if not (env.Env.inside_constructor &&
4492 (* expensive call behind short circuiting && *)
4493 SubType.is_sub_type env (Env.get_self env) concrete_ty) then
4494 Errors.assigning_to_const id_pos;
4496 env, member_ty, Some (mem_pos, vis)
4498 | _, Tdynamic ->
4499 let ty = Reason.Rdynamic_prop id_pos, Tdynamic in
4500 env, ty, None
4501 | _, Tobject
4502 | _, Tany
4503 | _, Terr ->
4504 default ()
4506 | _ ->
4507 Errors.non_object_member
4508 id_str id_pos (Typing_print.error (snd concrete_ty))
4509 (Reason.to_pos (fst concrete_ty));
4510 default ()
4513 (* k_lhs takes the type of the object receiver *)
4514 and obj_get_ ~is_method ~nullsafe ~valkind ~(pos_params : expr list option) ?(explicit_tparams=[])
4515 env ty1 cid (id_pos, id_str as id) k k_lhs =
4516 let env, ety1 = Env.expand_type env ty1 in
4517 match ety1 with
4518 | _, Tunresolved tyl ->
4519 let (env, vis), tyl = List.map_env (env, None) tyl
4520 begin fun (env, vis) ty ->
4521 let env, ty, vis' =
4522 obj_get_ ~is_method ~nullsafe ~valkind ~pos_params
4523 ~explicit_tparams env ty cid id k k_lhs in
4524 (* There is one special case where we need to expose the
4525 * visibility outside of obj_get (checkout inst_meth special
4526 * function).
4527 * We keep a witness of the "most restrictive" visibility
4528 * we encountered (position + visibility), to be able to
4529 * special case inst_meth.
4531 let vis = TVis.min_vis_opt vis vis' in
4532 (env, vis), ty
4533 end in
4534 let env, method_ = TUtils.in_var env (fst ety1, Tunresolved (tyl)) in
4535 env, method_, vis
4537 | p', (Tabstract(ak, Some ty)) ->
4538 let k_lhs' ty = match ak with
4539 | AKnewtype (_, _) -> k_lhs ty
4540 | _ -> k_lhs (p', Tabstract (ak, Some ty)) in
4541 obj_get_ ~is_method ~nullsafe ~valkind ~pos_params ~explicit_tparams env ty cid id k k_lhs'
4543 | p', (Tabstract(ak,_)) ->
4544 let resl =
4545 try_over_concrete_supertypes env ety1
4546 (fun env ty ->
4547 (* We probably don't want to rewrap new types for the 'this' closure *)
4548 (* TODO AKENN: we shouldn't refine constraints by changing
4549 * the type like this *)
4550 let k_lhs' ty = match ak with
4551 | AKnewtype (_, _) -> k_lhs ty
4552 | _ -> k_lhs (p', Tabstract (ak, Some ty)) in
4553 obj_get_concrete_ty ~is_method ~valkind ~pos_params ~explicit_tparams env ty cid id k_lhs'
4554 ) in
4555 begin match resl with
4556 | [] -> begin
4557 Errors.non_object_member
4558 id_str id_pos (Typing_print.error (snd ety1))
4559 (Reason.to_pos (fst ety1));
4560 k (env, err_witness env id_pos, None)
4562 | ((_env, (_, ty), _vis) as res)::rest ->
4563 if List.exists rest (fun (_, (_,ty'), _) -> ty' <> ty)
4564 then
4565 begin
4566 Errors.ambiguous_member
4567 id_str id_pos (Typing_print.error (snd ety1))
4568 (Reason.to_pos (fst ety1));
4569 k (env, err_witness env id_pos, None)
4571 else k res
4574 | _, Toption ty -> begin match nullsafe with
4575 | Some p1 ->
4576 let env, method_, x = obj_get_ ~is_method ~nullsafe ~valkind
4577 ~pos_params ~explicit_tparams env ty cid id k k_lhs in
4578 let env, method_ = TUtils.non_null env method_ in
4579 env, (Reason.Rnullsafe_op p1, Toption method_), x
4580 | None ->
4581 Errors.null_member id_str id_pos
4582 (Reason.to_string
4583 "This is what makes me believe it can be null"
4584 (fst ety1)
4586 k (env, (fst ety1, Typing_utils.terr env), None)
4588 | _, _ ->
4589 k (obj_get_concrete_ty ~is_method ~valkind ~pos_params ~explicit_tparams env ety1 cid id k_lhs)
4592 (* Return true if the type ty1 contains the null value *)
4593 and type_could_be_null env ty1 =
4594 let _, tyl = TUtils.get_concrete_supertypes env ty1 in
4595 List.exists tyl
4596 (fun ety ->
4597 match snd ety with
4598 Toption _ | Tunresolved _ | Tmixed | Tany | Terr | Tdynamic -> true
4599 | Tarraykind _ | Tprim _ | Tvar _ | Tfun _ | Tabstract _
4600 | Tclass (_, _) | Ttuple _ | Tanon (_, _) | Tobject
4601 | Tshape _ | Tnonnull -> false)
4603 and class_id_for_new p env cid =
4604 let env, te, ty = static_class_id p env cid in
4605 (* Need to deal with union case *)
4606 let rec get_info res tyl =
4607 match tyl with
4608 | [] -> env, te, res
4609 | ty::tyl ->
4610 match snd ty with
4611 | Tunresolved tyl' ->
4612 get_info res (tyl' @ tyl)
4613 | _ ->
4614 (* Instantiation on an abstract class (e.g. from classname<T>) is
4615 * via the base type (to check constructor args), but the actual
4616 * type `ty` must be preserved. *)
4617 match TUtils.get_base_type env ty with
4618 | _, Tclass (sid, _) ->
4619 begin
4620 let class_ = Env.get_class env (snd sid) in
4621 match class_ with
4622 | None -> get_info res tyl
4623 | Some class_info -> get_info ((sid, class_info, ty)::res) tyl
4625 | _, (Tany | Terr | Tmixed | Tnonnull | Tarraykind _ | Toption _
4626 | Tprim _ | Tvar _ | Tfun _ | Tabstract (_, _) | Ttuple _
4627 | Tanon (_, _) | Tunresolved _ | Tobject | Tshape _ | Tdynamic ) ->
4628 get_info res tyl in
4629 get_info [] [ty]
4631 (* To be a valid trait declaration, all of its 'require extends' must
4632 * match; since there's no multiple inheritance, it follows that all of
4633 * the 'require extends' must belong to the same inheritance hierarchy
4634 * and one of them should be the child of all the others *)
4635 and trait_most_concrete_req_class trait env =
4636 List.fold_left trait.tc_req_ancestors ~f:begin fun acc (_p, ty) ->
4637 let _r, (_p, name), _paraml = TUtils.unwrap_class_type ty in
4638 let keep = match acc with
4639 | Some (c, _ty) -> SMap.mem name c.tc_ancestors
4640 | None -> false
4642 if keep then acc
4643 else
4644 let class_ = Env.get_class env name in
4645 (match class_ with
4646 | None
4647 | Some { tc_kind = Ast.Cinterface; _ } -> acc
4648 | Some { tc_kind = Ast.Ctrait; _ } ->
4649 (* this is an error case for which the nastCheck spits out
4650 * an error, but does *not* currently remove the offending
4651 * 'require extends' or 'require implements' *)
4653 | Some c -> Some (c, ty)
4655 end ~init:None
4657 (* For explicit type arguments we support a wildcard syntax `_` for which
4658 * Hack will generate a fresh type variable
4660 and type_argument env hint =
4661 match hint with
4662 | (_, Happly((_, id), [])) when id = SN.Typehints.wildcard ->
4663 Env.fresh_unresolved_type env
4664 | _ ->
4665 Phase.hint_locl env hint
4667 (* If there are no explicit type arguments then generate fresh type variables
4668 * for all of them. Otherwise, check the arity, and use the explicit types *)
4669 and type_arguments env p class_name tparams hintl =
4670 let default () = List.map_env env tparams begin fun env _ ->
4671 Env.fresh_unresolved_type env end in
4672 if hintl = []
4673 then default ()
4674 else if List.length hintl != List.length tparams
4675 then begin
4676 Errors.type_arity p class_name (string_of_int (List.length tparams));
4677 default ()
4679 else
4680 List.map_env env hintl type_argument
4682 (* When invoking a method the class_id is used to determine what class we
4683 * lookup the method in, but the type of 'this' will be the late bound type.
4684 * For example:
4686 * class C {
4687 * public static function get(): this { return new static(); }
4689 * public static function alias(): this { return self::get(); }
4692 * In C::alias, when we invoke self::get(), 'self' is resolved to the class
4693 * in the lexical scope (C), so call C::get. However the method is executed in
4694 * the current context, so static inside C::get will be resolved to the late
4695 * bound type (get_called_class() within C::alias).
4697 * This means when determining the type of this, CIparent and CIself should be
4698 * changed to CIstatic. For the other cases of C::get() or $c::get(), we only
4699 * look at the left hand side of the '::' and use the type type associated
4700 * with it.
4702 * Thus C::get() will return a type C, while $c::get() will return the same
4703 * type as $c.
4705 and this_for_method env cid default_ty = match cid with
4706 | CIparent | CIself | CIstatic ->
4707 let p = Reason.to_pos (fst default_ty) in
4708 let env, _te, ty = static_class_id p env CIstatic in
4709 ExprDepTy.make env CIstatic ty
4710 | _ ->
4711 env, default_ty
4713 and static_class_id p env =
4714 let make_result env te ty =
4715 env, (Some ty, te), ty in
4716 function
4717 | CIparent ->
4718 (match Env.get_self env with
4719 | _, Tclass ((_, self), _) ->
4720 (match Env.get_class env self with
4721 | Some (
4722 {tc_kind = Ast.Ctrait; _}
4723 as trait) ->
4724 (match trait_most_concrete_req_class trait env with
4725 | None ->
4726 Errors.parent_in_trait p;
4727 make_result env T.CIparent (Reason.Rwitness p, Typing_utils.terr env)
4728 | Some (_, parent_ty) ->
4729 (* inside a trait, parent is SN.Typehints.this, but with the
4730 * type of the most concrete class that the trait has
4731 * "require extend"-ed *)
4732 let r = Reason.Rwitness p in
4733 let env, parent_ty = Phase.localize_with_self env parent_ty in
4734 make_result env T.CIparent (r, TUtils.this_of parent_ty)
4736 | _ ->
4737 let parent = Env.get_parent env in
4738 let parent_defined = snd parent <> Typing_utils.tany env in
4739 if not parent_defined
4740 then Errors.parent_undefined p;
4741 let r = Reason.Rwitness p in
4742 let env, parent = Phase.localize_with_self env parent in
4743 (* parent is still technically the same object. *)
4744 make_result env T.CIparent (r, TUtils.this_of (r, snd parent))
4746 | _, (Terr | Tany | Tmixed | Tnonnull | Tarraykind _ | Toption _ | Tprim _
4747 | Tfun _ | Ttuple _ | Tshape _ | Tvar _ | Tdynamic
4748 | Tanon (_, _) | Tunresolved _ | Tabstract (_, _) | Tobject
4749 ) ->
4750 let parent = Env.get_parent env in
4751 let parent_defined = snd parent <> Typing_utils.tany env in
4752 if not parent_defined
4753 then Errors.parent_undefined p;
4754 let r = Reason.Rwitness p in
4755 let env, parent = Phase.localize_with_self env parent in
4756 (* parent is still technically the same object. *)
4757 make_result env T.CIparent (r, TUtils.this_of (r, snd parent))
4759 | CIstatic ->
4760 make_result env T.CIstatic
4761 (Reason.Rwitness p, TUtils.this_of (Env.get_self env))
4762 | CIself ->
4763 make_result env T.CIself
4764 (Reason.Rwitness p, snd (Env.get_self env))
4765 | CI (c, hl) ->
4766 let class_ = Env.get_class env (snd c) in
4767 (match class_ with
4768 | None ->
4769 make_result env (T.CI (c, hl)) (Reason.Rwitness p, Typing_utils.tany env)
4770 | Some class_ ->
4771 let env, tyl = type_arguments env p (snd c) class_.tc_tparams hl in
4772 make_result env (T.CI (c, hl))
4773 (Reason.Rwitness (fst c), Tclass (c, tyl))
4775 | CIexpr (p, _ as e) ->
4776 let env, te, ty = expr env e in
4777 let rec resolve_ety ty =
4778 let env, ty = TUtils.fold_unresolved env ty in
4779 let _, ty = Env.expand_type env ty in
4780 match TUtils.get_base_type env ty with
4781 | _, Tabstract (AKnewtype (classname, [the_cls]), _) when
4782 classname = SN.Classes.cClassname -> resolve_ety the_cls
4783 | _, Tabstract (AKgeneric _, _)
4784 | _, Tclass _ -> ty
4785 | r, Tunresolved tyl -> r, Tunresolved (List.map tyl resolve_ety)
4786 | _, Tvar _ as ty -> resolve_ety ty
4787 | _, Tdynamic as ty -> ty
4788 | _, (Tany | Tprim Tstring | Tabstract (_, None) | Tmixed | Tobject)
4789 when not (Env.is_strict env) ->
4790 Reason.Rwitness p, Typing_utils.tany env
4791 | _, (Terr | Tany | Tmixed | Tnonnull | Tarraykind _ | Toption _
4792 | Tprim _ | Tfun _ | Ttuple _
4793 | Tabstract ((AKenum _ | AKdependent _ | AKnewtype _), _)
4794 | Tanon (_, _) | Tobject | Tshape _ as ty
4795 ) ->
4796 Errors.expected_class ~suffix:(", but got "^Typing_print.error ty) p;
4797 Reason.Rwitness p, Typing_utils.terr env in
4798 let result_ty = resolve_ety ty in
4799 make_result env (T.CIexpr te) result_ty
4801 and call_construct p env class_ params el uel cid =
4802 let cid = if cid = CIparent then CIstatic else cid in
4803 let env, tcid, cid_ty = static_class_id p env cid in
4804 let ety_env = {
4805 type_expansions = [];
4806 this_ty = cid_ty;
4807 substs = Subst.make class_.tc_tparams params;
4808 from_class = Some cid;
4809 } in
4810 let env = Phase.check_tparams_constraints ~use_pos:p ~ety_env env class_.tc_tparams in
4811 if class_.tc_is_xhp then env, tcid, [], [] else
4812 let cstr = Env.get_construct env class_ in
4813 let mode = Env.get_mode env in
4814 Typing_hooks.dispatch_constructor_hook class_ params env p;
4815 match (fst cstr) with
4816 | None ->
4817 if el <> [] &&
4818 (mode = FileInfo.Mstrict || mode = FileInfo.Mpartial) &&
4819 class_.tc_members_fully_known
4820 then Errors.constructor_no_args p;
4821 let env, tel, _tyl = exprs env el in
4822 env, tcid, tel, []
4823 | Some { ce_visibility = vis; ce_type = lazy m; _ } ->
4824 TVis.check_obj_access p env (Reason.to_pos (fst m), vis);
4825 let env, m = Phase.localize ~ety_env env m in
4826 let env, tel, tuel, _ty = call ~expected:None p env m el uel in
4827 env, tcid, tel, tuel
4829 and check_arity ?(check_min=true) ?(did_unpack=false) pos pos_def (arity:int) exp_arity =
4830 let exp_min = (Typing_defs.arity_min exp_arity) in
4831 if check_min && arity < exp_min then
4832 Errors.typing_too_few_args pos pos_def;
4833 match exp_arity with
4834 | Fstandard (_, exp_max) ->
4835 let arity = if did_unpack then arity + 1 else arity in
4836 if (arity > exp_max)
4837 then Errors.typing_too_many_args pos pos_def;
4838 | Fvariadic _ | Fellipsis _ -> ()
4840 and check_lambda_arity lambda_pos def_pos lambda_arity expected_arity =
4841 let expected_min = Typing_defs.arity_min expected_arity in
4842 match lambda_arity, expected_arity with
4843 | Fstandard (lambda_min, _), Fstandard _ ->
4844 if lambda_min < expected_min
4845 then Errors.typing_too_few_args lambda_pos def_pos;
4846 if lambda_min > expected_min
4847 then Errors.typing_too_many_args lambda_pos def_pos
4848 | _, _ -> ()
4850 and check_deprecated p { ft_pos; ft_deprecated; _ } =
4851 match ft_deprecated with
4852 | Some s -> Errors.deprecated_use p ft_pos s
4853 | None -> ()
4855 (* The variadic capture argument is an array listing the passed
4856 * variable arguments for the purposes of the function body; callsites
4857 * should not unify with it *)
4858 and variadic_param env ft =
4859 match ft.ft_arity with
4860 | Fvariadic (_, p_ty) -> env, Some p_ty
4861 | Fellipsis _ | Fstandard _ -> env, None
4863 and param_modes { fp_pos; fp_kind; _ } (pos, e) =
4864 match fp_kind, e with
4865 | FPnormal, Unop (Ast.Uref, _) ->
4866 Errors.pass_by_ref_annotation_unexpected pos fp_pos
4867 | FPnormal, Callconv _ ->
4868 Errors.inout_annotation_unexpected pos fp_pos
4869 | FPnormal, _
4870 | FPref, Unop (Ast.Uref, _) -> ()
4871 | FPref, Callconv (kind, _) ->
4872 (match kind with
4873 (* HHVM supports pass-by-ref for arguments annotated as 'inout'. *)
4874 | Ast.Pinout -> ()
4876 | FPref, _ ->
4877 Errors.pass_by_ref_annotation_missing pos fp_pos
4878 (* HHVM also allows '&' on arguments to inout parameters via interop layer. *)
4879 | FPinout, Unop (Ast.Uref, _)
4880 | FPinout, Callconv (Ast.Pinout, _) -> ()
4881 | FPinout, _ ->
4882 Errors.inout_annotation_missing pos fp_pos
4884 and inout_write_back env { fp_type; _ } (_, e) =
4885 match e with
4886 | Callconv (Ast.Pinout, e1) ->
4887 (* Translate the write-back semantics of inout parameters.
4889 * This matters because we want to:
4890 * (1) make sure we can write to the original argument
4891 * (modifiable lvalue check)
4892 * (2) allow for growing of locals / Tunresolveds (type side effect)
4893 * but otherwise unify the argument type with the parameter hint
4895 let env, _te, _ty = assign_ (fst e1) Reason.URparam_inout env e1 fp_type in
4897 | _ -> env
4899 and call ~expected ?receiver_type pos env fty el uel =
4900 let env, tel, tuel, ty = call_ ~expected ~receiver_type pos env fty el uel in
4901 (* We need to solve the constraints after every single function call.
4902 * The type-checker is control-flow sensitive, the same value could
4903 * have different type depending on the branch that we are in.
4904 * When this is the case, a call could violate one of the constraints
4905 * in a branch. *)
4906 let env = Env.check_todo env in
4907 env, tel, tuel, ty
4909 and call_ ~expected ~receiver_type pos env fty el uel =
4910 let make_unpacked_traversable_ty pos ty =
4911 let unpack_r = Reason.Runpack_param pos in
4912 unpack_r, Tclass ((pos, SN.Collections.cTraversable), [ty])
4914 let env, efty = Env.expand_type env fty in
4915 (match efty with
4916 | _, (Terr | Tany | Tunresolved [] | Tdynamic) ->
4917 let el = el @ uel in
4918 let env, tel = List.map_env env el begin fun env elt ->
4919 let env, te, arg_ty =
4920 expr ~expected:(pos, Reason.URparam, (Reason.Rnone, Typing_utils.tany env)) ~is_func_arg:true env elt
4922 let env =
4923 match elt with
4924 | _, Callconv (Ast.Pinout, e1) ->
4925 let env, _te, _ty = assign_ (fst e1) Reason.URparam_inout env e1 efty in
4927 | _, Unop (Ast.Uref, e1) ->
4928 let env, _te, _ty = assign_ (fst e1) Reason.URparam env e1 efty in
4930 | _ -> env in
4931 let env, _arg_ty = check_valid_rvalue pos env arg_ty in
4932 env, te
4933 end in
4934 let env = call_untyped_unpack env uel in
4935 Typing_hooks.dispatch_fun_call_hooks [] (List.map (el @ uel) fst) env;
4936 let ty =
4937 if snd efty = Tdynamic then
4938 (Reason.Rdynamic_call pos, Tdynamic)
4939 else (Reason.Rnone, Typing_utils.tany env)
4941 env, tel, [], ty
4942 | _, Tunresolved [ty] ->
4943 call ~expected pos env ty el uel
4944 | r, Tunresolved tyl ->
4945 let env, retl = List.map_env env tyl begin fun env ty ->
4946 let env, _, _, ty = call ~expected pos env ty el uel in env, ty
4947 end in
4948 let env, ty = TUtils.in_var env (r, Tunresolved retl) in
4949 env, [], [], ty
4950 | r2, Tfun ft ->
4951 (* Typing of format string functions. It is dependent on the arguments (el)
4952 * so it cannot be done earlier.
4954 let env, ft = Typing_exts.retype_magic_func env ft el in
4955 check_deprecated pos ft;
4956 let pos_def = Reason.to_pos r2 in
4957 let env, var_param = variadic_param env ft in
4959 Typing_reactivity.check_call env receiver_type pos r2 ft;
4961 (* Force subtype with expected result *)
4962 let env = check_expected_ty "Call result" env ft.ft_ret expected in
4964 let is_lambda e = match snd e with Efun _ -> true | _ -> false in
4966 let get_next_param_info paraml =
4967 match paraml with
4968 | param::paraml ->
4969 Some param, paraml
4970 | [] ->
4971 var_param, paraml in
4973 (* Given an expected function type ft, check types for the non-unpacked
4974 * arguments. Don't check lambda expressions if check_lambdas=false *)
4975 let rec check_args check_lambdas env el paraml =
4976 match el with
4977 (* We've got an argument *)
4978 | ((pos, _ as e), opt_result) :: el ->
4979 (* Pick up next parameter type info *)
4980 let opt_param, paraml = get_next_param_info paraml in
4981 let env, one_result =
4982 if is_lambda e && not check_lambdas || Option.is_some opt_result
4983 then env, opt_result
4984 else
4985 begin match opt_param with
4986 | Some param ->
4987 let env, te, ty =
4988 expr ~is_func_arg:true ~accept_using_var:param.fp_accept_disposable
4989 ~expected:(pos, Reason.URparam, param.fp_type) env e in
4990 let env = call_param env param (e, ty) in
4991 env, Some (te, ty)
4992 | None ->
4993 let env, te, ty = expr ~expected:(pos, Reason.URparam, (Reason.Rnone, Typing_utils.tany env))
4994 ~is_func_arg:true env e in
4995 env, Some (te, ty)
4996 end in
4997 let env, rl, paraml = check_args check_lambdas env el paraml in
4998 env, (e, one_result)::rl, paraml
5000 | [] ->
5001 env, [], paraml in
5003 (* First check the non-lambda arguments. For generic functions, this
5004 * is likely to resolve type variables to concrete types *)
5005 let rl = List.map el (fun e -> (e, None)) in
5006 let env, rl, _ = check_args false env rl ft.ft_params in
5007 (* Now check the lambda arguments, hopefully with type variables resolved *)
5008 let env, rl, paraml = check_args true env rl ft.ft_params in
5009 (* We expect to see results for all arguments after this second pass *)
5010 let get_param opt =
5011 match opt with
5012 | Some x -> x
5013 | None -> failwith "missing parameter in check_args" in
5014 let tel = List.map rl (fun (_, opt) -> fst (get_param opt)) in
5015 let env, tuel, arity, check_min, did_unpack =
5016 match uel with
5017 | [] -> env, [], List.length el, true, false
5018 | e :: _ ->
5019 (* Enforces that e is unpackable. If e is a tuple, check types against
5020 * parameter types *)
5021 let env, te, ety = expr env e in
5022 match ety with
5023 | _, Ttuple tyl ->
5024 let rec check_elements env tyl paraml =
5025 match tyl with
5026 | [] -> env
5027 | ty::tyl ->
5028 let opt_param, paraml = get_next_param_info paraml in
5029 match opt_param with
5030 | None -> env
5031 | Some param ->
5032 let env = call_param env param (e, ty) in
5033 check_elements env tyl paraml in
5034 let env = check_elements env tyl paraml in
5035 env, [te], List.length el + List.length tyl, true, false
5036 | _ ->
5037 let param_tyl = List.map paraml (fun param -> param.fp_type) in
5038 let add_variadic_param_ty param_tyl =
5039 match var_param with
5040 | Some param -> param.fp_type :: param_tyl
5041 | None -> param_tyl in
5042 let param_tyl = add_variadic_param_ty param_tyl in
5043 let pos = fst e in
5044 let env = List.fold_right param_tyl ~init:env
5045 ~f:(fun param_ty env ->
5046 let traversable_ty = make_unpacked_traversable_ty pos param_ty in
5047 Type.sub_type pos Reason.URparam env ety traversable_ty)
5049 let check_min = (TypecheckerOptions.experimental_feature_enabled
5050 (Env.get_options env)
5051 TypecheckerOptions.experimental_unpacking_check_arity) in
5052 env, [te], List.length el, check_min, true
5054 (* If we unpacked an array, we don't check arity exactly. Since each
5055 * unpacked array consumes 1 or many parameters, it is nonsensical to say
5056 * that not enough args were passed in (so we don't do the min check).
5058 let () = check_arity ~check_min ~did_unpack pos pos_def arity ft.ft_arity in
5059 (* Variadic params cannot be inout so we can stop early *)
5060 let env = wfold_left2 inout_write_back env ft.ft_params el in
5061 Typing_hooks.dispatch_fun_call_hooks
5062 ft.ft_params (List.map (el @ uel) fst) env;
5063 env, tel, tuel, ft.ft_ret
5064 | r2, Tanon (arity, id) ->
5065 let env, tel, tyl = exprs ~is_func_arg:true env el in
5066 let expr_for_unpacked_expr_list env = function
5067 | [] -> env, [], None, Pos.none
5068 | (pos, _) as e :: _ ->
5069 let env, te, ety = expr env e in
5070 env, [te], Some ety, pos
5072 let append_tuple_types tyl = function
5073 | Some (_, Ttuple tuple_tyl) -> tyl @ tuple_tyl
5074 | _ -> tyl
5076 let determine_arity env min_arity pos = function
5077 | None
5078 | Some (_, Ttuple _) ->
5079 env, Fstandard (min_arity, min_arity)
5080 | Some (ety) ->
5081 (* We need to figure out the underlying type of the unpacked expr type.
5083 * For example, assume the call is:
5084 * $lambda(...$y);
5085 * where $y is a variadic or collection of strings.
5087 * $y may have the type Tarraykind or Traversable, however we need to
5088 * pass Fvariadic a param of type string.
5090 * Assuming $y has type Tarraykind, in order to get the underlying type
5091 * we create a fresh_type(), wrap it in a Traversable and make that
5092 * Traversable a super type of the expr type (Tarraykind). This way
5093 * we can infer the underlying type and create the correct param for
5094 * Fvariadic.
5096 let ty = Env.fresh_type() in
5097 let traversable_ty = make_unpacked_traversable_ty pos ty in
5098 let env = Type.sub_type pos Reason.URparam env ety traversable_ty in
5099 let param =
5100 { fp_pos = pos;
5101 fp_name = None;
5102 fp_type = ty;
5103 fp_kind = FPnormal;
5104 fp_accept_disposable = false;
5105 fp_mutable = false;
5108 env, Fvariadic (min_arity, param)
5110 let env, tuel, uety_opt, uepos = expr_for_unpacked_expr_list env uel in
5111 let tyl = append_tuple_types tyl uety_opt in
5112 let env, call_arity = determine_arity env (List.length tyl) uepos uety_opt in
5113 let anon = Env.get_anonymous env id in
5114 let fpos = Reason.to_pos r2 in
5115 (match anon with
5116 | None ->
5117 Errors.anonymous_recursive_call pos;
5118 env, tel, tuel, err_witness env pos
5119 | Some (_, _, counter, _, anon) ->
5120 let () = check_arity pos fpos (Typing_defs.arity_min call_arity) arity in
5121 let tyl = List.map tyl TUtils.default_fun_param in
5122 counter := !counter + 1;
5123 let env, _, ty = anon ~el env tyl call_arity in
5124 env, tel, tuel, ty)
5125 | _, Tarraykind _ when not (Env.is_strict env) ->
5126 (* Relaxing call_user_func to work with an array in partial mode *)
5127 let env = call_untyped_unpack env uel in
5128 env, [], [], (Reason.Rnone, Typing_utils.tany env)
5129 | _, ty ->
5130 bad_call pos ty;
5131 let env = call_untyped_unpack env uel in
5132 env, [], [], err_witness env pos
5135 and call_param env param ((pos, _ as e), arg_ty) =
5136 (match param.fp_name with
5137 | None -> ()
5138 | Some name -> Typing_suggest.save_param name env param.fp_type arg_ty
5140 param_modes param e;
5141 let env, arg_ty = check_valid_rvalue pos env arg_ty in
5143 (* When checking params the type 'x' may be expression dependent. Since
5144 * we store the expression id in the local env for Lvar, we want to apply
5145 * it in this case.
5147 let env, dep_ty = match snd e with
5148 | Lvar _ -> ExprDepTy.make env (CIexpr e) arg_ty
5149 | _ -> env, arg_ty in
5150 Type.sub_type pos Reason.URparam env dep_ty param.fp_type
5152 and call_untyped_unpack env uel = match uel with
5153 (* In the event that we don't have a known function call type, we can still
5154 * verify that any unpacked arguments (`...$args`) are something that can
5155 * be actually unpacked. *)
5156 | [] -> env
5157 | e::_ -> begin
5158 let env, _, ety = expr env e in
5159 match ety with
5160 | _, Ttuple _ -> env (* tuples are always fine *)
5161 | _ -> begin
5162 let pos = fst e in
5163 let ty = Env.fresh_type () in
5164 let unpack_r = Reason.Runpack_param pos in
5165 let unpack_ty = unpack_r, Tclass ((pos, SN.Collections.cTraversable), [ty]) in
5166 Type.sub_type pos Reason.URparam env ety unpack_ty
5170 and bad_call p ty =
5171 Errors.bad_call p (Typing_print.error ty)
5173 and unop ~is_func_arg ~forbid_uref p env uop te ty =
5174 let check_dynamic env ty ~f =
5175 if TUtils.is_dynamic env ty then
5176 env, (fst ty, Tdynamic)
5177 else f()
5179 let make_result env te result_ty =
5180 env, T.make_typed_expr p result_ty (T.Unop(uop, te)), result_ty in
5181 match uop with
5182 | Ast.Unot ->
5183 Async.enforce_nullable_or_not_awaitable env p ty;
5184 (* !$x (logical not) works with any type, so we just return Tbool *)
5185 make_result env te (Reason.Rlogic_ret p, Tprim Tbool)
5186 | Ast.Utild ->
5187 (* ~$x (bitwise not) only works with int *)
5188 let env, ty =
5189 check_dynamic env ty ~f:begin fun () ->
5190 let int_ty = (Reason.Rarith p, Tprim Tint) in
5191 Type.sub_type p Reason.URnone env ty int_ty, int_ty
5194 make_result env te ty
5195 | Ast.Uincr
5196 | Ast.Upincr
5197 | Ast.Updecr
5198 | Ast.Udecr
5199 | Ast.Uplus
5200 | Ast.Uminus ->
5201 (* math operators work with int or floats, so we call sub_type *)
5202 let env, ty = check_dynamic env ty ~f:begin fun () ->
5203 Type.sub_type p Reason.URnone env ty (Reason.Rarith p, Tprim Tnum), ty
5206 make_result env te ty
5207 | Ast.Uref ->
5208 (* We basically just ignore references in non-strict files *)
5209 if forbid_uref && TypecheckerOptions.experimental_feature_enabled
5210 (Env.get_options env)
5211 TypecheckerOptions.experimental_disallow_refs_in_array
5212 then Errors.binding_ref_in_array p
5213 else if Env.is_strict env && not is_func_arg
5214 then Errors.reference_expr p;
5215 make_result env te ty
5216 | Ast.Usilence ->
5217 (* Silencing does not change the type *)
5218 make_result env te ty
5220 and binop in_cond p env bop p1 te1 ty1 p2 te2 ty2 =
5221 let rec is_any ty =
5222 match Env.expand_type env ty with
5223 | (_, (_, (Tany | Terr))) -> true
5224 | (_, (_, Tunresolved tyl)) -> List.for_all tyl is_any
5225 | _ -> false in
5226 (* Test if `ty` is *not* the any type (or a variant thereof) and
5227 * is a subtype of the primitive type `prim`. *)
5228 let is_sub_prim env ty prim =
5229 let ty_prim = (Reason.Rarith p, Tprim prim) in
5230 if not (is_any ty) && SubType.is_sub_type env ty ty_prim
5231 then Some (fst ty) else None in
5232 (* Test if `ty` is *not* the any type (or a variant thereof) and
5233 * is a subtype of `num` but is not a subtype of `int` *)
5234 let is_sub_num_not_sub_int env ty =
5235 let ty_num = (Reason.Rarith p, Tprim Tnum) in
5236 let ty_int = (Reason.Rarith p, Tprim Tint) in
5237 if not (is_any ty) && SubType.is_sub_type env ty ty_num
5238 && not (SubType.is_sub_type env ty ty_int)
5239 then Some (fst ty) else None in
5240 (* Force ty1 to be a subtype of ty2 (unless it is any) *)
5241 let enforce_sub_ty env ty1 ty2 =
5242 let env = Type.sub_type p Reason.URnone env ty1 ty2 in
5243 Env.expand_type env ty1 in
5244 let make_result env te1 te2 ty =
5245 env, T.make_typed_expr p ty (T.Binop(bop, te1, te2)), ty in
5246 let check_dynamic f =
5247 if TUtils.is_dynamic env ty1 then
5248 let result_prim =
5249 match is_sub_prim env ty2 Tfloat with
5250 | Some r ->
5251 (* dynamic op float = float *)
5252 (r, Tprim Tfloat)
5253 | _ ->
5254 (* dynamic op _ = num *)
5255 (fst ty2, Tprim Tnum)
5257 make_result env te1 te2 result_prim
5258 else if TUtils.is_dynamic env ty2 then
5259 let result_prim =
5260 match is_sub_prim env ty1 Tfloat with
5261 | Some r ->
5262 (* dynamic op float = float *)
5263 (r, Tprim Tfloat)
5264 | _ ->
5265 (* dynamic op _ = num *)
5266 (fst ty1, Tprim Tnum)
5268 make_result env te1 te2 result_prim
5269 else f ()
5271 match bop with
5272 | Ast.Plus ->
5273 let env, ty1 = TUtils.fold_unresolved env ty1 in
5274 let env, ty2 = TUtils.fold_unresolved env ty2 in
5275 let env, ety1 = Env.expand_type env ty1 in
5276 let env, ety2 = Env.expand_type env ty2 in
5277 (match ety1, ety2 with
5278 (* For array<V1>+array<V2> and array<K1,V1>+array<K2,V2>, allow
5279 * the addition to produce a supertype. (We could also handle
5280 * when they have mismatching annotations, but we get better error
5281 * messages if we just let those get unified in the next case. *)
5282 (* The general types are:
5283 * function<Tk,Tv>(array<Tk,Tv>, array<Tk,Tv>): array<Tk,Tv>
5284 * function<T>(array<T>, array<T>): array<T>
5285 * and subtyping on the arguments deals with everything
5287 | (_, Tarraykind (AKmap _ as ak)), (_, Tarraykind (AKmap _))
5288 | (_, Tarraykind (AKvec _ as ak)), (_, Tarraykind (AKvec _)) ->
5289 let env, a_sup = Env.fresh_unresolved_type env in
5290 let env, b_sup = Env.fresh_unresolved_type env in
5291 let res_ty = Reason.Rarray_plus_ret p, Tarraykind (
5292 match ak with
5293 | AKvec _ -> AKvec a_sup
5294 | AKmap _ -> AKmap (a_sup, b_sup)
5295 | _ -> assert false
5296 ) in
5297 let env = Type.sub_type p1 Reason.URnone env ety1 res_ty in
5298 let env = Type.sub_type p2 Reason.URnone env ety2 res_ty in
5299 make_result env te1 te2 res_ty
5300 | (_, Tarraykind _), (_, Tarraykind (AKshape _)) ->
5301 let env, ty2 = Typing_arrays.downcast_aktypes env ty2 in
5302 binop in_cond p env bop p1 te1 ty1 p2 te2 ty2
5303 | (_, Tarraykind (AKshape _)), (_, Tarraykind _) ->
5304 let env, ty1 = Typing_arrays.downcast_aktypes env ty1 in
5305 binop in_cond p env bop p1 te1 ty1 p2 te2 ty2
5306 | (_, Tarraykind _), (_, Tarraykind _)
5307 | (_, (Tany | Terr)), (_, Tarraykind _)
5308 | (_, Tarraykind _), (_, Tany) ->
5309 let env, ty = Type.unify p Reason.URnone env ty1 ty2 in
5310 make_result env te1 te2 ty
5311 | (_, Tdynamic), (_, Tdynamic) ->
5312 make_result env te1 te2 (Reason.Rarith p, Tdynamic)
5313 | (_, (Tany | Terr | Tmixed | Tnonnull | Tarraykind _ | Toption _ | Tdynamic
5314 | Tprim _ | Tvar _ | Tfun _ | Tabstract (_, _) | Tclass (_, _)
5315 | Ttuple _ | Tanon (_, _) | Tunresolved _ | Tobject | Tshape _
5317 ), _ ->
5318 let env, texpr, ty =
5319 binop in_cond p env Ast.Minus p1 te1 ty1 p2 te2 ty2 in
5320 match snd texpr with
5321 | T.Binop (_, te1, te2) -> make_result env te1 te2 ty
5322 | _ -> assert false
5324 | Ast.Minus | Ast.Star -> check_dynamic begin fun () ->
5325 let env, ty1 = enforce_sub_ty env ty1 (Reason.Rarith p1, Tprim Tnum) in
5326 let env, ty2 = enforce_sub_ty env ty2 (Reason.Rarith p2, Tprim Tnum) in
5327 (* If either side is a float then float: 1.0 - 1 -> float *)
5328 (* These have types
5329 * function(float, num): float
5330 * function(num, float): float
5332 match is_sub_prim env ty1 Tfloat, is_sub_prim env ty2 Tfloat with
5333 | (Some r, _) | (_, Some r) ->
5334 make_result env te1 te2 (r, Tprim Tfloat)
5335 | _, _ ->
5336 (* Both sides are integers, then integer: 1 - 1 -> int *)
5337 (* This has type
5338 * function(int, int): int
5340 match is_sub_prim env ty1 Tint, is_sub_prim env ty2 Tint with
5341 | (Some _, Some _) ->
5342 make_result env te1 te2 (Reason.Rarith_ret p, Tprim Tint)
5343 | _, _ ->
5344 (* Either side is a non-int num then num *)
5345 (* This has type
5346 * function(num, num): num
5348 match is_sub_num_not_sub_int env ty1,
5349 is_sub_num_not_sub_int env ty2 with
5350 | (Some r, _) | (_, Some r) ->
5351 make_result env te1 te2 (r, Tprim Tnum)
5352 (* Otherwise? *)
5353 | _, _ -> make_result env te1 te2 ty1
5355 | Ast.Slash | Ast.Starstar -> check_dynamic begin fun () ->
5356 let env, ty1 = enforce_sub_ty env ty1 (Reason.Rarith p1, Tprim Tnum) in
5357 let env, ty2 = enforce_sub_ty env ty2 (Reason.Rarith p2, Tprim Tnum) in
5358 (* If either side is a float then float *)
5359 (* These have types
5360 * function(float, num) : float
5361 * function(num, float) : float
5362 * [Actually, for division result can be false if second arg is zero]
5364 match is_sub_prim env ty1 Tfloat, is_sub_prim env ty2 Tfloat with
5365 | (Some r, _) | (_, Some r) ->
5366 make_result env te1 te2 (r, Tprim Tfloat)
5367 (* Otherwise it has type
5368 * function(num, num) : num
5369 * [Actually, for division result can be false if second arg is zero]
5371 | _, _ ->
5372 let r = match bop with
5373 | Ast.Slash -> Reason.Rret_div p
5374 | _ -> Reason.Rarith_ret p in
5375 make_result env te1 te2 (r, Tprim Tnum)
5377 | Ast.Percent -> check_dynamic begin fun () ->
5378 (* Integer remainder function has type
5379 * function(int, int) : int
5380 * [Actually, result can be false if second arg is zero]
5382 let env, _ = enforce_sub_ty env ty1 (Reason.Rarith p1, Tprim Tint) in
5383 let env, _ = enforce_sub_ty env ty2 (Reason.Rarith p1, Tprim Tint) in
5384 make_result env te1 te2 (Reason.Rarith_ret p, Tprim Tint)
5386 | Ast.Xor ->
5387 if TUtils.is_dynamic env ty1 && TUtils.is_dynamic env ty2 then
5388 make_result env te1 te2 (Reason.Rbitwise p, Tdynamic) else
5389 begin match is_sub_prim env ty1 Tbool, is_sub_prim env ty2 Tbool with
5390 | (Some _, _)
5391 | (_, Some _) ->
5392 (* Logical xor:
5393 * function(bool, bool) : bool
5395 let env, _ = if TUtils.is_dynamic env ty1 then env, ty1 else
5396 enforce_sub_ty env ty1 (Reason.Rlogic_ret p1, Tprim Tbool) in
5397 let env, _ = if TUtils.is_dynamic env ty2 then env, ty2 else
5398 enforce_sub_ty env ty2 (Reason.Rlogic_ret p1, Tprim Tbool) in
5399 make_result env te1 te2 (Reason.Rlogic_ret p, Tprim Tbool)
5400 | _, _ ->
5401 (* Arithmetic xor:
5402 * function(int, int) : int
5404 let env, _ = if TUtils.is_dynamic env ty1 then env, ty1 else
5405 enforce_sub_ty env ty1 (Reason.Rarith p1, Tprim Tint) in
5406 let env, _ = if TUtils.is_dynamic env ty2 then env, ty2 else
5407 enforce_sub_ty env ty2 (Reason.Rarith p1, Tprim Tint) in
5408 make_result env te1 te2 (Reason.Rarith_ret p, Tprim Tint)
5410 (* Equality and disequality:
5411 * function<T>(T, T): bool
5413 | Ast.Eqeq | Ast.Diff ->
5414 make_result env te1 te2 (Reason.Rcomp p, Tprim Tbool)
5415 | Ast.EQeqeq | Ast.Diff2 ->
5416 if not in_cond
5417 then Typing_equality_check.assert_nontrivial p bop env ty1 ty2;
5418 make_result env te1 te2 (Reason.Rcomp p, Tprim Tbool)
5419 | Ast.Lt | Ast.Lte | Ast.Gt | Ast.Gte | Ast.Cmp ->
5420 let ty_result = match bop with Ast.Cmp -> Tprim Tint | _ -> Tprim Tbool in
5421 let ty_num = (Reason.Rcomp p, Tprim Nast.Tnum) in
5422 let ty_string = (Reason.Rcomp p, Tprim Nast.Tstring) in
5423 let ty_datetime =
5424 (Reason.Rcomp p, Tclass ((p, SN.Classes.cDateTime), [])) in
5425 let both_sub ty =
5426 SubType.is_sub_type env ty1 ty && SubType.is_sub_type env ty2 ty in
5427 (* So we have three different types here:
5428 * function(num, num): bool
5429 * function(string, string): bool
5430 * function(DateTime, DateTime): bool
5432 if both_sub ty_num || both_sub ty_string || both_sub ty_datetime
5433 then make_result env te1 te2 (Reason.Rcomp p, ty_result)
5434 else
5435 (* TODO this is questionable; PHP's semantics for conversions with "<"
5436 * are pretty crazy and we may want to just disallow this? *)
5437 (* This is universal:
5438 * function<T>(T, T): bool
5440 let env, _ =
5441 if TUtils.is_dynamic env ty1 || TUtils.is_dynamic env ty2
5442 then env, ty1 else Type.unify p Reason.URnone env ty1 ty2 in
5443 make_result env te1 te2 (Reason.Rcomp p, ty_result)
5444 | Ast.Dot ->
5445 (* A bit weird, this one:
5446 * function(Stringish | string, Stringish | string) : string)
5448 let env = SubType.sub_string p1 env ty1 in
5449 let env = SubType.sub_string p2 env ty2 in
5450 make_result env te1 te2 (Reason.Rconcat_ret p, Tprim Tstring)
5451 | Ast.LogXor
5452 | Ast.AMpamp
5453 | Ast.BArbar ->
5454 make_result env te1 te2 (Reason.Rlogic_ret p, Tprim Tbool)
5455 | Ast.Amp | Ast.Bar | Ast.Ltlt | Ast.Gtgt ->
5456 (* If both are dynamic, we can only return dynamic *)
5457 if TUtils.is_dynamic env ty1 && TUtils.is_dynamic env ty2 then
5458 make_result env te1 te2 (Reason.Rbitwise_ret p, Tdynamic) else
5459 (* Otherwise at least one of these is an int, so the result is an int *)
5460 let env, _ = if TUtils.is_dynamic env ty1
5461 then env, ty1
5462 else enforce_sub_ty env ty1 (Reason.Rbitwise p1, Tprim Tint) in
5463 let env, _ = if TUtils.is_dynamic env ty2
5464 then env, ty2 else
5465 enforce_sub_ty env ty2 (Reason.Rbitwise p2, Tprim Tint) in
5466 make_result env te1 te2 (Reason.Rbitwise_ret p, Tprim Tint)
5467 | Ast.Eq _ ->
5468 assert false
5470 and condition_var_non_null env = function
5471 | _, Lvar x
5472 | _, Dollardollar x ->
5473 let x_ty = Env.get_local env (snd x) in
5474 let env, x_ty = TUtils.non_null env x_ty in
5475 set_local env x x_ty
5476 | p, Class_get (((), cname), (_, member_name)) as e ->
5477 let env, _te, ty = expr env e in
5478 let env, local = Env.FakeMembers.make_static p env cname member_name in
5479 let lvar = (p, local) in
5480 let env = set_local env lvar ty in
5481 let local = p, Lvar lvar in
5482 condition_var_non_null env local
5483 (* TODO TAST: generate an assignment to the fake local in the TAST *)
5484 | p, Obj_get ((_, This | _, Lvar _ as obj),
5485 (_, Id (_, member_name)),
5486 _) as e ->
5487 let env, _te, ty = expr env e in
5488 let env, local = Env.FakeMembers.make p env obj member_name in
5489 let lvar = (p, local) in
5490 let env = set_local env lvar ty in
5491 let local = p, Lvar lvar in
5492 condition_var_non_null env local
5493 | _ -> env
5495 and condition_isset env = function
5496 | _, Array_get (x, _) -> condition_isset env x
5497 | v -> condition_var_non_null env v
5500 * Build an environment for the true or false branch of
5501 * conditional statements.
5503 and condition ?lhs_of_null_coalesce env tparamet =
5504 let expr env x =
5505 let env, _te, ty = raw_expr ?lhs_of_null_coalesce ~in_cond:true env x in
5506 Async.enforce_nullable_or_not_awaitable env (fst x) ty;
5507 check_valid_rvalue (fst x) env ty
5508 in let condition = condition ?lhs_of_null_coalesce
5509 in function
5510 | _, Expr_list [] -> env
5511 | _, Expr_list [x] ->
5512 let env, _ = expr env x in
5513 condition env tparamet x
5514 | r, Expr_list (x::xs) ->
5515 let env, _ = expr env x in
5516 condition env tparamet (r, Expr_list xs)
5517 | _, Call (Cnormal, (_, Id (_, func)), _, [param], [])
5518 when SN.PseudoFunctions.isset = func && tparamet &&
5519 not (Env.is_strict env) ->
5520 condition_isset env param
5521 | _, Call (Cnormal, (_, Id (_, func)), _, [e], [])
5522 when not tparamet && SN.StdlibFunctions.is_null = func ->
5523 condition_var_non_null env e
5524 | r, Binop ((Ast.Eqeq | Ast.EQeqeq as bop),
5525 (_, Null), e)
5526 | r, Binop ((Ast.Eqeq | Ast.EQeqeq as bop),
5527 e, (_, Null)) when not tparamet ->
5528 let env, x_ty = expr env e in
5529 let env =
5530 if bop == Ast.Eqeq then check_null_wtf env r x_ty else env in
5531 condition_var_non_null env e
5532 | (p, (Lvar _ | Obj_get _ | Class_get _) as e) ->
5533 let env, ty = expr env e in
5534 let env, ety = Env.expand_type env ty in
5535 (match ety with
5536 | _, Tarraykind (AKany | AKempty)
5537 | _, Tprim Tbool -> env
5538 | _, (Terr | Tany | Tmixed | Tnonnull | Tarraykind _ | Toption _ | Tdynamic
5539 | Tprim _ | Tvar _ | Tfun _ | Tabstract (_, _) | Tclass (_, _)
5540 | Ttuple _ | Tanon (_, _) | Tunresolved _ | Tobject | Tshape _
5541 ) ->
5542 condition env (not tparamet) (p, Binop (Ast.Eqeq, e, (p, Null))))
5543 | r, Binop (Ast.Eq None, var, e) when tparamet ->
5544 let env, e_ty = expr env e in
5545 let env = check_null_wtf env r e_ty in
5546 condition_var_non_null env var
5547 | p1, Binop (Ast.Eq None, (_, (Lvar _ | Obj_get _) as lv), (p2, _)) ->
5548 let env, _ = expr env (p1, Binop (Ast.Eq None, lv, (p2, Null))) in
5549 condition env tparamet lv
5550 | p, Binop ((Ast.Diff | Ast.Diff2 as op), e1, e2) ->
5551 let op = if op = Ast.Diff then Ast.Eqeq else Ast.EQeqeq in
5552 condition env (not tparamet) (p, Binop (op, e1, e2))
5553 | _, Id (_, s) when s = SN.HH.rx_is_enabled ->
5554 (* when Rx\IS_ENABLED is false - switch env to non-reactive *)
5555 if not tparamet
5556 then Env.set_env_reactive env Nonreactive
5557 else env
5558 | _, Binop (Ast.AMpamp, e1, e2) when tparamet ->
5559 let env = condition env true e1 in
5560 let env = condition env true e2 in
5562 | _, Binop (Ast.BArbar, e1, e2) when not tparamet ->
5563 let env = condition env false e1 in
5564 let env = condition env false e2 in
5566 | _, Call (Cnormal, (p, Id (_, f)), _, [lv], [])
5567 when tparamet && f = SN.StdlibFunctions.is_array ->
5568 is_array env `PHPArray p f lv
5569 | _, Call (Cnormal, (p, Id (_, f)), _, [lv], [])
5570 when tparamet && f = SN.StdlibFunctions.is_vec ->
5571 is_array env `HackVec p f lv
5572 | _, Call (Cnormal, (p, Id (_, f)), _, [lv], [])
5573 when tparamet && f = SN.StdlibFunctions.is_dict ->
5574 is_array env `HackDict p f lv
5575 | _, Call (Cnormal, (p, Id (_, f)), _, [lv], [])
5576 when tparamet && f = SN.StdlibFunctions.is_keyset ->
5577 is_array env `HackKeyset p f lv
5578 | _, Call (Cnormal, (p, Id (_, f)), _, [lv], [])
5579 when tparamet && f = SN.StdlibFunctions.is_int ->
5580 is_type env lv Tint (Reason.Rpredicated (p, f))
5581 | _, Call (Cnormal, (p, Id (_, f)), _, [lv], [])
5582 when tparamet && f = SN.StdlibFunctions.is_bool ->
5583 is_type env lv Tbool (Reason.Rpredicated (p, f))
5584 | _, Call (Cnormal, (p, Id (_, f)), _, [lv], [])
5585 when tparamet && f = SN.StdlibFunctions.is_float ->
5586 is_type env lv Tfloat (Reason.Rpredicated (p, f))
5587 | _, Call (Cnormal, (p, Id (_, f)), _, [lv], [])
5588 when tparamet && f = SN.StdlibFunctions.is_string ->
5589 is_type env lv Tstring (Reason.Rpredicated (p, f))
5590 | _, Call (Cnormal, (p, Id (_, f)), _, [lv], [])
5591 when tparamet && f = SN.StdlibFunctions.is_resource ->
5592 is_type env lv Tresource (Reason.Rpredicated (p, f))
5593 | _, Unop (Ast.Unot, e) ->
5594 condition env (not tparamet) e
5595 | p, InstanceOf (ivar, ((), cid)) when tparamet && is_instance_var ivar ->
5596 (* Check the expession and determine its static type *)
5597 let env, _te, x_ty = raw_expr ~in_cond:false env ivar in
5599 (* What is the local variable bound to the expression? *)
5600 let env, ((ivar_pos, _) as ivar) = get_instance_var env ivar in
5602 (* The position p here is not really correct... it's the position
5603 * of the instanceof expression, not the class id. But we don't store
5604 * position data for the latter. *)
5605 let env, _te, obj_ty = static_class_id p env cid in
5607 if SubType.is_sub_type env obj_ty (
5608 Reason.none, Tclass ((Pos.none, SN.Classes.cAwaitable), [Reason.none, Typing_utils.tany env])
5609 ) then () else Async.enforce_nullable_or_not_awaitable env (fst ivar) x_ty;
5611 let safe_instanceof_enabled =
5612 TypecheckerOptions.experimental_feature_enabled
5613 (Env.get_options env) TypecheckerOptions.experimental_instanceof in
5614 let rec resolve_obj env obj_ty =
5615 (* Expand so that we don't modify x *)
5616 let env, obj_ty = Env.expand_type env obj_ty in
5617 match obj_ty with
5618 (* If it's a generic that's expression dependent, we need to
5619 look at all of its upper bounds and create an unresolved type to
5620 represent all of the possible types.
5622 | r, Tabstract (AKgeneric s, _) when AbstractKind.is_generic_dep_ty s ->
5623 let upper_bounds = TySet.elements (Env.get_upper_bounds env s) in
5624 let env, tyl = List.map_env env upper_bounds resolve_obj in
5625 env, (r, Tunresolved tyl)
5626 | _, Tabstract (AKgeneric name, _) ->
5627 if safe_instanceof_enabled
5628 then Errors.instanceof_generic_classname p name;
5629 env, obj_ty
5630 | _, Tabstract (AKdependent (`this, []), Some (_, Tclass _)) ->
5631 let env, obj_ty =
5632 (* Technically instanceof static is not strong enough to prove
5633 * that a type is exactly the same as the late bound type.
5634 * For now we allow this lie to exist. To solve
5635 * this we either need to create a new type that means
5636 * subtype of static or provide a way of specifying exactly
5637 * the late bound type i.e. $x::class === static::class
5639 if cid = CIstatic then
5640 ExprDepTy.make env CIstatic obj_ty
5641 else
5642 env, obj_ty in
5643 env, obj_ty
5644 | _, Tabstract ((AKdependent _ | AKnewtype _), Some ty) ->
5645 resolve_obj env ty
5646 | _, Tclass ((_, cid as _c), tyl) ->
5647 begin match Env.get_class env cid with
5648 (* Why would this happen? *)
5649 | None ->
5650 env, (Reason.Rwitness ivar_pos, Tobject)
5652 | Some class_info ->
5653 if SubType.is_sub_type env x_ty obj_ty
5654 then
5655 (* If the right side of the `instanceof` object is
5656 * a super type of what we already knew. In this case,
5657 * since we already have a more specialized object, we
5658 * don't touch the original object. Check out the unit
5659 * test srecko.php if this is unclear.
5661 * Note that if x_ty is Typing_utils.tany env, no amount of subtype
5662 * checking will be able to specify it
5663 * further. This is arguably desirable to maintain
5664 * the invariant that removing annotations gets rid
5665 * of typing errors in partial mode (See also
5666 * t3216948). *)
5667 env, x_ty
5668 else
5669 (* We only implement the safe instanceof in strict mode *)
5670 (* Also: for generic types we implememt it only with
5671 * experimental feature enabled *)
5672 if Env.is_strict env && (tyl = [] || safe_instanceof_enabled)
5673 then safe_instanceof env p ivar x_ty obj_ty _c class_info
5674 else env, obj_ty
5676 | r, Tunresolved tyl ->
5677 let env, tyl = List.map_env env tyl resolve_obj in
5678 env, (r, Tunresolved tyl)
5679 | _, (Terr | Tany | Tmixed | Tnonnull| Tarraykind _ | Tprim _ | Tvar _
5680 | Tfun _ | Tabstract ((AKenum _ | AKnewtype _ | AKdependent _), _)
5681 | Ttuple _ | Tanon (_, _) | Toption _ | Tobject | Tshape _
5682 | Tdynamic) ->
5683 env, (Reason.Rwitness ivar_pos, Tobject)
5685 let env, x_ty = resolve_obj env obj_ty in
5686 set_local env ivar x_ty
5687 | _p, Is (ivar, h) ->
5688 (* What is the local variable bound to the expression? *)
5689 let env, ivar = get_instance_var env ivar in
5690 (* Resolve the typehint to a type *)
5691 let env, hint_ty = Phase.hint_locl env h in
5692 begin match (IsAsExprHint.validate hint_ty) with
5693 | IsAsExprHint.Valid
5694 | IsAsExprHint.Partial _ ->
5695 set_local env ivar hint_ty
5696 | IsAsExprHint.Invalid _ -> env
5698 | _, Binop ((Ast.Eqeq | Ast.EQeqeq), e, (_, Null))
5699 | _, Binop ((Ast.Eqeq | Ast.EQeqeq), (_, Null), e) ->
5700 let env, _ = expr env e in
5702 | e ->
5703 let env, _ = expr env e in
5706 and safe_instanceof env p ivar ivar_ty obj_ty _c class_info =
5707 let ivar_pos = fst ivar in
5708 (* Generate fresh names consisting of formal type parameter name
5709 * with unique suffix *)
5710 let env, tparams_with_new_names =
5711 List.map_env env class_info.tc_tparams
5712 (fun env ((_, (_,name), _) as tp) ->
5713 let env, name = Env.add_fresh_generic_parameter env name in
5714 env, (tp, name)) in
5715 let s =
5716 snd _c ^ "<" ^
5717 String.concat "," (List.map tparams_with_new_names ~f:snd)
5718 ^ ">" in
5719 let reason = Reason.Rinstanceof (ivar_pos, s) in
5720 let tyl_fresh = List.map
5721 ~f:(fun (_, newname) -> (reason, Tabstract(AKgeneric newname, None)))
5722 tparams_with_new_names in
5724 (* Type of variable in block will be class name
5725 * with fresh type parameters *)
5726 let obj_ty = (fst obj_ty, Tclass(_c, tyl_fresh)) in
5728 (* Add in constraints as assumptions on those type parameters *)
5729 let ety_env = {
5730 type_expansions = [];
5731 substs = Subst.make class_info.tc_tparams tyl_fresh;
5732 this_ty = obj_ty; (* In case `this` appears in constraints *)
5733 from_class = None;
5734 } in
5735 let add_bounds env ((_, _, cstr_list), ty_fresh) =
5736 List.fold_left cstr_list ~init:env ~f:begin fun env (ck, ty) ->
5737 (* Substitute fresh type parameters for
5738 * original formals in constraint *)
5739 let env, ty = Phase.localize ~ety_env env ty in
5740 SubType.add_constraint p env ck ty_fresh ty end in
5741 let env =
5742 List.fold_left (List.zip_exn class_info.tc_tparams tyl_fresh)
5743 ~f:add_bounds ~init:env in
5745 (* Finally, if we have a class-test on something with static class type,
5746 * then we can chase the hierarchy and decompose the types to deduce
5747 * further assumptions on type parameters. For example, we might have
5748 * class B<Tb> { ... }
5749 * class C extends B<int>
5750 * and have obj_ty = C and x_ty = B<T> for a generic parameter T.
5751 * Then SubType.add_constraint will deduce that T=int and add int as
5752 * both lower and upper bound on T in env.lenv.tpenv
5754 let env = SubType.add_constraint p env Ast.Constraint_as obj_ty ivar_ty in
5756 (* It's often the case that the fresh name isn't necessary. For
5757 * example, if C<T> extends B<T>, and we have $x:B<t> for some type t
5758 * then $x instanceof B should refine to $x:C<t>.
5759 * We take a simple approach:
5760 * For a fresh type parameter T#1, if
5761 * 1) There is an eqality constraint T#1 = t then replace T#1 with t.
5762 * 2) T#1 is covariant, and T#1 <: t and occurs nowhere else in the constraints
5763 * 3) T#1 is contravariant, and t <: T#1 and occurs nowhere else in the constraints
5765 let tparams_in_constraints = Env.get_tpenv_tparams env in
5766 let tyl_fresh_simplified =
5767 List.map tparams_with_new_names
5768 ~f:begin fun ((variance, _, _), newname) ->
5769 match variance,
5770 TySet.elements (Env.get_lower_bounds env newname),
5771 TySet.elements (Env.get_equal_bounds env newname),
5772 TySet.elements (Env.get_upper_bounds env newname) with
5773 | _, _, [ty], _ -> ty
5774 | Ast.Covariant, _, _, [ty]
5775 | Ast.Contravariant, [ty], _, _
5776 when not (SSet.mem newname tparams_in_constraints) -> ty
5777 | _, _, _, _ -> (reason, Tabstract(AKgeneric newname, None)) end in
5778 let obj_ty_simplified = (fst obj_ty, Tclass(_c, tyl_fresh_simplified)) in
5779 env, obj_ty_simplified
5781 and is_instance_var = function
5782 | _, (Lvar _ | This) -> true
5783 | _, Obj_get ((_, This), (_, Id _), _) -> true
5784 | _, Obj_get ((_, Lvar _), (_, Id _), _) -> true
5785 | _, Class_get (_, _) -> true
5786 | _ -> false
5788 and get_instance_var env = function
5789 | p, Class_get (((), cname), (_, member_name)) ->
5790 let env, local = Env.FakeMembers.make_static p env cname member_name in
5791 env, (p, local)
5792 | p, Obj_get ((_, This | _, Lvar _ as obj), (_, Id (_, member_name)), _) ->
5793 let env, local = Env.FakeMembers.make p env obj member_name in
5794 env, (p, local)
5795 | _, Lvar (p, x) -> env, (p, x)
5796 | p, This -> env, (p, this)
5797 | _ -> failwith "Should only be called when is_instance_var is true"
5799 and check_null_wtf env p ty =
5800 if not (Env.is_strict env) then env else
5801 let env, ty = TUtils.fold_unresolved env ty in
5802 let env, ety = Env.expand_type env ty in
5803 match ety with
5804 | _, Toption ty ->
5805 (* Find sketchy nulls hidden under singleton Tunresolved *)
5806 let env, ty = TUtils.fold_unresolved env ty in
5807 let env, ety = Env.expand_type env ty in
5808 (match ety with
5809 | _, (Tmixed | Tnonnull) ->
5810 Errors.sketchy_null_check p
5811 | _, Tprim _ ->
5812 Errors.sketchy_null_check_primitive p
5813 | _, (Terr | Tany | Tarraykind _ | Toption _ | Tvar _ | Tfun _
5814 | Tabstract (_, _) | Tclass (_, _) | Ttuple _ | Tanon (_, _)
5815 | Tunresolved _ | Tobject | Tshape _ | Tdynamic) -> ());
5817 | _, (Terr | Tany | Tmixed | Tnonnull | Tarraykind _ | Tprim _ | Tvar _ | Tdynamic
5818 | Tfun _ | Tabstract (_, _) | Tclass (_, _) | Ttuple _ | Tanon (_, _)
5819 | Tunresolved _ | Tobject | Tshape _ ) -> env
5821 and is_type env e tprim r =
5822 match e with
5823 | p, Class_get (((), cname), (_, member_name)) ->
5824 let env, local = Env.FakeMembers.make_static p env cname member_name in
5825 set_local env (p, local) (r, Tprim tprim)
5826 | p, Obj_get ((_, This | _, Lvar _ as obj), (_, Id (_, member_name)), _) ->
5827 let env, local = Env.FakeMembers.make p env obj member_name in
5828 set_local env (p, local) (r, Tprim tprim)
5829 | _, Lvar lvar ->
5830 set_local env lvar (r, Tprim tprim)
5831 | _ -> env
5833 (* Refine type for is_array, is_vec, is_keyset and is_dict tests
5834 * `pred_name` is the function name itself (e.g. 'is_vec')
5835 * `p` is position of the function name in the source
5836 * `arg_expr` is the argument to the function
5838 and is_array env ty p pred_name arg_expr =
5839 let env, _te, arg_ty = expr env arg_expr in
5840 let r = Reason.Rpredicated (p, pred_name) in
5841 let env, tarrkey_name = Env.add_fresh_generic_parameter env "Tk" in
5842 let tarrkey = (r, Tabstract (AKgeneric tarrkey_name, None)) in
5843 let env = SubType.add_constraint p env Ast.Constraint_as
5844 tarrkey (r, Tprim Tarraykey) in
5845 let env, tfresh_name = Env.add_fresh_generic_parameter env "T" in
5846 let tfresh = (r, Tabstract (AKgeneric tfresh_name, None)) in
5847 (* This is the refined type of e inside the branch *)
5848 let refined_ty =
5849 (r, (match ty with
5850 | `HackDict ->
5851 Tclass ((Pos.none, SN.Collections.cDict), [tarrkey; tfresh])
5852 | `HackVec ->
5853 Tclass ((Pos.none, SN.Collections.cVec), [tfresh])
5854 | `HackKeyset ->
5855 Tclass ((Pos.none, SN.Collections.cKeyset), [tarrkey])
5856 | `PHPArray ->
5857 let safe_isarray_enabled =
5858 TypecheckerOptions.experimental_feature_enabled
5859 (Env.get_options env) TypecheckerOptions.experimental_isarray in
5860 if safe_isarray_enabled
5861 then Tarraykind (AKvarray_or_darray tfresh)
5862 else Tarraykind AKany)) in
5863 (* Add constraints on generic parameters that must
5864 * hold for refined_ty <:arg_ty. For example, if arg_ty is Traversable<T>
5865 * and refined_ty is keyset<T#1> then we know T#1 <: T *)
5866 let env = SubType.add_constraint p env Ast.Constraint_as refined_ty arg_ty in
5867 match arg_expr with
5868 | (_, Class_get (((), cname), (_, member_name))) ->
5869 let env, local = Env.FakeMembers.make_static p env cname member_name in
5870 set_local env (p, local) refined_ty
5871 | (_, Obj_get ((_, This | _, Lvar _ as obj), (_, Id (_, member_name)), _)) ->
5872 let env, local = Env.FakeMembers.make p env obj member_name in
5873 set_local env (p, local) refined_ty
5874 | (_, Lvar lvar) ->
5875 set_local env lvar refined_ty
5876 | _ -> env
5878 and string2 env idl =
5879 let env, tel =
5880 List.fold_left idl ~init:(env,[]) ~f:begin fun (env,tel) x ->
5881 let env, te, ty = expr env x in
5882 let p = fst x in
5883 let env = SubType.sub_string p env ty in
5884 env, te::tel
5885 end in
5886 env, List.rev tel
5888 (* If the current class inherits from classes that take type arguments, we need
5889 * to check that the arguments provided are consistent with the constraints on
5890 * the type parameters. *)
5891 and check_implements_tparaml (env: Env.env) ht =
5892 let _r, (p, c), paraml = TUtils.unwrap_class_type ht in
5893 let class_ = Decl_env.get_class_dep env.Env.decl_env c in
5894 match class_ with
5895 | None ->
5896 (* The class lives in PHP land *)
5898 | Some class_ ->
5899 let size1 = List.length class_.dc_tparams in
5900 let size2 = List.length paraml in
5901 if size1 <> size2 then Errors.class_arity p class_.dc_pos c size1;
5902 let subst = Inst.make_subst class_.dc_tparams paraml in
5903 iter2_shortest begin fun (_, (p, _), cstrl) ty ->
5904 List.iter cstrl begin fun (ck, cstr) ->
5905 (* Constraint might contain uses of generic type parameters *)
5906 let cstr = Inst.instantiate subst cstr in
5907 match ck with
5908 | Ast.Constraint_as ->
5909 Type.sub_type_decl p Reason.URnone env ty cstr
5910 | Ast.Constraint_eq ->
5911 (* This code could well be unreachable, because we don't allow
5912 * equality constraints on class generics. *)
5913 Type.sub_type_decl p Reason.URnone env ty cstr;
5914 Type.sub_type_decl p Reason.URnone env cstr ty
5915 | Ast.Constraint_super ->
5916 Type.sub_type_decl p Reason.URnone env cstr ty
5918 end class_.dc_tparams paraml
5920 (* In order to type-check a class, we need to know what "parent"
5921 * refers to. Sometimes people write "parent::", when that happens,
5922 * we need to know the type of parent.
5924 and class_def_parent env class_def class_type =
5925 match class_def.c_extends with
5926 | (_, Happly ((_, x), _) as parent_ty) :: _ ->
5927 let parent_type = Decl_env.get_class_dep env.Env.decl_env x in
5928 (match parent_type with
5929 | Some parent_type -> check_parent class_def class_type parent_type
5930 | None -> ());
5931 let parent_ty = Decl_hint.hint env.Env.decl_env parent_ty in
5932 env, Some x, parent_ty
5933 (* The only case where we have more than one parent class is when
5934 * dealing with interfaces and interfaces cannot use parent.
5936 | _ :: _
5937 | _ -> env, None, (Reason.Rnone, Typing_utils.tany env)
5939 and check_parent class_def class_type parent_type =
5940 let position = fst class_def.c_name in
5941 if class_type.tc_const && not parent_type.dc_const
5942 then Errors.self_const_parent_not position;
5943 if parent_type.dc_const && not class_type.tc_const
5944 then Errors.parent_const_self_not position;
5945 (* Are all the parents in Hack? Do we know all their methods?
5946 * If so, let's check that the abstract methods have been implemented.
5948 if class_type.tc_members_fully_known
5949 then check_parent_abstract position parent_type class_type;
5950 if parent_type.dc_final
5951 then Errors.extend_final position parent_type.dc_pos parent_type.dc_name
5952 else ()
5954 and check_parent_abstract position parent_type class_type =
5955 let is_final = class_type.tc_final in
5956 if parent_type.dc_kind = Ast.Cabstract &&
5957 (class_type.tc_kind <> Ast.Cabstract || is_final)
5958 then begin
5959 check_extend_abstract_meth ~is_final position class_type.tc_methods;
5960 check_extend_abstract_meth ~is_final position class_type.tc_smethods;
5961 check_extend_abstract_const ~is_final position class_type.tc_consts;
5962 check_extend_abstract_typeconst
5963 ~is_final position class_type.tc_typeconsts;
5964 end else ()
5966 and class_def tcopt c =
5967 let env = EnvFromDef.class_env tcopt c in
5968 let tc = Env.get_class env (snd c.c_name) in
5969 add_decl_errors (Option.(map tc (fun tc -> value_exn tc.tc_decl_errors)));
5970 let c = TNBody.class_meth_bodies tcopt c in
5971 if not !auto_complete then begin
5972 NastCheck.class_ env c;
5973 NastInitCheck.class_ env c;
5974 end;
5975 match tc with
5976 | None ->
5977 (* This can happen if there was an error during the declaration
5978 * of the class. *)
5979 None
5980 | Some tc ->
5981 Typing_requirements.check_class env tc;
5982 Some (class_def_ env c tc)
5984 and class_def_ env c tc =
5985 Typing_hooks.dispatch_enter_class_def_hook c tc;
5986 let env = Env.set_mode env c.c_mode in
5987 let pc, _ = c.c_name in
5988 let impl = List.map
5989 (c.c_extends @ c.c_implements @ c.c_uses)
5990 (Decl_hint.hint env.Env.decl_env) in
5991 TI.check_tparams_instantiable env (fst c.c_tparams);
5992 let env, constraints =
5993 Phase.localize_generic_parameters_with_bounds env (fst c.c_tparams)
5994 ~ety_env:(Phase.env_with_self env) in
5995 let env = add_constraints (fst c.c_name) env constraints in
5996 Typing_variance.class_ (Env.get_options env) (snd c.c_name) tc impl;
5997 List.iter impl (check_implements_tparaml env);
5999 let env, parent_id, parent = class_def_parent env c tc in
6000 let is_final = tc.tc_final in
6001 if (tc.tc_kind = Ast.Cnormal || is_final) && tc.tc_members_fully_known
6002 then begin
6003 check_extend_abstract_meth ~is_final pc tc.tc_methods;
6004 check_extend_abstract_meth ~is_final pc tc.tc_smethods;
6005 check_extend_abstract_const ~is_final pc tc.tc_consts;
6006 check_extend_abstract_typeconst ~is_final pc tc.tc_typeconsts;
6007 end;
6008 let env = Env.set_parent env parent in
6009 let env = match parent_id with
6010 | None -> env
6011 | Some parent_id -> Env.set_parent_id env parent_id in
6012 if tc.tc_final then begin
6013 match c.c_kind with
6014 | Ast.Cinterface -> Errors.interface_final (fst c.c_name)
6015 | Ast.Cabstract -> ()
6016 | Ast.Ctrait -> Errors.trait_final (fst c.c_name)
6017 | Ast.Cenum ->
6018 Errors.internal_error pc "The parser should not parse final on enums"
6019 | Ast.Cnormal -> ()
6020 end;
6021 SMap.iter (check_static_method tc.tc_methods) tc.tc_smethods;
6022 List.iter impl (class_implements_type env c);
6023 if tc.tc_is_disposable
6024 then List.iter (c.c_extends @ c.c_uses) (Typing_disposable.enforce_is_disposable env);
6025 let typed_vars = List.map c.c_vars (class_var_def env ~is_static:false c) in
6026 let typed_methods = List.map c.c_methods (method_def env) in
6027 let typed_typeconsts = List.map c.c_typeconsts (typeconst_def env) in
6028 let typed_consts, const_types =
6029 List.unzip (List.map c.c_consts (class_const_def env)) in
6030 let env = Typing_enum.enum_class_check env tc c.c_consts const_types in
6031 let typed_constructor = class_constr_def env c in
6032 let env = Env.set_static env in
6033 let typed_static_vars =
6034 List.map c.c_static_vars (class_var_def env ~is_static:true c) in
6035 let typed_static_methods = List.map c.c_static_methods (method_def env) in
6036 Typing_hooks.dispatch_exit_class_def_hook c tc;
6038 T.c_annotation = save_env env;
6039 T.c_mode = c.c_mode;
6040 T.c_final = c.c_final;
6041 T.c_is_xhp = c.c_is_xhp;
6042 T.c_kind = c.c_kind;
6043 T.c_name = c.c_name;
6044 T.c_tparams = c.c_tparams;
6045 T.c_extends = c.c_extends;
6046 T.c_uses = c.c_uses;
6047 T.c_xhp_attr_uses = c.c_xhp_attr_uses;
6048 T.c_xhp_category = c.c_xhp_category;
6049 T.c_req_extends = c.c_req_extends;
6050 T.c_req_implements = c.c_req_implements;
6051 T.c_implements = c.c_implements;
6052 T.c_consts = typed_consts;
6053 T.c_typeconsts = typed_typeconsts;
6054 T.c_static_vars = typed_static_vars;
6055 T.c_vars = typed_vars;
6056 T.c_constructor = typed_constructor;
6057 T.c_static_methods = typed_static_methods;
6058 T.c_methods = typed_methods;
6059 T.c_user_attributes = List.map c.c_user_attributes (user_attribute env);
6060 T.c_enum = c.c_enum;
6063 and check_static_method obj method_name static_method =
6064 if SMap.mem method_name obj
6065 then begin
6066 let lazy (static_method_reason, _) = static_method.ce_type in
6067 let dyn_method = SMap.find_unsafe method_name obj in
6068 let lazy (dyn_method_reason, _) = dyn_method.ce_type in
6069 Errors.static_dynamic
6070 (Reason.to_pos static_method_reason)
6071 (Reason.to_pos dyn_method_reason)
6072 method_name
6074 else ()
6076 and check_extend_abstract_meth ~is_final p smap =
6077 SMap.iter begin fun x ce ->
6078 match ce.ce_type with
6079 | lazy (r, Tfun { ft_abstract = true; _ }) ->
6080 Errors.implement_abstract ~is_final p (Reason.to_pos r) "method" x
6081 | _ -> ()
6082 end smap
6084 (* Type constants must be bound to a concrete type for non-abstract classes.
6086 and check_extend_abstract_typeconst ~is_final p smap =
6087 SMap.iter begin fun x tc ->
6088 if tc.ttc_type = None then
6089 Errors.implement_abstract ~is_final p (fst tc.ttc_name) "type constant" x
6090 end smap
6092 and check_extend_abstract_const ~is_final p smap =
6093 SMap.iter begin fun x cc ->
6094 match cc.cc_type with
6095 | r, _ when cc.cc_abstract && not cc.cc_synthesized ->
6096 Errors.implement_abstract ~is_final p (Reason.to_pos r) "constant" x
6097 | _,
6099 Terr
6100 | Tdynamic
6101 | Tany
6102 | Tmixed
6103 | Tnonnull
6104 | Tarray (_, _)
6105 | Tdarray (_, _)
6106 | Tvarray _
6107 | Tvarray_or_darray _
6108 | Toption _
6109 | Tprim _
6110 | Tfun _
6111 | Tapply (_, _)
6112 | Ttuple _
6113 | Tshape _
6114 | Taccess (_, _)
6115 | Tthis
6116 | Tgeneric _
6117 ) -> ()
6118 end smap
6120 and typeconst_def env {
6121 c_tconst_name = (pos, _) as id;
6122 c_tconst_constraint;
6123 c_tconst_type;
6125 let env, cstr = opt Phase.hint_locl env c_tconst_constraint in
6126 let env, ty = opt Phase.hint_locl env c_tconst_type in
6127 ignore (
6128 Option.map2 ty cstr ~f:(Type.sub_type pos Reason.URtypeconst_cstr env)
6131 T.c_tconst_name = id;
6132 T.c_tconst_constraint = c_tconst_constraint;
6133 T.c_tconst_type = c_tconst_type;
6136 and class_const_def env (h, id, e) =
6137 let env, ty, opt_expected =
6138 match h with
6139 | None -> env, Env.fresh_type(), None
6140 | Some h ->
6141 let env, ty = Phase.hint_locl env h in
6142 env, ty, Some (fst id, Reason.URhint, ty)
6144 match e with
6145 | Some e ->
6146 let env, te, ty' = expr ?expected:opt_expected env e in
6147 ignore (Type.sub_type (fst id) Reason.URhint env ty' ty);
6148 (h, id, Some te), ty'
6149 | None ->
6150 (h, id, None), ty
6152 and class_constr_def env c =
6153 let env = { env with Env.inside_constructor = true } in
6154 Option.map c.c_constructor (method_def env)
6156 and class_implements_type env c1 ctype2 =
6157 let params =
6158 List.map (fst c1.c_tparams) begin fun (_, (p, s), _) ->
6159 (Reason.Rwitness p, Tgeneric s)
6160 end in
6161 let r = Reason.Rwitness (fst c1.c_name) in
6162 let ctype1 = r, Tapply (c1.c_name, params) in
6163 Typing_extends.check_implements env ctype2 ctype1;
6166 (* Type-check a property declaration, with optional initializer *)
6167 and class_var_def env ~is_static c cv =
6168 (* First pick up and localize the hint if it exists *)
6169 let env, expected =
6170 match cv.cv_type with
6171 | None ->
6172 env, None
6173 | Some (p, _ as cty) ->
6174 let env =
6175 (* If this is an XHP attribute and we're in strict mode,
6176 relax to partial mode to allow the use of the "array"
6177 annotation without specifying type parameters. Until
6178 recently HHVM did not allow "array" with type parameters
6179 in XHP attribute declarations, so this is a temporary
6180 hack to support existing code for now. *)
6181 (* Task #5815945: Get rid of this Hack *)
6182 if cv.cv_is_xhp && (Env.is_strict env)
6183 then Env.set_mode env FileInfo.Mpartial
6184 else env in
6185 let cty = TI.instantiable_hint env cty in
6186 let env, cty = Phase.localize_with_self env cty in
6187 env, Some (p, Reason.URhint, cty) in
6188 (* Next check the expression, passing in expected type if present *)
6189 let env, typed_cv_expr, ty =
6190 match cv.cv_expr with
6191 | None -> env, None, Env.fresh_type()
6192 | Some e ->
6193 let env, te, ty = expr ?expected env e in
6194 (* Check that the inferred type is a subtype of the expected type.
6195 * Eventually this will be the responsibility of `expr`
6197 let env =
6198 match expected with
6199 | None -> env
6200 | Some (p, ur, cty) -> Type.sub_type p ur env ty cty in
6201 env, Some te, ty in
6202 begin
6203 if Option.is_none cv.cv_type
6204 then begin
6205 if Env.is_strict env
6206 then Errors.add_a_typehint (fst cv.cv_id)
6207 else
6208 let pos, name = cv.cv_id in
6209 let name = if is_static then "$" ^ name else name in
6210 let var_type = Reason.Rwitness pos, Typing_utils.tany env in
6211 if Option.is_none cv.cv_expr
6212 then Typing_suggest.uninitialized_member (snd c.c_name) name env var_type ty
6213 else Typing_suggest.save_member name env var_type ty
6214 end;
6216 T.cv_final = cv.cv_final;
6217 T.cv_is_xhp = cv.cv_is_xhp;
6218 T.cv_visibility = cv.cv_visibility;
6219 T.cv_type = cv.cv_type;
6220 T.cv_id = cv.cv_id;
6221 T.cv_expr = typed_cv_expr;
6222 T.cv_user_attributes = List.map cv.cv_user_attributes (user_attribute env);
6226 and localize_where_constraints
6227 ~ety_env (env:Env.env) (where_constraints:Nast.where_constraint list) =
6228 let add_constraint env (h1, ck, h2) =
6229 let env, ty1 =
6230 Phase.localize env (Decl_hint.hint env.Env.decl_env h1) ~ety_env in
6231 let env, ty2 =
6232 Phase.localize env (Decl_hint.hint env.Env.decl_env h2) ~ety_env in
6233 SubType.add_constraint (fst h1) env ck ty1 ty2
6235 List.fold_left where_constraints ~f:add_constraint ~init:env
6237 and add_constraints p env constraints =
6238 let add_constraint env (ty1, ck, ty2) =
6239 SubType.add_constraint p env ck ty1 ty2 in
6240 List.fold_left constraints ~f:add_constraint ~init: env
6242 and user_attribute env ua =
6243 let typed_ua_params =
6244 List.map ua.ua_params (fun e -> let _env, te, _ty = expr env e in te) in
6246 T.ua_name = ua.ua_name;
6247 T.ua_params = typed_ua_params;
6250 and method_def env m =
6251 (* reset the expression dependent display ids for each method body *)
6252 Reason.expr_display_id_map := IMap.empty;
6253 Typing_hooks.dispatch_enter_method_def_hook m;
6254 let pos = fst m.m_name in
6255 let env =
6256 Env.env_with_locals env Typing_continuations.Map.empty Local_id.Map.empty
6258 let env = Env.set_env_function_pos env pos in
6259 let reactive = Decl.fun_reactivity env.Env.decl_env m.m_user_attributes in
6260 let mut = TUtils.fun_mutable m.m_user_attributes in
6261 let env = Env.set_env_reactive env reactive in
6262 let env = Env.set_fun_mutable env mut in
6263 let ety_env =
6264 { (Phase.env_with_self env) with from_class = Some CIstatic; } in
6265 let env, constraints =
6266 Phase.localize_generic_parameters_with_bounds env m.m_tparams
6267 ~ety_env:ety_env in
6268 TI.check_tparams_instantiable env m.m_tparams;
6269 let env = add_constraints pos env constraints in
6270 let env =
6271 localize_where_constraints ~ety_env env m.m_where_constraints in
6272 let env =
6273 if Env.is_static env then env
6274 else Env.set_local env this (Env.get_self env) in
6275 let env =
6276 match Env.get_class env (Env.get_self_id env) with
6277 | None -> env
6278 | Some c ->
6279 (* Mark $this as a using variable if it has a disposable type *)
6280 if c.tc_is_disposable
6281 then Env.set_using_var env this
6282 else env in
6283 let env = Env.clear_params env in
6284 let env, ty = match m.m_ret with
6285 | None ->
6286 env, Typing_return.make_default_return env m.m_name
6287 | Some ret ->
6288 let ret = TI.instantiable_hint env ret in
6289 (* If a 'this' type appears it needs to be compatible with the
6290 * late static type
6292 let ety_env =
6293 { (Phase.env_with_self env) with
6294 from_class = Some CIstatic } in
6295 Phase.localize ~ety_env env ret in
6296 let return = Typing_return.make_info m.m_fun_kind m.m_user_attributes env
6297 ~is_explicit:(Option.is_some m.m_ret) ~is_by_ref:m.m_ret_by_ref ty in
6298 TI.check_params_instantiable env m.m_params;
6299 let env, param_tys = List.map_env env m.m_params make_param_local_ty in
6300 if Env.is_strict env then begin
6301 List.iter2_exn ~f:(check_param env) m.m_params param_tys;
6302 end;
6303 Typing_memoize.check_method env m;
6304 let env, typed_params =
6305 List.map_env env (List.zip_exn param_tys m.m_params) bind_param in
6306 let env, t_variadic = match m.m_variadic with
6307 | FVvariadicArg vparam ->
6308 TI.check_param_instantiable env vparam;
6309 let env, ty = make_param_local_ty env vparam in
6310 if Env.is_strict env then
6311 check_param env vparam ty;
6312 let env, t_variadic = bind_param env (ty, vparam) in
6313 env, (T.FVvariadicArg t_variadic)
6314 | FVellipsis -> env, T.FVellipsis
6315 | FVnonVariadic -> env, T.FVnonVariadic in
6316 let nb = Nast.assert_named_body m.m_body in
6317 let env, tb =
6318 fun_ ~abstract:m.m_abstract env return pos nb m.m_fun_kind in
6319 let env =
6320 Env.check_todo env in
6321 let m_ret =
6322 match m.m_ret with
6323 | None when
6324 snd m.m_name = SN.Members.__destruct
6325 || snd m.m_name = SN.Members.__construct ->
6326 Some (pos, Happly((pos, "void"), []))
6327 | None when Env.is_strict env ->
6328 Typing_return.suggest_return env pos return.Typing_env_return_info.return_type; None
6329 | None -> let (pos, id) = m.m_name in
6330 let id = (Env.get_self_id env) ^ "::" ^ id in
6331 Typing_suggest.save_fun_or_method (pos, id);
6332 m.m_ret
6333 | Some hint ->
6334 Typing_return.async_suggest_return (m.m_fun_kind) hint (fst m.m_name); m.m_ret in
6335 Env.log_anonymous env;
6336 let m = { m with m_ret = m_ret; } in
6337 Typing_hooks.dispatch_exit_method_def_hook m;
6339 T.m_annotation = save_env env;
6340 T.m_final = m.m_final;
6341 T.m_abstract = m.m_abstract;
6342 T.m_visibility = m.m_visibility;
6343 T.m_name = m.m_name;
6344 T.m_tparams = m.m_tparams;
6345 T.m_where_constraints = m.m_where_constraints;
6346 T.m_variadic = t_variadic;
6347 T.m_params = typed_params;
6348 T.m_fun_kind = m.m_fun_kind;
6349 T.m_user_attributes = List.map m.m_user_attributes (user_attribute env);
6350 T.m_ret = m.m_ret;
6351 T.m_body = T.NamedBody {
6352 T.fnb_nast = tb;
6353 T.fnb_unsafe = nb.fnb_unsafe;
6355 T.m_ret_by_ref = m.m_ret_by_ref;
6358 and typedef_def tcopt typedef =
6359 let env = EnvFromDef.typedef_env tcopt typedef in
6360 let tdecl = Env.get_typedef env (snd typedef.t_name) in
6361 add_decl_errors (Option.(map tdecl (fun tdecl -> value_exn tdecl.td_decl_errors)));
6362 let env = Typing_env.set_mode env typedef.t_mode in
6363 let env, constraints =
6364 Phase.localize_generic_parameters_with_bounds env typedef.t_tparams
6365 ~ety_env:(Phase.env_with_self env) in
6366 let env = add_constraints (fst typedef.t_name) env constraints in
6367 NastCheck.typedef env typedef;
6368 let {
6369 t_annotation = ();
6370 t_name = t_pos, _;
6371 t_tparams = _;
6372 t_constraint = tcstr;
6373 t_kind = hint;
6374 t_user_attributes = _;
6375 t_vis = _;
6376 t_mode = _;
6377 } = typedef in
6378 let ty = TI.instantiable_hint env hint in
6379 let env, ty = Phase.localize_with_self env ty in
6380 let env = begin match tcstr with
6381 | Some tcstr ->
6382 let cstr = TI.instantiable_hint env tcstr in
6383 let env, cstr = Phase.localize_with_self env cstr in
6384 Typing_ops.sub_type t_pos Reason.URnewtype_cstr env ty cstr
6385 | _ -> env
6386 end in
6387 let env = begin match hint with
6388 | pos, Hshape { nsi_allows_unknown_fields=_; nsi_field_map } ->
6389 check_shape_keys_validity env pos (ShapeMap.keys nsi_field_map)
6390 | _ -> env
6391 end in
6393 T.t_annotation = save_env env;
6394 T.t_name = typedef.t_name;
6395 T.t_mode = typedef.t_mode;
6396 T.t_vis = typedef.t_vis;
6397 T.t_user_attributes = List.map typedef.t_user_attributes (user_attribute env);
6398 T.t_constraint = typedef.t_constraint;
6399 T.t_kind = typedef.t_kind;
6400 T.t_tparams = typedef.t_tparams;
6403 and gconst_def tcopt cst =
6404 Typing_hooks.dispatch_global_const_hook cst.cst_name;
6405 let env = EnvFromDef.gconst_env tcopt cst in
6406 let env = Typing_env.set_mode env cst.cst_mode in
6407 add_decl_errors (Option.map (Env.get_gconst env (snd cst.cst_name)) ~f:snd);
6409 let typed_cst_value, env =
6410 match cst.cst_value with
6411 | None -> None, env
6412 | Some value ->
6413 match cst.cst_type with
6414 | Some hint ->
6415 let ty = TI.instantiable_hint env hint in
6416 let env, dty = Phase.localize_with_self env ty in
6417 let env, te, value_type =
6418 expr ~expected:(fst hint, Reason.URhint, dty) env value in
6419 let env = Typing_utils.sub_type env value_type dty in
6420 Some te, env
6421 | None ->
6422 let env, te, _value_type = expr env value in
6423 Some te, env
6425 { T.cst_annotation = save_env env;
6426 T.cst_mode = cst.cst_mode;
6427 T.cst_name = cst.cst_name;
6428 T.cst_type = cst.cst_type;
6429 T.cst_value = typed_cst_value;
6430 T.cst_is_define = cst.cst_is_define;
6433 (* Calls the method of a class, but allows the f callback to override the
6434 * return value type *)
6435 and overload_function p env ((), class_id) method_id el uel f =
6436 let env, _ce, ty = static_class_id p env class_id in
6437 let env, _tel, _ = exprs ~is_func_arg:true env el in
6438 let env, fty, _ =
6439 class_get ~is_method:true ~is_const:false env ty method_id class_id in
6440 (* call the function as declared to validate arity and input types,
6441 but ignore the result and overwrite with custom one *)
6442 let (env, res), has_error = Errors.try_with_error
6443 (* TODO: Should we be passing hints here *)
6444 (fun () -> (let env, _, _, ty =
6445 call ~expected:None p env fty el uel in env, ty), false)
6446 (fun () -> (env, (Reason.Rwitness p, Typing_utils.tany env)), true) in
6447 (* if there are errors already stop here - going forward would
6448 * report them twice *)
6449 if has_error then env, T.make_typed_expr p res T.Any, res
6450 else let env, ty = f env fty res el in
6451 (* TODO TAST: do this right *)
6452 env, T.make_typed_expr p ty T.Any, ty
6454 and update_array_type ?lhs_of_null_coalesce p env e1 e2 valkind =
6455 let access_type = Typing_arrays.static_array_access env e2 in
6456 let type_mapper =
6457 Typing_arrays.update_array_type p access_type in
6458 match valkind with
6459 | `lvalue | `lvalue_subexpr ->
6460 let env, te1, ty1 =
6461 raw_expr ~valkind:`lvalue_subexpr ~in_cond:false env e1 in
6462 let env, ty1 = type_mapper env ty1 in
6463 begin match e1 with
6464 | (_, Lvar (_, x)) ->
6465 (* type_mapper has updated the type in ty1 typevars, but we
6466 need to update the local variable type too *)
6467 let env, ty1 = set_valid_rvalue p env x ty1 in
6468 env, te1, ty1
6469 | _ -> env, te1, ty1
6471 | _ ->
6472 raw_expr ~in_cond:false ?lhs_of_null_coalesce env e1
6474 let nast_to_tast opts nast =
6475 let convert_def = function
6476 | Nast.Fun f -> T.Fun (fun_def opts f)
6477 | Nast.Constant gc -> T.Constant (gconst_def opts gc)
6478 | Nast.Typedef td -> T.Typedef (typedef_def opts td)
6479 | Nast.Class c ->
6480 match class_def opts c with
6481 | Some c -> T.Class c
6482 | None -> failwith @@ Printf.sprintf
6483 "Error in declaration of class: %s" (snd c.c_name)
6485 List.map nast convert_def