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