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