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