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