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