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