2 * Copyright (c) 2015, Facebook, Inc.
5 * This source code is licensed under the BSD-style license found in the
6 * LICENSE file in the "hack" directory of this source tree. An additional grant
7 * of patent rights can be found in the PATENTS file in the same directory.
11 (* This module implements the typing.
13 * Given an Nast.program, it infers the type of all the local
14 * variables, and checks that all the types are correct (aka
23 module TUtils
= Typing_utils
24 module Reason
= Typing_reason
25 module Inst
= Decl_instantiate
26 module Type
= Typing_ops
27 module Env
= Typing_env
28 module LEnv
= Typing_lenv
29 module Dep
= Typing_deps.Dep
30 module Async
= Typing_async
31 module SubType
= Typing_subtype
32 module Unify
= Typing_unify
33 module TGen
= Typing_generic
34 module SN
= Naming_special_names
35 module TI
= Typing_instantiability
36 module TVis
= Typing_visibility
37 module TNBody
= Typing_naming_body
38 module TS
= Typing_structure
40 module Phase
= Typing_phase
41 module Subst
= Decl_subst
42 module ExprDepTy
= Typing_dependent_type.ExprDepTy
43 module TCO
= TypecheckerOptions
44 module EnvFromDef
= Typing_env_from_def.EnvFromDef
(Nast.Annotations
)
45 module TySet
= Typing_set
46 module IsAsExprHint
= TUtils.IsAsExprHint
48 (*****************************************************************************)
50 (*****************************************************************************)
52 (* A guess as to the last position we were typechecking, for use in debugging,
53 * such as figuring out what a runaway hh_server thread is doing. Updated
54 * only best-effort -- it's an approximation to point debugging in the right
55 * direction, nothing more. *)
56 let debug_last_pos = ref Pos.none
57 let debug_print_last_pos _
= print_endline
(Pos.string (Pos.to_absolute
60 (****************************************************************************)
62 (****************************************************************************)
64 let expr_hook = ref None
66 let with_expr_hook hook f
= with_context
67 ~enter
: (fun () -> expr_hook := Some hook
)
68 ~exit
: (fun () -> expr_hook := None
)
71 (*****************************************************************************)
73 (*****************************************************************************)
75 let suggest env p ty
=
76 let ty = Typing_expand.fully_expand env
ty in
77 (match Typing_print.suggest ty with
78 | "..." -> Errors.expecting_type_hint p
79 | ty -> Errors.expecting_type_hint_suggest p
ty
82 let err_witness env p
= Reason.Rwitness p
, Typing_utils.terr env
84 (* When typing an expression, we optionally pass in the type
85 * that we *expect* the expression to have.
88 Pos.t
* Reason.ureason
* locl
ty
90 let expr_error env p r
=
91 let ty = (r
, Typing_utils.terr env
) in
92 env
, T.make_typed_expr p
ty T.Any
, ty
94 let expr_any env p r
=
95 let ty = (r
, Typing_utils.tany env
) in
96 env
, T.make_typed_expr p
ty T.Any
, ty
98 let compare_field_kinds x y
=
100 | Nast.AFvalue
(p1
, _
), Nast.AFkvalue
((p2
, _
), _
)
101 | Nast.AFkvalue
((p2
, _
), _
), Nast.AFvalue
(p1
, _
) ->
102 Errors.field_kinds p1 p2
;
107 let check_consistent_fields x l
=
108 List.for_all l
(compare_field_kinds x
)
110 let unbound_name env
(pos
, name
) =
111 match Env.get_mode env
with
112 | FileInfo.Mstrict
->
113 (Errors.unbound_name_typing pos name
;
114 expr_error env pos
(Reason.Rwitness pos
))
116 | FileInfo.Mdecl
| FileInfo.Mpartial
| FileInfo.Mphp
->
117 expr_any env pos
(Reason.Rwitness pos
)
119 (* Is this type Traversable<vty> or Container<vty> for some vty? *)
120 let get_value_collection_inst ty =
122 | (_
, Tclass
((_
, c
), [vty
])) when
123 c
= SN.Collections.cTraversable
||
124 c
= SN.Collections.cContainer
->
129 (* Is this type KeyedTraversable<kty,vty>
130 * or KeyedContainer<kty,vty>
131 * or Indexish<kty,vty>
134 let get_key_value_collection_inst ty =
136 | (_
, Tclass
((_
, c
), [kty
; vty
])) when
137 c
= SN.Collections.cKeyedTraversable
||
138 c
= SN.Collections.cKeyedContainer
||
139 c
= SN.Collections.cIndexish
->
144 (* Is this type varray<vty> or a supertype for some vty? *)
145 let get_varray_inst ty =
147 (* It's varray<vty> *)
148 | (_
, Tarraykind
(AKvarray vty
)) -> Some vty
149 | _
-> get_value_collection_inst ty
151 (* Is this type one of the value collection types with element type vty? *)
152 let get_vc_inst vc_kind
ty =
154 | (_
, Tclass
((_
, c
), [vty
]))
155 when c
= vc_kind_to_name vc_kind
-> Some vty
156 | _
-> get_value_collection_inst ty
158 (* Is this type array<vty> or a supertype for some vty? *)
159 let get_akvec_inst ty =
161 | (_
, Tarraykind
(AKvec vty
)) -> Some vty
162 | _
-> get_value_collection_inst ty
164 (* Is this type array<kty,vty> or a supertype for some kty and vty? *)
165 let get_akmap_inst ty =
167 | (_
, Tarraykind
(AKmap
(kty
, vty
))) -> Some
(kty
, vty
)
168 | _
-> get_key_value_collection_inst ty
170 (* Is this type one of the three key-value collection types
171 * e.g. dict<kty,vty> or a supertype for some kty and vty? *)
172 let get_kvc_inst kvc_kind
ty =
174 | (_
, Tclass
((_
, c
), [kty
; vty
]))
175 when c
= kvc_kind_to_name kvc_kind
-> Some
(kty
, vty
)
176 | _
-> get_key_value_collection_inst ty
178 (* Is this type darray<kty, vty> or a supertype for some kty and vty? *)
179 let get_darray_inst ty =
181 (* It's darray<kty, vty> *)
182 | (_
, Tarraykind
(AKdarray
(kty
, vty
))) -> Some
(kty
, vty
)
183 | _
-> get_key_value_collection_inst ty
186 (* Try running function on each concrete supertype in turn. Return all
189 let try_over_concrete_supertypes env
ty f
=
190 let env, tyl
= TUtils.get_concrete_supertypes
env ty in
191 (* If there is just a single result then don't swallow errors *)
196 let rec iter_over_types env resl tyl
=
201 (fun () -> iter_over_types env (f
env ty::resl
) tyl
)
202 (fun _
-> iter_over_types env resl tyl
) in
203 iter_over_types env [] tyl
206 T.{tcopt
= Env.get_tcopt
env; tenv
= env.Env.tenv
; subst
= env.Env.subst
}
209 (*****************************************************************************)
210 (* Handling function/method arguments *)
211 (*****************************************************************************)
214 let has_accept_disposable_attribute param
=
215 List.exists param
.param_user_attributes
216 (fun { ua_name
; _
} -> SN.UserAttributes.uaAcceptDisposable
= snd ua_name
)
218 let has_mutable_attribute param
=
219 List.exists param
.param_user_attributes
220 (fun { ua_name
; _
} -> SN.UserAttributes.uaMutable
= snd ua_name
)
222 (* Check whether this is a function type that (a) either returns a disposable
223 * or (b) has the <<__ReturnDisposable>> attribute
225 let is_return_disposable_fun_type env ty =
226 match Env.expand_type
env ty with
227 | _env
, (_
, Tfun ft
) ->
228 ft
.ft_return_disposable
|| Option.is_some
(Typing_disposable.is_disposable_type
env ft
.ft_ret
)
231 let enforce_param_not_disposable env param
ty =
232 if has_accept_disposable_attribute param
then ()
234 let p = param
.param_pos
in
235 match Typing_disposable.is_disposable_type
env ty with
237 Errors.invalid_disposable_hint
p (strip_ns class_name
)
241 (* This function is used to determine the type of an argument.
242 * When we want to type-check the body of a function, we need to
243 * introduce the type of the arguments of the function in the environment
244 * Let's take an example, we want to check the code of foo:
246 * function foo(int $x): int {
247 * // CALL TO make_param_type on (int $x)
248 * // Now we know that the type of $x is int
250 * return $x; // in the environment $x is an int, the code is correct
253 * When we localize, we want to resolve to "static" or "$this" depending on
254 * the context. Even though we are passing in CIstatic, resolve_with_class_id
255 * is smart enough to know what to do. Why do this? Consider the following
258 * abstract const type T;
260 * private this::T $val;
262 * final public function __construct(this::T $x) {
266 * public static function create(this::T $x): this {
267 * return new static($x);
271 * class D extends C { const type T = int; }
273 * In __construct() we want to be able to assign $x to $this->val. The type of
274 * $this->val will expand to '$this::T', so we need $x to also be '$this::T'.
275 * We can do this soundly because when we construct a new class such as,
276 * 'new D(0)' we can determine the late static bound type (D) and resolve
277 * 'this::T' to 'D::T' which is int.
279 * A similar line of reasoning is applied for the static method create.
281 let make_param_local_ty env param
=
283 { (Phase.env_with_self
env) with from_class
= Some CIstatic
; } in
285 match param
.param_hint
with
286 | None
when param
.param_expr
= None
->
287 let r = Reason.Rwitness param
.param_pos
in
288 env, (r, TUtils.tany
env)
290 (* if the type is missing, use an unbound type variable *)
291 let _r, ty = Env.fresh_type
() in
292 let r = Reason.Rwitness param
.param_pos
in
295 let ty = Decl_hint.hint
env.Env.decl_env x
in
296 Phase.localize ~
ety_env env ty
298 let ty = match ty with
299 | _
, t
when param
.param_is_variadic
->
300 (* when checking the body of a function with a variadic
301 * argument, "f(C ...$args)", $args is a varray<C> *)
302 let r = Reason.Rvar_param param
.param_pos
in
303 let arr_values = r, t
in
305 if TypecheckerOptions.experimental_feature_enabled
306 (Env.get_options
env)
307 TypecheckerOptions.experimental_darray_and_varray
308 then AKvarray
arr_values
309 else AKvec
arr_values in
315 (* Given a localized parameter type and parameter information, infer
316 * a type for the parameter default expression (if present) and check that
317 * it is a subtype of the parameter type. Set the type of the parameter in
318 * the locals environment *)
319 let rec bind_param env (ty1
, param
) =
320 let env, param_te
, ty2
=
321 match param
.param_expr
with
323 (* XXX: We don't want to replace this Tany with Tdynamic, since it
324 represents the lack of a default parameter, which is valid
325 We really want a bottom type here rather than Tany, but this is fine
327 env, None
, (Reason.none
, Tany
)
329 let env, te
, ty = expr ~expected
:(param
.param_pos
, Reason.URparam
, ty1
) env e
in
330 Typing_sequencing.sequence_check_expr e
;
333 Typing_suggest.save_param
(param
.param_name
) env ty1 ty2
;
334 let env = Type.sub_type param
.param_pos
Reason.URhint
env ty2 ty1
in
336 T.param_annotation
= T.make_expr_annotation param
.param_pos ty1
;
337 T.param_hint
= param
.param_hint
;
338 T.param_is_reference
= param
.param_is_reference
;
339 T.param_is_variadic
= param
.param_is_variadic
;
340 T.param_pos
= param
.param_pos
;
341 T.param_name
= param
.param_name
;
342 T.param_expr
= param_te
;
343 T.param_callconv
= param
.param_callconv
;
344 T.param_user_attributes
= List.map param
.param_user_attributes
(user_attribute
env);
346 let mode = get_param_mode param
.param_is_reference param
.param_callconv
in
347 let id = Local_id.get param
.param_name
in
348 let env = Env.set_local
env id ty1
in
349 let env = Env.set_param
env id (ty1
, mode) in
350 let env = if has_accept_disposable_attribute param
351 then Env.set_using_var
env id else env in
352 let env = if has_mutable_attribute param
353 then Env.add_mutable_var
env id (param
.param_pos
, Typing_mutability_env.Borrowed
)
357 (* In strict mode, we force you to give a type declaration on a parameter *)
358 (* But the type checker is nice: it makes a suggestion :-) *)
359 and check_param
env param
ty =
360 match param
.param_hint
with
361 | None
-> suggest env param
.param_pos
ty
363 (* We do not permit hints to implement IDisposable or IAsyncDisposable *)
364 enforce_param_not_disposable env param
ty
366 and check_inout_return
env =
367 let params = Local_id.Map.elements
(Env.get_params
env) in
368 List.fold
params ~init
:env ~f
:begin fun env (id, ((r, ty), mode)) ->
371 (* Whenever the function exits normally, we require that each local
372 * corresponding to an inout parameter be compatible with the original
373 * type for the parameter (under subtyping rules). *)
374 let local_ty = Env.get_local
env id in
375 let env, ety
= Env.expand_type
env local_ty in
376 let pos = Reason.to_pos
(fst ety
) in
377 let param_ty = Reason.Rinout_param
(Reason.to_pos
r), ty in
378 Type.sub_type
pos Reason.URassign_inout
env ety
param_ty
382 and add_decl_errors
= function
384 | Some errors
-> Errors.merge_into_current errors
386 (*****************************************************************************)
387 (* Now we are actually checking stuff! *)
388 (*****************************************************************************)
389 and fun_def tcopt f
=
390 (* reset the expression dependent display ids for each function body *)
391 Reason.expr_display_id_map
:= IMap.empty
;
392 Typing_hooks.dispatch_enter_fun_def_hook f
;
393 let pos = fst f
.f_name
in
394 let nb = TNBody.func_body tcopt f
in
395 let env = EnvFromDef.fun_env tcopt f
in
396 add_decl_errors
(Option.map
397 (Env.get_fun
env (snd f
.f_name
))
398 ~f
:(fun x
-> Option.value_exn x
.ft_decl_errors
)
400 let env = Env.set_env_function_pos
env pos in
401 let reactive = Decl.fun_reactivity
env.Env.decl_env f
.f_user_attributes
in
402 let mut = TUtils.fun_mutable f
.f_user_attributes
in
403 let env = Env.set_env_reactive
env reactive in
404 let env = Env.set_fun_mutable
env mut in
405 NastCheck.fun_
env f
nb;
406 (* Fresh type environment is actually unnecessary, but I prefer to
407 * have a guarantee that we are using a clean typing environment. *)
408 let tfun_def = Env.fresh_tenv
env (
410 let env = Env.set_mode
env f
.f_mode
in
411 let env, constraints
=
412 Phase.localize_generic_parameters_with_bounds
env f
.f_tparams
413 ~
ety_env:(Phase.env_with_self
env) in
414 let env = add_constraints
pos env constraints
in
416 localize_where_constraints
417 ~
ety_env:(Phase.env_with_self
env) env f
.f_where_constraints
in
421 env, (Reason.Rwitness
pos, Typing_utils.tany
env)
423 let ty = TI.instantiable_hint
env ret
in
424 Phase.localize_with_self
env ty in
425 let return = Typing_return.make_info f
.f_fun_kind f
.f_user_attributes
env
426 ~is_explicit
:(Option.is_some f
.f_ret
) ~is_by_ref
:f
.f_ret_by_ref
ty in
427 TI.check_params_instantiable
env f
.f_params
;
428 TI.check_tparams_instantiable
env f
.f_tparams
;
429 let env, param_tys
= List.map_env
env f
.f_params
make_param_local_ty in
430 if Env.is_strict
env then
431 List.iter2_exn ~f
:(check_param
env) f
.f_params param_tys
;
432 Typing_memoize.check_function
env f
;
433 let env, typed_params
= List.map_env
env (List.zip_exn param_tys f
.f_params
)
435 let env, t_variadic
= match f
.f_variadic
with
436 | FVvariadicArg vparam
->
437 TI.check_param_instantiable
env vparam
;
438 let env, ty = make_param_local_ty env vparam
in
439 if Env.is_strict
env then
440 check_param
env vparam
ty;
441 let env, t_vparam
= bind_param env (ty, vparam
) in
442 env, T.FVvariadicArg t_vparam
444 if Env.is_strict
env then
445 Errors.ellipsis_strict_mode ~require
:`Type_and_param_name
pos;
447 | FVnonVariadic
-> env, T.FVnonVariadic
in
448 let env, tb
= fun_
env return pos nb f
.f_fun_kind
in
449 let env = Env.check_todo
env in
450 if Env.is_strict
env then Env.log_anonymous
env;
451 begin match f
.f_ret
with
452 | None
when Env.is_strict
env ->
453 Typing_return.suggest_return
env pos return.Typing_env_return_info.return_type
454 | None
-> Typing_suggest.save_fun_or_method f
.f_name
456 Typing_return.async_suggest_return
(f
.f_fun_kind
) hint
pos
459 T.f_annotation
= save_env env;
463 T.f_tparams
= f
.f_tparams
;
464 T.f_where_constraints
= f
.f_where_constraints
;
465 T.f_variadic
= t_variadic
;
466 T.f_params
= typed_params
;
467 T.f_fun_kind
= f
.f_fun_kind
;
468 T.f_user_attributes
= List.map f
.f_user_attributes
(user_attribute
env);
469 T.f_body
= T.NamedBody
{
471 T.fnb_unsafe
= nb.fnb_unsafe
;
473 T.f_ret_by_ref
= f
.f_ret_by_ref
;
476 Typing_hooks.dispatch_exit_fun_def_hook f
;
479 (*****************************************************************************)
480 (* function used to type closures, functions and methods *)
481 (*****************************************************************************)
483 and fun_ ?
(abstract
=false) env return pos named_body f_kind
=
484 Env.with_env
env begin fun env ->
485 debug_last_pos := pos;
486 let env = Env.set_return
env return in
487 let env = Env.set_fn_kind
env f_kind
in
488 let env, tb
= block
env named_body
.fnb_nast
in
489 Typing_sequencing.sequence_check_block named_body
.fnb_nast
;
490 let { Typing_env_return_info.return_type
= ret
; _
} = Env.get_return
env in
492 if Nast_terminality.Terminal.block
env named_body
.fnb_nast
||
494 named_body
.fnb_unsafe
||
497 else fun_implicit_return
env pos ret f_kind
in
498 debug_last_pos := Pos.none
;
502 and fun_implicit_return
env pos ret
= function
503 | Ast.FGenerator
| Ast.FAsyncGenerator
-> env
506 (* A function without a terminal block has an implicit return; the
508 let env = check_inout_return
env in
509 let rty = Reason.Rno_return
pos, Tprim
Nast.Tvoid
in
510 Typing_suggest.save_return
env ret
rty;
511 Type.sub_type
pos Reason.URreturn
env rty ret
513 (* An async function without a terminal block has an implicit return;
514 * the Awaitable<void> type *)
515 let r = Reason.Rno_return_async
pos in
516 let rty = r, Tclass
((pos, SN.Classes.cAwaitable
), [r, Tprim
Nast.Tvoid
]) in
517 Typing_suggest.save_return
env ret
rty;
518 Type.sub_type
pos Reason.URreturn
env rty ret
521 List.map_env
env stl stmt
523 (* Set a local; must not be already assigned if it is a using variable *)
524 and set_local ?
(is_using_clause
= false) env (pos,x
) ty =
525 if Env.is_using_var
env x
528 then Errors.duplicate_using_var
pos
529 else Errors.illegal_disposable
pos "assigned";
530 let env = Env.set_local
env x
ty in
531 if is_using_clause
then Env.set_using_var
env x
else env
533 (* Check an individual component in the expression `e` in the
534 * `using (e) { ... }` statement.
535 * This consists of either
536 * a simple assignment `$x = e`, in which `$x` is the using variable, or
537 * an arbitrary expression `e`, in which case a temporary is the using
538 * variable, inaccessible in the source.
539 * Return the typed expression and its type, and any variables that must
540 * be designated as "using variables" for avoiding escapes.
542 and check_using_expr has_await
env ((pos, content
) as using_clause
) =
544 (* Simple assignment to local of form `$lvar = e` *)
545 | Binop
(Ast.Eq None
, (lvar_pos
, Lvar lvar
), e
) ->
546 let env, te
, ty = expr ~is_using_clause
:true env e
in
547 let env = Typing_disposable.enforce_is_disposable_type
env has_await
(fst e
) ty in
548 let env = set_local ~is_using_clause
:true env lvar
ty in
549 (* We are assigning a new value to the local variable, so we need to
550 * generate a new expression id
552 let env = Env.set_local_expr_id
env (snd lvar
) (Ident.tmp
()) in
553 env, (T.make_typed_expr
pos ty (T.Binop
(Ast.Eq None
,
554 T.make_typed_expr lvar_pos
ty (T.Lvar lvar
), te
)), [snd lvar
])
556 (* Arbitrary expression. This will be assigned to a temporary *)
558 let env, typed_using_clause
, ty = expr ~is_using_clause
:true env using_clause
in
559 let env = Typing_disposable.enforce_is_disposable_type
env has_await
pos ty in
560 env, (typed_using_clause
, [])
562 (* Check the using clause e in
563 * `using (e) { ... }` statement (`has_await = false`) or
564 * `await using (e) { ... }` statement (`has_await = true`).
565 * The expression consists of a comma-separated list of expressions (Expr_list)
566 * or a single expression.
567 * Return the typed expression, and any variables that must
568 * be designated as "using variables" for avoiding escapes.
570 and check_using_clause
env has_await
((pos, content
) as using_clause
) =
572 | Expr_list using_clauses
->
573 let env, pairs
= List.map_env
env using_clauses
(check_using_expr has_await
) in
574 let typed_using_clauses, vars_list
= List.unzip pairs
in
575 env, T.make_implicitly_typed_expr
pos (T.Expr_list
typed_using_clauses),
576 List.concat vars_list
578 let env, (typed_using_clause
, vars
) = check_using_expr has_await
env using_clause
in
579 env, typed_using_clause
, vars
581 (* Require a new construct with disposable *)
582 and enforce_return_disposable _env e
=
587 Errors.invalid_return_disposable
p
589 and enforce_mutable_return
env e
=
590 Typing_mutability.verify_valid_mutable_return_value
env env.Env.function_pos e
592 and stmt
env = function
600 let env, te
, ty = expr
env e
in
601 (* NB: this check does belong here and not in expr, even though it only
602 * applies to expressions -- we actually want to perform the check on
603 * statements that are expressions, e.g., "foo();" we want to check, but
604 * "return foo();" we do not even though the expression "foo()" is a
605 * subexpression of the statement "return foo();". *)
607 | Nast.Binop
(Ast.Eq _
, _
, _
) -> ()
608 | _
-> Async.enforce_not_awaitable
env (fst e
) ty);
611 let env, te
, _
= expr
env e
in
612 (* We stash away the locals environment because condition updates it
613 * locally for checking b1. For example, we might have condition
614 * $x === null, or $x instanceof C, which changes the type of $x in
616 let parent_lenv = env.Env.lenv
in
617 let env = condition
env true e
in
618 let env, tb1
= block
env b1
in
619 let lenv1 = env.Env.lenv
in
620 let env = { env with Env.lenv
= parent_lenv } in
621 let env = condition
env false e
in
622 let env, tb2
= block
env b2
in
623 let lenv2 = env.Env.lenv
in
624 let terminal1 = Nast_terminality.Terminal.block
env b1
in
625 let terminal2 = Nast_terminality.Terminal.block
env b2
in
627 if terminal1 && terminal2
629 let env = LEnv.integrate
env parent_lenv lenv1 in
630 let env = LEnv.integrate
env env.Env.lenv
lenv2 in
631 LEnv.integrate
env env.Env.lenv
parent_lenv
634 let env = LEnv.integrate
env parent_lenv lenv1 in
635 LEnv.integrate
env env.Env.lenv
lenv2
639 let env = LEnv.integrate
env parent_lenv lenv2 in
640 LEnv.integrate
env env.Env.lenv
lenv1
642 else LEnv.intersect
env parent_lenv lenv1 lenv2 in
643 (* TODO TAST: annotate with joined types *)
644 env, T.If
(te
, tb1
, tb2
)
645 | Return
(p, None
) ->
646 let env = check_inout_return
env in
647 let rty = Typing_return.wrap_awaitable
env p (Reason.Rwitness
p, Tprim Tvoid
) in
648 let { Typing_env_return_info.return_type
= expected_return
; _
} = Env.get_return
env in
649 Typing_suggest.save_return
env expected_return
rty;
650 let env = Type.sub_type
p Reason.URreturn
env rty expected_return
in
651 env, T.Return
(p, None
)
652 | Return
(p, Some e
) ->
653 let env = check_inout_return
env in
655 let Typing_env_return_info.{
656 return_type
; return_disposable
; return_mutable
; return_explicit
; return_by_ref
} =
657 Env.get_return
env in
660 then Some
(pos, Reason.URreturn
,
661 Typing_return.strip_awaitable
(Env.get_fn_kind
env) env return_type
)
662 else Some
(pos, Reason.URreturn
, (Reason.Rwitness
p, Typing_utils.tany
env)) in
663 if return_disposable
then enforce_return_disposable
env e
;
664 let env, te
, rty = expr ~is_using_clause
:return_disposable ?
expected:expected env e
in
665 if return_mutable
then enforce_mutable_return
env te
;
666 if return_by_ref
&& TypecheckerOptions.experimental_feature_enabled
667 (Env.get_options
env)
668 TypecheckerOptions.experimental_disallow_refs_in_array
669 then begin match snd e
with
670 | Array_get _
-> Errors.return_ref_in_array
p
673 let rty = Typing_return.wrap_awaitable
env p rty in
674 (match snd
(Env.expand_type
env return_type
) with
676 (* Yell about returning a value from a void function. This catches
677 * more issues than just unifying with void would do -- in particular
678 * just unifying allows you to return a Typing_utils.tany env from a void function,
679 * which is clearly wrong. Note this check is best-effort; if the
680 * function returns a generic type which later ends up being Tvoid
681 * then there's not much we can do here. *)
682 Errors.return_in_void
p (Reason.to_pos
r);
683 env, T.Return
(p, Some te
)
684 | _
, Tunresolved _
->
685 (* we allow return types to grow for anonymous functions *)
686 let env, rty = TUtils.unresolved
env rty in
687 let env = Type.sub_type
pos Reason.URreturn
env rty return_type
in
688 env, T.Return
(p, Some te
)
689 | _
, (Terr
| Tany
| Tmixed
| Tnonnull
| Tarraykind _
| Toption _
| Tprim _
690 | Tvar _
| Tfun _
| Tabstract
(_
, _
) | Tclass
(_
, _
) | Ttuple _
691 | Tanon
(_
, _
) | Tobject
| Tshape _
| Tdynamic
) ->
692 Typing_suggest.save_return
env return_type
rty;
693 let env = Type.sub_type
pos Reason.URreturn
env rty return_type
in
694 env, T.Return
(p, Some te
)
697 (* NOTE: leaks scope as currently implemented; this matches
698 the behavior in naming (cf. `do_stmt` in naming/naming.ml).
700 let parent_lenv = env.Env.lenv
in
701 let env = Env.freeze_local_env
env in
702 let env, _
= block
env b
in
703 let env, te
, _
= expr
env e
in
704 let after_block = env.Env.lenv
in
706 if env.Env.in_loop
then 1 else Typing_alias.get_depth st
in
707 let env, tb
= Env.in_loop
env begin
708 iter_n_acc
alias_depth begin fun env ->
709 let env = condition
env true e
in
710 let env, tb
= block
env b
in
714 if Nast.Visitor.HasContinue.block b
715 then LEnv.fully_integrate
env parent_lenv
716 else { env with Env.lenv
= after_block } in
717 let env = condition
env false e
in
719 | While
(e
, b
) as st
->
720 let env, te
, _
= expr
env e
in
721 let parent_lenv = env.Env.lenv
in
722 let env = Env.freeze_local_env
env in
724 if env.Env.in_loop
then 1 else Typing_alias.get_depth st
in
725 let env, tb
= Env.in_loop
env begin
726 iter_n_acc
alias_depth begin fun env ->
727 let env = condition
env true e
in
728 (* TODO TAST: avoid repeated generation of block *)
729 let env, tb
= block
env b
in
733 let env = LEnv.fully_integrate
env parent_lenv in
734 let env = condition
env false e
in
735 env, T.While
(te
, tb
)
736 | Using
(has_await
, using_clause
, using_block
) ->
737 let env, typed_using_clause
, using_vars
= check_using_clause
env has_await using_clause
in
738 let env, typed_using_block
= block
env using_block
in
739 (* Remove any using variables from the environment, as they should not
740 * be in scope outside the block *)
741 let env = List.fold_left using_vars ~init
:env ~f
:Env.unset_local
in
742 env, T.Using
(has_await
, typed_using_clause
, typed_using_block
)
743 | For
(e1
, e2
, e3
, b
) as st
->
744 (* For loops leak their initalizer, but nothing that's defined in the
747 let (env, te1
, _
) = expr
env e1
in (* initializer *)
748 let (env, te2
, _
) = expr
env e2
in
749 let parent_lenv = env.Env.lenv
in
750 let env = Env.freeze_local_env
env in
752 if env.Env.in_loop
then 1 else Typing_alias.get_depth st
in
753 let env, (tb
, te3
) = Env.in_loop
env begin
754 iter_n_acc
alias_depth begin fun env ->
755 let env = condition
env true e2
in (* iteration 0 *)
756 let env, tb
= block
env b
in
757 let (env, te3
, _
) = expr
env e3
in
761 let env = LEnv.fully_integrate
env parent_lenv in
762 let env = condition
env false e2
in
763 env, T.For
(te1
, te2
, te3
, tb
)
765 let cl = List.map ~f
:drop_dead_code_after_break
cl in
766 Nast_terminality.SafeCase.check
(fst e
) env cl;
767 let env, te
, ty = expr
env e
in
768 Async.enforce_not_awaitable
env (fst e
) ty;
769 let env = check_exhaustiveness
env (fst e
) ty cl in
770 let parent_lenv = env.Env.lenv
in
771 let env, cl, tcl
= case_list
parent_lenv ty env cl in
772 let env = LEnv.intersect_nonterminal_branches
env parent_lenv cl in
773 env, T.Switch
(te
, tcl
)
774 | Foreach
(e1
, e2
, b
) as st
->
775 let check_dynamic env ty ~f
=
776 if TUtils.is_dynamic
env ty then
779 (* It's safe to do foreach over a disposable, as no leaking is possible *)
780 let env, te1
, ty1
= expr ~accept_using_var
:true env e1
in
781 let parent_lenv = env.Env.lenv
in
782 let env = Env.freeze_local_env
env in
783 let env, ty2
= as_expr
env (fst e1
) e2
in
785 check_dynamic env ty1 ~f
:begin fun () ->
786 Type.sub_type
(fst e1
) Reason.URforeach
env ty1 ty2
789 if env.Env.in_loop
then 1 else Typing_alias.get_depth st
in
790 let env, (te2
, tb
) = Env.in_loop
env begin
791 iter_n_acc
alias_depth begin fun env ->
792 let env, te2
= bind_as_expr
env ty1 ty2 e2
in
793 let env, tb
= block
env b
in
797 let env = LEnv.fully_integrate
env parent_lenv in
798 env, T.Foreach
(te1
, te2
, tb
)
799 | Try
(tb
, cl, fb
) ->
800 let env, ttb
, tcl
, tfb
= try_catch
env tb
cl fb
in
801 env, T.Try
(ttb
, tcl
, tfb
)
803 Typing_reactivity.disallow_static_or_global_in_reactive_context
env el
805 let env = List.fold_left el ~f
:begin fun env e
->
807 | _
, Binop
(Ast.Eq _
, (_
, Lvar
(p, x
)), _
) ->
808 Env.add_todo
env (TGen.no_generic
p x
)
811 let env, tel
, _
= exprs
env el
in
812 env, T.Static_var tel
814 Typing_reactivity.disallow_static_or_global_in_reactive_context
env el
816 let env = List.fold_left el ~f
:begin fun env e
->
818 | _
, Binop
(Ast.Eq _
, (_
, Lvar
(p, x
)), _
) ->
819 Env.add_todo
env (TGen.no_generic
p x
)
822 let env, tel
, _
= exprs
env el
in
823 env, T.Global_var tel
824 | Throw
(is_terminal
, e
) ->
826 let env, te
, ty = expr
env e
in
827 let env = exception_ty
p env ty in
828 env, T.Throw
(is_terminal
, te
)
834 and check_exhaustiveness
env pos ty caselist
=
835 check_exhaustiveness_
env pos ty caselist
false
837 and check_exhaustiveness_
env pos ty caselist enum_coming_from_unresolved
=
838 (* Right now we only do exhaustiveness checking for enums. *)
839 (* This function has a built in hack where if Tunresolved has an enum
840 inside then it tells the enum exhaustiveness checker to
841 not punish for extra default *)
842 let env, (_
, ty) = Env.expand_type
env ty in
845 let new_enum = enum_coming_from_unresolved
||
846 (List.length tyl
> 1 && List.exists tyl ~f
:begin fun cur_ty
->
847 let _, (_, cur_ty
) = Env.expand_type
env cur_ty
in
849 | Tabstract
(AKenum
_, _) -> true
852 List.fold_left tyl ~init
:env ~f
:begin fun env ty ->
853 check_exhaustiveness_
env pos ty caselist
new_enum
855 | Tabstract
(AKenum
id, _) ->
856 let dep = Dep.AllMembers
id in
857 Option.iter
env.Env.decl_env
.Decl_env.droot
858 (fun root
-> Typing_deps.add_idep root
dep);
859 let tc = unsafe_opt
@@ Env.get_enum
env id in
860 Typing_enum.check_enum_exhaustiveness
pos tc
861 caselist enum_coming_from_unresolved
;
863 | Terr
| Tany
| Tmixed
| Tnonnull
| Tarraykind
_ | Tclass
_ | Toption
_
864 | Tprim
_ | Tvar
_ | Tfun
_ | Tabstract
(_, _) | Ttuple
_ | Tanon
(_, _)
865 | Tobject
| Tshape
_ | Tdynamic
-> env
867 and case_list
parent_lenv ty env cl =
868 let env = { env with Env.lenv
= parent_lenv } in
869 case_list_
parent_lenv ty env cl
871 and try_catch
env tb
cl fb
=
872 let unfrozen_parent_lenv = env.Env.lenv
in
873 let env = Env.freeze_local_env
env in
874 let parent_lenv = env.Env.lenv
in
875 let env, ttb
= block
env tb
in
876 let after_try = env.Env.lenv
in
877 let env, term_lenv_tcb_l
= List.map_env
env cl
878 begin fun env (_, _, b
as catch_block
) ->
879 let env, lenv
, tcb
= catch
parent_lenv after_try env catch_block
in
880 let term = Nast_terminality.Terminal.block
env b
in
881 env, (term, lenv
, tcb
)
883 let term_lenv_l = List.map term_lenv_tcb_l
(fun (a
, b
, _) -> (a
, b
)) in
884 let tcb_l = List.map term_lenv_tcb_l
(fun (_, _, a
) -> a
) in
886 (Nast_terminality.Terminal.block
env tb
, after_try) :: term_lenv_l in
887 let env, tfb
= if List.is_empty fb
then env, [] else begin
888 let after_catchl = env.Env.lenv
in
889 let lenv_l = List.map
term_lenv_l (fun (_, a
) -> a
) in
890 let env = LEnv.integrate_list
env parent_lenv lenv_l in
891 let env = LEnv.fully_integrate
env parent_lenv in
892 let env, tfb
= block
env fb
in
893 { env with Env.lenv
= after_catchl }, tfb
895 let env = LEnv.intersect_nonterminal_branches
env parent_lenv term_lenv_l in
896 let env, _ = block
env fb
in
897 let after_finally = env.Env.lenv
in
898 let env = LEnv.integrate
env after_finally unfrozen_parent_lenv in
899 let env = LEnv.integrate
env unfrozen_parent_lenv after_finally in
902 and drop_dead_code_after_break_block
= function
904 | Break x
:: _ -> [Break x
], true
908 | If
(_, [], []) as if_stmt
-> if_stmt
, false
909 | If
(cond
, b1
, b2
) ->
910 let b1, drop1
= if b1 = [] then [], true
911 else drop_dead_code_after_break_block
b1 in
912 let b2, drop2
= if b2 = [] then [], true
913 else drop_dead_code_after_break_block
b2 in
914 If
(cond
, b1, b2), drop1
&& drop2
917 if drop
then ([x'
], true) else begin
918 let rest'
, drop
= drop_dead_code_after_break_block
rest in
922 and drop_dead_code_after_break
= function
923 | Default b
-> Default
(fst
(drop_dead_code_after_break_block b
))
924 | Case
(e
, b
) -> Case
(e
, fst
(drop_dead_code_after_break_block b
))
926 and case_list_
parent_lenv ty env = function
929 (* TODO this is wrong, should continue on to the other cases, but it
930 * doesn't matter in practice since our parser won't parse default
931 * anywhere but in the last position :) Should fix all of this as well
932 * as totality detection for switch. *)
933 let env, tb
= block
env b
in
934 env, [Nast_terminality.Terminal.case
env (Default b
), env.Env.lenv
],
936 | (Case
(e
, b
)) as ce
:: rl
->
937 (* TODO - we should consider handling the comparisons the same
938 * way as Binop Ast.EqEq, since case statements work using ==
939 * comparison rules *)
941 (* The way we handle terminal/nonterminal here is not quite right, you
942 * can still break the type system with things like P3131824. *)
943 let ty_num = (Reason.Rnone
, Tprim
Nast.Tnum
) in
944 let ty_arraykey = (Reason.Rnone
, Tprim
Nast.Tarraykey
) in
945 let both_are_sub_types env tprim ty1 ty2
=
946 (SubType.is_sub_type
env ty1 tprim
) &&
947 (SubType.is_sub_type
env ty2 tprim
) in
948 let env, te
, ty2
= expr
env e
in
949 let env, _ = if (both_are_sub_types env ty_num ty ty2
) ||
950 (both_are_sub_types env ty_arraykey ty ty2
)
952 else Type.unify
(fst e
) Reason.URnone
env ty ty2
in
954 if Nast_terminality.Terminal.block
env b
then
955 let env, tb
= block
env b
in
956 let lenv = env.Env.lenv in
957 let env, rl
, tcl
= case_list
parent_lenv ty env rl
in
958 env, (Nast_terminality.Terminal.case
env ce
, lenv) :: rl
, T.Case
(te
, tb
)::tcl
960 (* Since this block is not terminal we will end up falling through to the
961 * next block. This means the lenv will include what our current
962 * environment is, intersected (or integrated?) with the environment
963 * after executing the block. Example:
965 * $x = 0; // $x = int
968 * $x = ''; // $x = string
971 * $x; // $x = int & string
974 let lenv1 = env.Env.lenv in
975 let env, tb
= block
env b
in
976 (* PERF: If the case is empty or a Noop then we do not need to intersect
977 * the lenv since they will be the same.
979 * This saves the cost of intersecting the lenv for the common pattern of
985 let env = match b
with
987 | _ -> LEnv.intersect
env parent_lenv lenv1 env.Env.lenv in
988 let env, rl
, tcl
= case_list_
parent_lenv ty env rl
in
989 env, rl
, T.Case
(te
, tb
)::tcl
991 and catch
parent_lenv after_try env (sid
, exn
, b
) =
992 let env = { env with Env.lenv = after_try } in
993 let env = LEnv.fully_integrate
env parent_lenv in
994 let cid = CI
(sid
, []) in
995 let ety_p = (fst sid
) in
996 TUtils.process_class_id
cid;
997 let env, _, _ = instantiable_cid
ety_p env cid in
998 let env, _te
, ety
= static_class_id
ety_p env cid in
999 let env = exception_ty
ety_p env ety
in
1000 let env = set_local
env exn ety
in
1001 let env, tb
= block
env b
in
1002 (* Only keep the local bindings if this catch is non-terminal *)
1003 env, env.Env.lenv, (sid
, exn
, tb
)
1005 and as_expr
env pe
=
1006 let make_result ty = env, ty in
1009 let ty = Env.fresh_type
() in
1010 let tvector = Tclass
((pe
, SN.Collections.cTraversable
), [ty]) in
1011 make_result (Reason.Rforeach pe
, tvector)
1013 let ty1 = Env.fresh_type
() in
1014 let ty2 = Env.fresh_type
() in
1015 let tmap = Tclass
((pe
, SN.Collections.cKeyedTraversable
), [ty1; ty2]) in
1016 make_result (Reason.Rforeach pe
, tmap)
1018 let ty = Env.fresh_type
() in
1019 let tvector = Tclass
((pe
, SN.Classes.cAsyncIterator
), [ty]) in
1020 make_result (Reason.Rasyncforeach pe
, tvector)
1022 let ty1 = Env.fresh_type
() in
1023 let ty2 = Env.fresh_type
() in
1024 let tmap = Tclass
((pe
, SN.Classes.cAsyncKeyedIterator
), [ty1; ty2]) in
1025 make_result (Reason.Rasyncforeach pe
, tmap)
1027 and bind_as_expr
env loop_ty
ty aexpr
=
1028 let env, ety
= Env.expand_type
env ty in
1031 | _, Tclass
((p, _), [ty2]) ->
1032 (p, (Reason.Rnone
, TUtils.desugar_mixed
env Reason.Rnone
), ty2)
1033 | _, Tclass
((p, _), [ty1; ty2]) -> (p, ty1, ty2)
1034 | _ -> assert false in
1035 (* Set id as dynamic if the foreach loop was dynamic *)
1036 let env, eloop_ty
= Env.expand_type
env loop_ty
in
1037 let ty1, ty2 = if TUtils.is_dynamic
env eloop_ty
then
1038 (fst
ty1, Tdynamic
), (fst
ty2, Tdynamic
) else ty1, ty2 in
1041 let env, te
, _ = assign
p env ev
ty2 in
1043 | Await_as_v
(p, ev
) ->
1044 let env, te
, _ = assign
p env ev
ty2 in
1045 env, T.Await_as_v
(p, te
)
1046 | As_kv
((p, Lvar
((_, k
) as id)), ev
) ->
1047 let env, ty1'
= set_valid_rvalue
p env k
ty1 in
1048 let env, te
, _ = assign
p env ev
ty2 in
1049 env, T.As_kv
(T.make_typed_expr
p ty1'
(T.Lvar
id), te
)
1050 | Await_as_kv
(p, (p1
, Lvar
((_, k
) as id)), ev
) ->
1051 let env, ty1'
= set_valid_rvalue
p env k
ty1 in
1052 let env, te
, _ = assign
p env ev
ty2 in
1053 env, T.Await_as_kv
(p, T.make_typed_expr p1
ty1'
(T.Lvar
id), te
)
1054 | _ -> (* TODO Probably impossible, should check that *)
1059 ?
(accept_using_var
= false)
1060 ?
(is_using_clause
= false)
1064 begin match expected with
1066 | Some
(_, r, ty) ->
1067 Typing_log.log_types
1 (fst e
) env
1068 [Typing_log.Log_sub
("Typing.expr " ^
Typing_reason.string_of_ureason
r,
1069 [Typing_log.Log_type
("expected_ty", ty)])] end;
1070 raw_expr ~accept_using_var ~is_using_clause ?is_func_arg ?forbid_uref ?
expected ~in_cond
:false env e
1074 ?
(accept_using_var
= false)
1075 ?
(is_using_clause
= false)
1077 ?lhs_of_null_coalesce
1080 ?valkind
:(valkind
=`other
)
1082 debug_last_pos := fst e
;
1084 expr_ ~in_cond ~accept_using_var ~is_using_clause ?
expected
1085 ?lhs_of_null_coalesce ?is_func_arg ?forbid_uref
1087 let () = match !expr_hook with
1088 | Some f
-> f e
(Typing_expand.fully_expand
env ty)
1093 let valkind = `lvalue
in
1094 expr_ ~in_cond
:false ~
valkind env e
1096 and is_pseudo_function s
=
1097 s
= SN.PseudoFunctions.hh_show
||
1098 s
= SN.PseudoFunctions.hh_show_env
||
1099 s
= SN.PseudoFunctions.hh_log_level
1101 (* $x ?? 0 is handled similarly to $x ?: 0, except that the latter will also
1102 * look for sketchy null checks in the condition. *)
1103 (* TODO TAST: type refinement should be made explicit in the typed AST *)
1104 and eif
env ~
expected ~coalesce ~in_cond
p c e1 e2
=
1105 let condition = condition ~lhs_of_null_coalesce
:coalesce
in
1106 let env, tc, tyc
= raw_expr ~in_cond ~lhs_of_null_coalesce
:coalesce
env c
in
1107 let parent_lenv = env.Env.lenv in
1108 let c = if coalesce
then (p, Binop
(Ast.Diff2
, c, (p, Null
))) else c in
1109 let env = condition env true c in
1110 let env, te1
, ty1 = match e1
with
1112 let env, ty = TUtils.non_null
env tyc
in
1115 let env, te1
, ty1 = expr ?
expected env e1
in
1118 let lenv1 = env.Env.lenv in
1119 let env = { env with Env.lenv = parent_lenv } in
1120 let env = condition env false c in
1121 let env, te2
, ty2 = expr ?
expected env e2
in
1122 let lenv2 = env.Env.lenv in
1124 LEnv.intersect_fake
lenv1.Env.fake_members lenv2.Env.fake_members in
1125 (* we restore the locals to their parent state so as not to leak the
1126 * effects of the `condition` calls above *)
1127 let env = { env with Env.lenv =
1128 { parent_lenv with Env.fake_members = fake_members } } in
1129 (* This is a shortened form of what we do in Typing_lenv.intersect. The
1130 * latter takes local environments as arguments, but our types here
1131 * aren't assigned to local variables in an environment *)
1132 (* TODO: Omit if expected type is present and checked in calls to expr *)
1133 let env, ty1 = TUtils.unresolved
env ty1 in
1134 let env, ty2 = TUtils.unresolved
env ty2 in
1135 let env, ty = Unify.unify
env ty1 ty2 in
1136 let te = if coalesce
then T.NullCoalesce
(tc, te2
) else T.Eif
(tc, te1
, te2
) in
1137 env, T.make_typed_expr
p ty te, ty
1139 and check_escaping_var
env (pos, x) =
1140 if Env.is_using_var
env x
1143 then Errors.escaping_this
pos
1145 if Option.is_some
(Local_id.Map.get
x (Env.get_params
env))
1146 then Errors.escaping_disposable_parameter
pos
1147 else Errors.escaping_disposable
pos
1150 and exprs ?
(accept_using_var
= false) ?is_func_arg ?
expected env el
=
1156 let env, te, ty = expr ~accept_using_var ?is_func_arg ?
expected env e
in
1157 let env, tel
, tyl
= exprs ~accept_using_var ?is_func_arg ?
expected env el
in
1158 env, te::tel
, ty::tyl
1160 and exprs_expected
(pos, ur
, expected_tyl
) env el
=
1161 match el
, expected_tyl
with
1164 | e
::el
, expected_ty
::expected_tyl
->
1165 let env, te, ty = expr ~
expected:(pos, ur
, expected_ty
) env e
in
1166 let env, tel
, tyl
= exprs_expected
(pos, ur
, expected_tyl
) env el
in
1167 env, te::tel
, ty::tyl
1174 ?
(accept_using_var
= false)
1175 ?
(is_using_clause
= false)
1176 ?lhs_of_null_coalesce
1177 ?
(is_func_arg
=false)
1178 ?
(forbid_uref
=false)
1179 ~
(valkind: [> `lvalue
| `lvalue_subexpr
| `other
])
1181 let make_result env te ty =
1182 env, T.make_typed_expr
p ty te, ty in
1185 * Given a list of types, computes their supertype. If any of the types are
1186 * unknown (e.g., comes from PHP), the supertype will be Typing_utils.tany env.
1188 let compute_supertype ~
expected env tys
=
1189 let env, supertype
=
1191 | None
-> Env.fresh_unresolved_type
env
1192 | Some
(_, _, ty) -> env, ty in
1193 let has_unknown = List.exists tys
(fun (_, ty) -> ty = Typing_utils.tany
env) in
1194 let env, tys
= List.map_env
env tys
TUtils.unresolved
in
1195 let subtype_value env ty =
1196 Type.sub_type
p Reason.URarray_value
env ty supertype
in
1198 (* If one of the values comes from PHP land, we have to be conservative
1199 * and consider that we don't know what the type of the values are. *)
1200 env, (Reason.Rwitness
p, Typing_utils.tany
env)
1202 let env = List.fold_left tys ~init
:env ~f
:subtype_value in
1206 * Given a 'a list and a method to extract an expr and its ty from a 'a, this
1207 * function extracts a list of exprs from the list, and computes the supertype
1208 * of all of the expressions' tys.
1210 let compute_exprs_and_supertype ~
expected env l extract_expr_and_ty
=
1211 let env, exprs_and_tys
= List.map_env
env l
(extract_expr_and_ty ~
expected) in
1212 let exprs, tys
= List.unzip exprs_and_tys
in
1213 let env, supertype
= compute_supertype ~
expected env tys
in
1214 env, exprs, supertype
in
1216 let shape_and_tuple_arrays_enabled =
1218 TypecheckerOptions.experimental_feature_enabled
1219 (Env.get_options
env)
1220 TypecheckerOptions.experimental_disable_shape_and_tuple_arrays
in
1222 let check_call ~is_using_clause ~
expected env p call_type e hl el uel ~in_suspend
=
1223 let env, te, result
=
1224 dispatch_call ~is_using_clause ~
expected p env call_type e hl el uel ~in_suspend
in
1225 let env = Env.forget_members
env p in
1229 | Any
-> expr_error env p (Reason.Rwitness
p)
1231 (* TODO: use expected type to determine expected element type *)
1232 make_result env (T.Array
[]) (Reason.Rwitness
p, Tarraykind AKempty
)
1234 (* TODO: use expected type to determine expected element type *)
1235 when Typing_arrays.is_shape_like_array
env l
&&
1236 shape_and_tuple_arrays_enabled ->
1237 let env, (tafl
, fdm
) = List.fold_left_env
env l
1238 ~init
:([], ShapeMap.empty
)
1239 ~f
:begin fun env (tafl
,fdm
) x ->
1240 let env, taf
, (key
, value) = akshape_field
env x in
1241 env, (taf
::tafl
, Nast.ShapeMap.add key
value fdm
)
1243 make_result env (T.Array
(List.rev tafl
))
1244 (Reason.Rwitness
p, Tarraykind
(AKshape fdm
))
1246 | Array
(x :: rl
as l
) ->
1247 (* True if all fields are values, or all fields are key => value *)
1248 let fields_consistent = check_consistent_fields x rl
in
1249 let is_vec = match x with
1250 | Nast.AFvalue
_ -> true
1251 | Nast.AFkvalue
_ -> false in
1252 if fields_consistent && is_vec then
1253 (* Use expected type to determine expected element type *)
1254 let env, elem_expected
=
1255 match expand_expected
env expected with
1256 | env, Some
(pos, ur
, ety
) ->
1257 begin match get_akvec_inst ety
with
1258 | Some vty
-> env, Some
(pos, ur
, vty
)
1263 let env, tel
, arraykind
=
1264 if shape_and_tuple_arrays_enabled then
1265 let env, tel
, fields
=
1266 List.foldi l ~f
:begin fun index
(env, tel
, acc
) e
->
1267 let env, te, ty = aktuple_field
env e
in
1268 env, te::tel
, IMap.add index
ty acc
1269 end ~init
:(env, [], IMap.empty
) in
1270 env, tel
, AKtuple fields
1272 let env, tel
, value_ty
=
1273 compute_exprs_and_supertype ~
expected:elem_expected
env l array_field_value
in
1274 env, tel
, AKvec value_ty
in
1276 (T.Array
(List.map tel
(fun e
-> T.AFvalue e
)))
1277 (Reason.Rwitness
p, Tarraykind arraykind
)
1280 (* TODO TAST: produce a typed expression here *)
1283 (* Use expected type to determine expected element type *)
1284 let env, vexpected
=
1285 match expand_expected
env expected with
1286 | env, Some
(pos, ur
, ety
) ->
1287 begin match get_akvec_inst ety
with
1288 | Some vty
-> env, Some
(pos, ur
, vty
)
1293 let env, _value_exprs
, value_ty
=
1294 compute_exprs_and_supertype ~
expected:vexpected
env l array_field_value
in
1295 make_result env T.Any
1296 (Reason.Rwitness
p, Tarraykind
(AKvec value_ty
))
1298 (* Use expected type to determine expected element type *)
1299 let env, kexpected
, vexpected
=
1300 match expand_expected
env expected with
1301 | env, Some
(pos, ur
, ety
) ->
1302 begin match get_akmap_inst ety
with
1303 | Some
(kty
, vty
) -> env, Some
(pos, ur
, kty
), Some
(pos, ur
, vty
)
1304 | None
-> env, None
, None
1308 let env, key_exprs
, key_ty
=
1309 compute_exprs_and_supertype ~
expected:kexpected
env l array_field_key
in
1310 let env, value_exprs
, value_ty
=
1311 compute_exprs_and_supertype ~
expected:vexpected
env l array_field_value
in
1313 (T.Array
(List.map
(List.zip_exn key_exprs value_exprs
)
1314 (fun (tek
, tev
) -> T.AFkvalue
(tek
, tev
))))
1315 (Reason.Rwitness
p, Tarraykind
(AKmap
(key_ty
, value_ty
)))
1318 (* Use expected type to determine expected key and value types *)
1319 let env, kexpected
, vexpected
=
1320 match expand_expected
env expected with
1321 | env, Some
(pos, ur
, ety
) ->
1322 begin match get_darray_inst ety
with
1323 | Some
(kty
, vty
) ->
1324 env, Some
(pos, ur
, kty
), Some
(pos, ur
, vty
)
1330 let keys, values
= List.unzip l
in
1332 let env, value_exprs
, value_ty
=
1333 compute_exprs_and_supertype ~
expected:vexpected
env values array_value
in
1334 let env, key_exprs
, key_ty
=
1335 compute_exprs_and_supertype ~
expected:kexpected
env keys array_value
in
1337 let field_exprs = List.zip_exn key_exprs value_exprs
in
1339 (T.Darray
field_exprs)
1340 (Reason.Rwitness
p, Tarraykind
(AKdarray
(key_ty
, value_ty
)))
1343 (* Use expected type to determine expected element type *)
1344 let env, elem_expected
=
1345 match expand_expected
env expected with
1346 | env, Some
(pos, ur
, ety
) ->
1347 begin match get_varray_inst ety
with
1349 env, Some
(pos, ur
, vty
)
1356 let env, value_exprs
, value_ty
=
1357 compute_exprs_and_supertype ~
expected:elem_expected
env values array_value
in
1359 (T.Varray value_exprs
)
1360 (Reason.Rwitness
p, Tarraykind
(AKvarray value_ty
))
1362 | ValCollection
(kind
, el
) ->
1363 (* Use expected type to determine expected element type *)
1364 let env, elem_expected
=
1365 match expand_expected
env expected with
1366 | env, Some
(pos, ur
, ety
) ->
1367 begin match get_vc_inst kind ety
with
1369 env, Some
(pos, ur
, vty
)
1374 let env, tel
, tyl
= exprs ?
expected:elem_expected
env el
in
1375 let env, tyl
= List.map_env
env tyl
Typing_env.unbind
in
1376 let env, elem_ty
, tyl
=
1377 match elem_expected
with
1378 | Some
(_, _, ty) -> env, ty, tyl
1380 let env, elem_ty
= Env.fresh_unresolved_type
env in
1381 let env, tyl
= List.map_env
env tyl
TUtils.unresolved
in
1382 env, elem_ty
, tyl
in
1383 let subtype_val env ty =
1384 Type.sub_type
p Reason.URvector
env ty elem_ty
in
1386 List.fold_left tyl ~init
:env ~f
:subtype_val in
1387 let tvector = Tclass
((p, vc_kind_to_name kind
), [elem_ty
]) in
1388 let ty = Reason.Rwitness
p, tvector in
1389 make_result env (T.ValCollection
(kind
, tel
)) ty
1390 | KeyValCollection
(kind
, l
) ->
1391 (* Use expected type to determine expected key and value types *)
1392 let env, kexpected
, vexpected
=
1393 match expand_expected
env expected with
1394 | env, Some
(pos, ur
, ety
) ->
1395 begin match get_kvc_inst kind ety
with
1396 | Some
(kty
, vty
) ->
1397 env, Some
(pos, ur
, kty
), Some
(pos, ur
, vty
)
1401 | _ -> env, None
, None
in
1402 let kl, vl
= List.unzip l
in
1403 let env, tkl
, kl = exprs ?
expected:kexpected
env kl in
1404 let env, tvl
, vl
= exprs ?
expected:vexpected
env vl
in
1405 let env, kl = List.map_env
env kl Typing_env.unbind
in
1407 match kexpected
with
1408 | Some
(_, _, k
) -> env, k
, kl
1410 let env, k
= Env.fresh_unresolved_type
env in
1411 let env, kl = List.map_env
env kl TUtils.unresolved
in
1413 let env, vl
= List.map_env
env vl
Typing_env.unbind
in
1415 match vexpected
with
1416 | Some
(_, _, v
) -> env, v
, vl
1418 let env, v
= Env.fresh_unresolved_type
env in
1419 let env, vl
= List.map_env
env vl
TUtils.unresolved
in
1421 let subtype_key env ty = Type.sub_type
p Reason.URkey
env ty k
in
1423 List.fold_left
kl ~init
:env ~f
:subtype_key in
1424 let subtype_val env ty = Type.sub_type
p Reason.URvalue
env ty v
in
1426 List.fold_left vl ~init
:env ~f
:subtype_val in
1427 let ty = Tclass
((p, kvc_kind_to_name kind
), [k
; v
])
1429 make_result env (T.KeyValCollection
(kind
, List.zip_exn tkl tvl
))
1430 (Reason.Rwitness
p, ty)
1432 let env, te, ty = expr
env e
in
1433 (* Clone only works on objects; anything else fatals at runtime *)
1434 let tobj = (Reason.Rwitness
p, Tobject
) in
1435 let env = Type.sub_type
p Reason.URclone
env ty tobj in
1436 make_result env (T.Clone
te) ty
1437 | This
when Env.is_static
env ->
1438 Errors.this_in_static
p;
1439 expr_error env p (Reason.Rwitness
p)
1440 | This
when valkind = `lvalue
->
1441 Errors.this_lvalue
p;
1442 expr_error env p (Reason.Rwitness
p)
1444 let r, _ = Env.get_self
env in
1446 then Errors.this_var_outside_class
p;
1447 if not accept_using_var
1448 then check_escaping_var
env (p,this
);
1449 let (_, ty) = Env.get_local
env this
in
1450 let r = Reason.Rwitness
p in
1452 let ty = r, TUtils.this_of
ty in
1453 (* '$this' always refers to the late bound static type *)
1454 let env, new_ty
= ExprDepTy.make
env CIstatic
ty in
1455 make_result env T.This
(new_ty
)
1456 | Assert
(AE_assert e
) ->
1457 let env, te, _ = expr
env e
in
1458 let env = condition env true e
in
1459 make_result env (T.Assert
(T.AE_assert
te)) (Reason.Rwitness
p, Tprim Tvoid
)
1461 make_result env T.True
(Reason.Rwitness
p, Tprim Tbool
)
1463 make_result env T.False
(Reason.Rwitness
p, Tprim Tbool
)
1464 (* TODO TAST: consider checking that the integer is in range. Right now
1465 * it's possible for HHVM to fail on well-typed Hack code
1468 make_result env (T.Int s
) (Reason.Rwitness
p, Tprim Tint
)
1470 make_result env (T.Float s
) (Reason.Rwitness
p, Tprim Tfloat
)
1471 (* TODO TAST: consider introducing a "null" type, and defining ?t to
1475 let ty = Env.fresh_type
() in
1476 make_result env T.Null
(Reason.Rwitness
p, Toption
ty)
1478 make_result env (T.String s
) (Reason.Rwitness
p, Tprim Tstring
)
1480 let env, tel
= string2
env idl
in
1481 make_result env (T.String2 tel
) (Reason.Rwitness
p, Tprim Tstring
)
1483 Typing_hooks.dispatch_id_hook
x env;
1484 let env, fty
= fun_type_of_id
env x [] in
1485 begin match fty
with
1486 | _, Tfun fty
-> check_deprecated
(fst
x) fty
;
1489 make_result env (T.Fun_id
x) fty
1490 | Id
((cst_pos
, cst_name
) as id) ->
1491 Typing_hooks.dispatch_id_hook
id env;
1492 Typing_hooks.dispatch_global_const_hook
id;
1493 (match Env.get_gconst
env cst_name
with
1494 | None
when Env.is_strict
env ->
1495 Errors.unbound_global cst_pos
;
1496 let ty = (Reason.Rwitness cst_pos
, Typing_utils.terr
env) in
1497 let te = T.make_implicitly_typed_expr cst_pos
(T.Id
id) in
1500 make_result env (T.Id
id) (Reason.Rwitness cst_pos
, Typing_utils.tany
env)
1502 if cst_name
= SN.HH.rx_is_enabled
1504 if Env.is_checking_lambda
()
1505 then Errors.rx_enabled_in_lambdas cst_pos
1506 else if Env.env_reactivity
env = Nonreactive
1507 then Errors.rx_enabled_in_non_rx_context cst_pos
1510 Phase.localize_with_self
env ty in
1511 make_result env (T.Id
id) ty
1513 | Method_id
(instance
, meth
) ->
1514 (* Method_id is used when creating a "method pointer" using the magic
1515 * inst_meth function.
1517 * Typing this is pretty simple, we just need to check that instance->meth
1518 * is public+not static and then return its type.
1520 Typing_hooks.dispatch_fun_id_hook
(p, "\\"^
SN.SpecialFunctions.inst_meth
);
1521 let env, te, ty1 = expr
env instance
in
1522 let env, result
, vis
=
1523 obj_get_with_visibility ~is_method
:true ~nullsafe
:None ~
valkind:`other ~pos_params
:None
env
1524 ty1 (CIexpr instance
) meth
(fun x -> x) in
1525 let has_lost_info = Env.FakeMembers.is_invalid
env instance
(snd meth
) in
1528 let name = "the method "^snd meth
in
1529 let env, result
= Env.lost_info
name env result
in
1530 make_result env (T.Method_id
(te, meth
)) result
1534 | _, Tfun fty
-> check_deprecated
p fty
1537 | Some
(method_pos
, Vprivate
_) ->
1538 Errors.private_inst_meth method_pos
p
1539 | Some
(method_pos
, Vprotected
_) ->
1540 Errors.protected_inst_meth method_pos
p
1543 make_result env (T.Method_id
(te, meth
)) result
1545 | Method_caller
((pos, class_name
) as pos_cname
, meth_name
) ->
1546 (* meth_caller('X', 'foo') desugars to:
1549 Typing_hooks.dispatch_fun_id_hook
(p, "\\"^
SN.SpecialFunctions.meth_caller
);
1550 let class_ = Env.get_class
env class_name
in
1552 | None
-> unbound_name env pos_cname
1554 (* Create a class type for the given object instantiated with unresolved
1555 * types for its type parameters.
1558 List.map_env
env class_.tc_tparams
TUtils.unresolved_tparam
in
1559 let params = List.map
class_.tc_tparams
begin fun (_, (p, n
), _) ->
1560 Reason.Rwitness
p, Tgeneric n
1562 let obj_type = Reason.Rwitness
p, Tapply
(pos_cname
, params) in
1564 (Phase.env_with_self
env) with
1565 substs
= Subst.make
class_.tc_tparams tvarl
;
1567 let env, local_obj_ty
= Phase.localize ~
ety_env env obj_type in
1569 obj_get ~is_method
:true ~nullsafe
:None
env local_obj_ty
1570 (CI
((pos, class_name
), [])) meth_name
(fun x -> x) in
1572 | reason
, Tfun fty
->
1573 check_deprecated
p fty
;
1574 (* We are creating a fake closure:
1575 * function(Class $x, arg_types_of(Class::meth_name))
1576 : return_type_of(Class::meth_name)
1579 ety_env with substs
= Subst.make
class_.tc_tparams tvarl
1582 Phase.check_tparams_constraints ~use_pos
:p ~
ety_env env class_.tc_tparams
in
1583 let env, local_obj_ty
= Phase.localize ~
ety_env env obj_type in
1584 let local_obj_fp = TUtils.default_fun_param local_obj_ty
in
1585 let fty = { fty with
1586 ft_params
= local_obj_fp :: fty.ft_params
} in
1587 let fun_arity = match fty.ft_arity
with
1588 | Fstandard
(min
, max
) -> Fstandard
(min
+ 1, max
+ 1)
1589 | Fvariadic
(min
, x) -> Fvariadic
(min
+ 1, x)
1590 | Fellipsis min
-> Fellipsis
(min
+ 1) in
1593 ft_deprecated
= None
;
1594 ft_abstract
= false;
1595 (* propagate 'is_coroutine' from the method being called*)
1596 ft_is_coroutine
= fty.ft_is_coroutine
;
1597 ft_arity
= fun_arity;
1598 ft_tparams
= fty.ft_tparams
;
1599 ft_where_constraints
= fty.ft_where_constraints
;
1600 ft_params
= fty.ft_params
;
1601 ft_ret
= fty.ft_ret
;
1602 ft_ret_by_ref
= fty.ft_ret_by_ref
;
1603 ft_reactive
= fty.ft_reactive
;
1604 ft_mutable
= fty.ft_mutable
;
1605 ft_returns_mutable
= fty.ft_returns_mutable
;
1606 ft_return_disposable
= fty.ft_return_disposable
;
1607 ft_decl_errors
= None
;
1609 make_result env (T.Method_caller
(pos_cname
, meth_name
))
1610 (reason
, Tfun
caller)
1612 (* This can happen if the method lives in PHP *)
1613 make_result env (T.Method_caller
(pos_cname
, meth_name
))
1614 (Reason.Rwitness
pos, Typing_utils.tany
env)
1617 | Smethod_id
(c, meth
) ->
1618 (* Smethod_id is used when creating a "method pointer" using the magic
1619 * class_meth function.
1621 * Typing this is pretty simple, we just need to check that c::meth is
1622 * public+static and then return its type.
1624 Typing_hooks.dispatch_fun_id_hook
(p, "\\"^
SN.SpecialFunctions.class_meth
);
1625 let class_ = Env.get_class
env (snd
c) in
1628 (* The class given as a static string was not found. *)
1631 let smethod = Env.get_static_member
true env class_ (snd meth
) in
1633 | None
-> (* The static method wasn't found. *)
1634 smember_not_found
p ~is_const
:false ~is_method
:true class_ (snd meth
);
1635 expr_error env p Reason.Rnone
1636 | Some
{ ce_type
= lazy ty; ce_visibility
; _ } ->
1637 let cid = CI
(c, []) in
1638 let env, _te
, cid_ty
= static_class_id
(fst
c) env cid in
1640 type_expansions
= [];
1641 substs
= SMap.empty
;
1643 from_class
= Some
cid;
1648 let env, ft
= Phase.localize_ft ~use_pos
:p ~
ety_env env ft
in
1649 let ty = r, Tfun ft
in
1650 check_deprecated
p ft
;
1651 match ce_visibility
with
1653 make_result env (T.Smethod_id
(c, meth
)) ty
1655 Errors.private_class_meth
(Reason.to_pos
r) p;
1658 Errors.protected_class_meth
(Reason.to_pos
r) p;
1662 Errors.internal_error
p "We have a method which isn't callable";
1667 let r = Reason.Rplaceholder
p in
1668 let ty = r, Tprim Tvoid
in
1669 make_result env (T.Lplaceholder
p) ty
1670 | Dollardollar
_ when valkind = `lvalue
->
1671 Errors.dollardollar_lvalue
p;
1672 expr_error env p (Reason.Rwitness
p)
1673 | Dollardollar
((_, x) as id) ->
1674 let ty = Env.get_local
env x in
1675 make_result env (T.Dollardollar
id) ty
1676 | Lvar
((_, x) as id) ->
1677 Typing_hooks.dispatch_lvar_hook
id env;
1678 let local_id = Local_id.to_string
x in
1679 if SN.Superglobals.is_superglobal
local_id
1680 then Env.error_if_reactive_context
env @@ begin fun () ->
1681 Errors.superglobal_in_reactive_context
p local_id;
1683 if not accept_using_var
1684 then check_escaping_var
env id;
1685 let ty = Env.get_local
env x in
1686 make_result env (T.Lvar
id) ty
1688 let env, te, _ty
= expr
env e
in
1689 (** Can't easily track any typing information for variable variable. *)
1690 make_result env (T.Dollar
te) (Reason.Rwitness
p, Typing_utils.tany
env)
1692 let env, expected = expand_expected
env expected in
1695 | Some
(pos, ur
, (_, Ttuple expected_tyl
)) ->
1696 exprs_expected
(pos, ur
, expected_tyl
) env el
1700 (* TODO TAST: figure out role of unbind here *)
1701 let env, tyl
= List.map_env
env tyl
Typing_env.unbind
in
1702 let ty = Reason.Rwitness
p, Ttuple tyl
in
1703 make_result env (T.List tel
) ty
1705 (* Use expected type to determine expected element types *)
1706 let env, expected1
, expected2
=
1707 match expand_expected
env expected with
1708 | env, Some
(pos, ur
, (_, Tclass
((_, k
), [ty1; ty2]))) when k
= SN.Collections.cPair
->
1709 env, Some
(pos, ur
, ty1), Some
(pos, ur
, ty2)
1710 | _ -> env, None
, None
in
1711 let env, te1
, ty1 = expr ?
expected:expected1
env e1
in
1712 let env, ty1 = Typing_env.unbind
env ty1 in
1713 let env, te2
, ty2 = expr ?
expected:expected2
env e2
in
1714 let env, ty2 = Typing_env.unbind
env ty2 in
1716 Reason.Rwitness
p, Tclass
((p, SN.Collections.cPair
), [ty1; ty2]) in
1717 make_result env (T.Pair
(te1
, te2
)) ty
1719 (* TODO: use expected type to determine tuple component types *)
1720 let env, tel
, tyl
= exprs env el
in
1721 let ty = Reason.Rwitness
p, Ttuple tyl
in
1722 make_result env (T.Expr_list tel
) ty
1723 | Array_get
(e
, None
) ->
1724 let env, te1
, ty1 = update_array_type
p env e None
valkind in
1725 let env, ty = array_append
p env ty1 in
1726 make_result env (T.Array_get
(te1
, None
)) ty
1727 | Array_get
(e1
, Some e2
) ->
1729 update_array_type ?lhs_of_null_coalesce
p env e1
(Some e2
) valkind in
1730 let env, ty1 = TUtils.fold_unresolved
env ty1 in
1731 let env, te2
, ty2 = expr
env e2
in
1732 let is_lvalue = (valkind == `lvalue
) in
1734 array_get ?lhs_of_null_coalesce
is_lvalue p env ty1 e2
ty2 in
1735 make_result env (T.Array_get
(te1
, Some te2
)) ty
1736 | Call
(Cnormal
, (pos_id
, Id
((_, s
) as id)), hl
, el
, [])
1737 when is_pseudo_function s
->
1738 let env, tel
, tys
= exprs ~accept_using_var
:true env el
in
1739 if s
= SN.PseudoFunctions.hh_show
1740 then List.iter tys
(Typing_log.hh_show
p env)
1742 if s
= SN.PseudoFunctions.hh_show_env
1743 then Typing_log.hh_show_env
p env
1745 if s
= SN.PseudoFunctions.hh_log_level
1747 | [(_, Int
(_, level_str
))] ->
1748 Typing_log.hh_log_level
(int_of_string level_str
)
1754 T.make_implicitly_typed_expr pos_id
(T.Id
id),
1757 [])) (Env.fresh_type
())
1758 | Call
(call_type
, e
, hl
, el
, uel
) ->
1759 let env, te, ty = check_call ~is_using_clause ~
expected env p call_type e hl el uel ~in_suspend
:false in
1760 Typing_mutability.enforce_mutable_call
env te;
1762 (* For example, e1 += e2. This is typed and translated as if
1763 * written e1 = e1 + e2.
1764 * TODO TAST: is this right? e1 will get evaluated more than once
1766 | Binop
(Ast.Eq
(Some op
), e1
, e2
) ->
1767 let e_fake = (p, Binop
(Ast.Eq None
, e1
, (p, Binop
(op
, e1
, e2
)))) in
1768 let env, te_fake
, ty = raw_expr in_cond
env e_fake in
1769 begin match snd te_fake
with
1770 | T.Binop
(_, te1
, (_, T.Binop
(_, _, te2
))) ->
1771 let te = T.Binop
(Ast.Eq
(Some op
), te1
, te2
) in
1772 make_result env te ty
1775 | Binop
(Ast.Eq None
, e1
, e2
) ->
1776 let forbid_uref = match e1
, e2
with
1777 | (_, Array_get
_), (_, Unop
(Ast.Uref
, _))
1778 | _, (_, Unop
(Ast.Uref
, (_, Array_get
_))) -> true
1780 let env, te2
, ty2 = raw_expr ~in_cond ~
forbid_uref env e2
in
1781 let env, te1
, ty = assign
p env e1
ty2 in
1783 if Env.env_local_reactive
env then
1784 Typing_mutability.handle_assignment_mutability
env te1 te2
1787 (* If we are assigning a local variable to another local variable then
1788 * the expression ID associated with e2 is transferred to e1
1791 | (_, Lvar
(_, x1
)), (_, Lvar
(_, x2
)) ->
1792 let eid2 = Env.get_local_expr_id
env x2
in
1796 ~f
:(Env.set_local_expr_id
env x1
) in
1797 make_result env (T.Binop
(Ast.Eq None
, te1
, te2
)) ty
1799 make_result env (T.Binop
(Ast.Eq None
, te1
, te2
)) ty
1801 | Binop
((Ast.AMpamp
| Ast.BArbar
as bop
), e1
, e2
) ->
1802 let c = bop
= Ast.AMpamp
in
1803 let lenv = env.Env.lenv in
1804 let env, te1
, ty1 = expr
env e1
in
1805 let env = condition env c e1
in
1806 let env, te2
, ty2 = raw_expr in_cond
env e2
in
1807 let env = { env with Env.lenv = lenv } in
1808 Typing_hooks.dispatch_binop_hook
p bop
ty1 ty2;
1809 make_result env (T.Binop
(bop
, te1
, te2
))
1810 (Reason.Rlogic_ret
p, Tprim Tbool
)
1811 | Binop
(bop
, e1
, e2
) when Env.is_strict
env
1812 && (snd e1
= Nast.Null
|| snd e2
= Nast.Null
)
1813 && (bop
= Ast.EQeqeq
|| bop
= Ast.Diff2
) ->
1814 let e, ne
= if snd e2
= Nast.Null
then e1
, e2
else e2
, e1
in
1815 let _, te, ty = raw_expr in_cond
env e in
1817 then Typing_equality_check.assert_nullable
p bop
env ty;
1818 let tne = T.make_typed_expr
(fst ne
) ty T.Null
in
1819 let te1, te2
= if snd e2
= Nast.Null
then te, tne else tne, te in
1820 make_result env (T.Binop
(bop
, te1, te2
))
1821 (Reason.Rcomp
p, Tprim Tbool
)
1822 | Binop
(bop
, e1
, e2
) ->
1823 let env, te1, ty1 = raw_expr in_cond
env e1
in
1824 let env, te2
, ty2 = raw_expr in_cond
env e2
in
1826 binop in_cond
p env bop
(fst e1
) te1 ty1 (fst e2
) te2
ty2 in
1827 Typing_hooks.dispatch_binop_hook
p bop
ty1 ty2;
1829 | Pipe
(e0
, e1
, e2
) ->
1830 let env, te1, ty = expr
env e1
in
1831 (** id is the ID of the $$ that is implicitly declared by the pipe.
1832 * Set the local type for the $$ in the RHS. *)
1833 let env = set_local
env e0
ty in
1834 let env, te2
, ty2 = expr
env e2
in
1836 * Return ty2 since the type of the pipe expression is the type of the
1839 * Note: env does have the type of this Pipe's $$, but it doesn't
1840 * override the outer one since they have different ID's.
1843 * a() |> ( inner1($$) |> inner2($$) ) + $$
1845 * The rightmost $$ refers to the result of a()
1847 make_result env (T.Pipe
(e0
, te1, te2
)) ty2
1849 let env, te, ty = raw_expr in_cond
env e in
1850 unop ~is_func_arg ~
forbid_uref p env uop
te ty
1851 | Eif
(c, e1
, e2
) -> eif
env ~
expected ~coalesce
:false ~in_cond
p c e1 e2
1852 | NullCoalesce
(e1
, e2
) -> eif
env ~
expected ~coalesce
:true ~in_cond
p e1 None e2
1854 begin match Env.get_typedef
env (snd sid
) with
1855 | Some
{td_tparams
= tparaml
; _} ->
1856 (* Typedef type parameters cannot have constraints *)
1857 let params = List.map ~f
:begin fun (_, (p, x), _) ->
1858 Reason.Rwitness
p, Tgeneric
x
1860 let tdef = Reason.Rwitness
(fst sid
), Tapply
(sid
, params) in
1862 Reason.Rwitness
p, Tapply
((p, SN.Classes.cTypename
), [tdef]) in
1863 let env, tparams
= List.map_env
env tparaml
begin fun env _ ->
1864 Env.fresh_unresolved_type
env
1866 let ety_env = { (Phase.env_with_self
env) with
1867 substs
= Subst.make tparaml tparams
} in
1868 let env = Phase.check_tparams_constraints ~use_pos
:p ~
ety_env env tparaml
in
1869 let env, ty = Phase.localize ~
ety_env env typename in
1870 make_result env (T.Typename sid
) ty
1872 (* Should never hit this case since we only construct this AST node
1873 * if in the expression Foo::class, Foo is a type def.
1875 expr_error env p (Reason.Rwitness
p)
1877 | Class_const
(cid, mid
) -> class_const
env p (cid, mid
)
1878 | Class_get
(((), x), (py
, y
))
1879 when Env.FakeMembers.get_static
env x y
<> None
->
1880 Env.error_if_reactive_context
env @@ begin fun () ->
1881 Errors.static_property_in_reactive_context
p
1883 let env, local
= Env.FakeMembers.make_static
p env x y
in
1884 let local = p, Lvar
(p, local) in
1885 let env, _, ty = expr
env local in
1886 let env, te, _ = static_class_id
p env x in
1887 make_result env (T.Class_get
(te, (py
, y
))) ty
1888 | Class_get
(((), cid), mid
) ->
1889 Env.error_if_reactive_context
env @@ begin fun () ->
1890 Errors.static_property_in_reactive_context
p
1892 TUtils.process_static_find_ref
cid mid
;
1893 let env, te, cty
= static_class_id
p env cid in
1895 class_get ~is_method
:false ~is_const
:false env cty mid
cid in
1896 if Env.FakeMembers.is_static_invalid
env cid (snd mid
)
1898 let fake_name = Env.FakeMembers.make_static_id
cid (snd mid
) in
1899 let env, ty = Env.lost_info
fake_name env ty in
1900 make_result env (T.Class_get
(te, mid
)) ty
1902 make_result env (T.Class_get
(te, mid
)) ty
1903 (* Fake member property access. For example:
1904 * if ($x->f !== null) { ...$x->f... }
1906 | Obj_get
(e, (pid
, Id
(py
, y
)), nf
)
1907 when Env.FakeMembers.get
env e y
<> None
->
1908 let env, local = Env.FakeMembers.make
p env e y
in
1909 let local = p, Lvar
(p, local) in
1910 let env, _, ty = expr
env local in
1911 let env, t_lhs
, _ = expr ~accept_using_var
:true env e in
1912 let t_rhs = T.make_typed_expr pid
ty (T.Id
(py
, y
)) in
1913 make_result env (T.Obj_get
(t_lhs
, t_rhs, nf
)) ty
1914 (* Statically-known instance property access e.g. $x->f *)
1915 | Obj_get
(e1
, (pm
, Id m
), nullflavor
) ->
1917 (match nullflavor
with
1918 | OG_nullthrows
-> None
1919 | OG_nullsafe
-> Some
p
1921 let env, te1, ty1 = expr ~accept_using_var
:true env e1
in
1923 obj_get ~is_method
:false ~
nullsafe ~
valkind env ty1 (CIexpr e1
) m
(fun x -> x) in
1924 let has_lost_info = Env.FakeMembers.is_invalid
env e1
(snd m
) in
1928 let name = "the member " ^ snd m
in
1929 Env.lost_info
name env result
1933 make_result env (T.Obj_get
(te1,
1934 T.make_implicitly_typed_expr pm
(T.Id m
), nullflavor
)) result
1935 (* Dynamic instance property access e.g. $x->$f *)
1936 | Obj_get
(e1
, e2
, nullflavor
) ->
1937 let env, te1, ty1 = expr ~accept_using_var
:true env e1
in
1938 let env, te2
, _ = expr
env e2
in
1939 let ty = if TUtils.is_dynamic
env ty1 then
1940 (Reason.Rwitness
p, Tdynamic
) else
1942 if Env.is_strict
env then
1944 Errors.dynamic_method_call
(fst e2
);
1945 (Reason.Rwitness
p, Typing_utils.terr
env)
1948 (Reason.Rwitness
p, Typing_utils.tany
env)
1950 make_result env (T.Obj_get
(te1, te2
, nullflavor
)) ty
1952 make_result env T.Yield_break
(Reason.Rwitness
p, Typing_utils.tany
env)
1954 let env, (taf
, opt_key
, value) = array_field
env af
in
1955 let send = Env.fresh_type
() in
1956 let env, key
= match af
, opt_key
with
1957 | Nast.AFvalue
(p, _), None
->
1959 match Env.get_fn_kind
env with
1963 Errors.internal_error
p "yield found in non-generator";
1964 Reason.Rwitness
p, Typing_utils.tany
env
1966 (Reason.Rwitness
p, Tprim Tint
)
1967 | Ast.FAsyncGenerator
->
1968 (Reason.Ryield_asyncnull
p,
1969 Toption
(Env.fresh_type
()))
1974 | _, _ -> assert false in
1975 let rty = match Env.get_fn_kind
env with
1977 (* yield in coroutine is already reported as error in NastCheck *)
1978 let _, _, ty = expr_error env p (Reason.Rwitness
p) in
1981 Reason.Ryield_gen
p,
1982 Tclass
((p, SN.Classes.cGenerator
), [key
; value; send])
1983 | Ast.FAsyncGenerator
->
1984 Reason.Ryield_asyncgen
p,
1985 Tclass
((p, SN.Classes.cAsyncGenerator
), [key
; value; send])
1986 | Ast.FSync
| Ast.FAsync
->
1987 failwith
"Parsing should never allow this" in
1988 let Typing_env_return_info.{ return_type
= expected_return
; _ } = Env.get_return
env in
1990 Type.sub_type
p (Reason.URyield
) env rty expected_return
in
1991 let env = Env.forget_members
env p in
1992 make_result env (T.Yield taf
) (Reason.Ryield_send
p, Toption
send)
1994 (* Await is permitted in a using clause e.g. using (await make_handle()) *)
1995 let env, te, rty = expr ~is_using_clause
env e in
1996 let env, ty = Async.overload_extract_from_awaitable
env p rty in
1997 make_result env (T.Await
te) ty
2001 | _, Call
(call_type
, e, hl
, el
, uel
) ->
2002 check_call ~is_using_clause ~
expected env p call_type
e hl el uel ~in_suspend
:true
2004 let env, te, (r, ty) = expr
env e in
2005 (* not a call - report an error *)
2006 Errors.non_call_argument_in_suspend
2008 (Reason.to_string
("This is " ^
Typing_print.error
ty) r);
2010 make_result env (T.Suspend
te) ty
2012 | Special_func func
-> special_func
env p func
2013 | New
(((), c), el
, uel
) ->
2014 Typing_hooks.dispatch_new_id_hook
c env p;
2015 TUtils.process_static_find_ref
c (p, SN.Members.__construct
);
2016 let env, tc, tel
, tuel
, ty =
2017 new_object ~
expected ~is_using_clause ~check_parent
:false ~check_not_abstract
:true
2019 let env = Env.forget_members
env p in
2020 make_result env (T.New
(tc, tel
, tuel
)) ty
2021 | Cast
((_, Harray
(None
, None
)), _)
2022 when Env.is_strict
env
2023 || TCO.migration_flag_enabled
(Env.get_tcopt
env) "array_cast" ->
2024 Errors.array_cast
p;
2025 expr_error env p (Reason.Rwitness
p)
2027 let env, te, ty2 = expr
env e in
2028 Async.enforce_not_awaitable
env (fst
e) ty2;
2029 if (TypecheckerOptions.experimental_feature_enabled
2030 (Env.get_options
env)
2031 TypecheckerOptions.experimental_forbid_nullable_cast
)
2032 && TUtils.is_option_non_mixed
env ty2
2034 let (r, ty2) = ty2 in
2035 Errors.nullable_cast
p (Typing_print.error
ty2) (Reason.to_pos
r)
2037 let env, ty = Phase.hint_locl
env hint
in
2038 make_result env (T.Cast
(hint
, te)) ty
2039 | InstanceOf
(e, ((), cid)) ->
2040 let env, te, _ = expr
env e in
2041 TUtils.process_class_id
cid;
2042 let env, te2
, _class
= instantiable_cid
p env cid in
2043 make_result env (T.InstanceOf
(te, te2
)) (Reason.Rwitness
p, Tprim Tbool
)
2045 if not
(TypecheckerOptions.experimental_feature_enabled
2046 (Env.get_options
env)
2047 TypecheckerOptions.experimental_is_expression
)
2049 Errors.experimental_feature
p "is expression";
2050 expr_error env p (Reason.Rwitness
p)
2052 let env, te, _ = expr
env e in
2053 let env, hint_ty
= Phase.hint_locl
env hint
in
2054 match (IsAsExprHint.validate hint_ty
) with
2055 | IsAsExprHint.Valid
->
2056 make_result env (T.Is
(te, hint
)) (Reason.Rwitness
p, Tprim Tbool
)
2057 | IsAsExprHint.Partial
(r, ty_
) ->
2058 Errors.partially_valid_is_as_expression_hint
(Reason.to_pos
r) "is"
2059 (IsAsExprHint.print ty_
);
2060 make_result env (T.Is
(te, hint
)) (Reason.Rwitness
p, Tprim Tbool
)
2061 | IsAsExprHint.Invalid
(r, ty_
) ->
2062 Errors.invalid_is_as_expression_hint
(Reason.to_pos
r) "is"
2063 (IsAsExprHint.print ty_
);
2064 expr_error env p (Reason.Rwitness
p)
2066 | As
(e, hint
, is_nullable
) ->
2067 if not
(TypecheckerOptions.experimental_feature_enabled
2068 (Env.get_options
env)
2069 TypecheckerOptions.experimental_as_expression
)
2071 Errors.experimental_feature
p "as expression";
2072 expr_error env p (Reason.Rnone
)
2074 let env, _, _ = expr
env e in
2075 let env, hint_ty
= Phase.hint_locl
env hint
in
2077 if not is_nullable
then hint_ty else
2079 (* Dont create ??hint *)
2080 | _ , Toption
_ -> hint_ty
2081 | _ -> Reason.Rwitness
p, Toption
(hint_ty) in
2082 let env, te, ty = assign
p env e hint_ty in
2083 match (IsAsExprHint.validate
hint_ty) with
2084 | IsAsExprHint.Valid
->
2085 make_result env (T.As
(te, hint
, is_nullable
)) ty
2086 | IsAsExprHint.Partial
(r, ty_
) ->
2087 Errors.partially_valid_is_as_expression_hint
(Reason.to_pos
r) "as"
2088 (IsAsExprHint.print ty_
);
2089 make_result env (T.As
(te, hint
, is_nullable
)) ty
2090 | IsAsExprHint.Invalid
(r, ty_
) ->
2091 Errors.invalid_is_as_expression_hint
(Reason.to_pos
r) "as"
2092 (IsAsExprHint.print ty_
);
2093 expr_error env p (Reason.Rwitness
p)
2096 (* This is the function type as declared on the lambda itself.
2097 * If type hints are absent then use Tany instead. *)
2098 let declared_ft = Decl.fun_decl_in_env
env.Env.decl_env f
in
2099 (* When creating a closure, the 'this' type will mean the late bound type
2100 * of the current enclosing class
2103 { (Phase.env_with_self
env) with from_class
= Some CIstatic
} in
2104 let env, declared_ft = Phase.localize_ft ~use_pos
:p ~
ety_env env declared_ft in
2105 List.iter idl
(check_escaping_var
env);
2106 (* Ensure lambda arity is not Fellipsis in strict mode *)
2107 begin match declared_ft.ft_arity
with
2108 | Fellipsis
_ when Env.is_strict
env ->
2109 Errors.ellipsis_strict_mode ~require
:`Param_name
p
2112 (* Is the return type declared? *)
2113 let is_explicit_ret = Option.is_some f
.f_ret
in
2114 let check_body_under_known_params ?ret_ty ft
=
2115 let (is_coroutine
, _counter
, _, anon
) = anon_make
env p f ft idl
in
2116 let ft = { ft with ft_is_coroutine
= is_coroutine
} in
2117 let ft = { ft with ft_reactive
= Nonreactive
} in
2118 let is_reactive, (env, tefun
, ty) =
2119 Env.check_lambda_reactive
(fun () -> anon ?ret_ty
env ft.ft_params
ft.ft_arity
) in
2120 let ft = { ft with ft_reactive
= is_reactive } in
2123 then (Reason.Rwitness
p, Tfun
{ ft with ft_ret
= declared_ft.ft_ret
})
2124 else (Reason.Rwitness
p, Tfun
{ ft with ft_ret
= ty }) in
2125 Typing_log.log_types
1 p env
2127 ("Typing.check_body_under_known_params",
2128 [Typing_log.Log_type
("ft", (Reason.Rwitness
p, Tfun
ft));
2129 Typing_log.Log_type
("inferred_ty", inferred_ty)])];
2130 env, tefun
, inferred_ty in
2131 let env, eexpected
= expand_expected
env expected in
2132 begin match eexpected
with
2133 | Some
(_pos
, _ur
, (_, Tfun expected_ft
)) ->
2134 (* First check that arities match up *)
2135 check_lambda_arity
p expected_ft
.ft_pos
declared_ft.ft_arity expected_ft
.ft_arity
;
2136 (* Use declared types for parameters in preference to those determined
2137 * by the context: they might be more general. *)
2138 let rec replace_non_declared_types params declared_ft_params expected_ft_params
=
2139 match params, declared_ft_params
, expected_ft_params
with
2140 | param
::params, declared_ft_param
::declared_ft_params
,
2141 expected_ft_param
::expected_ft_params
->
2142 let rest = replace_non_declared_types params declared_ft_params expected_ft_params
in
2143 let resolved_ft_param = if Option.is_some param
.param_hint
2144 then declared_ft_param
2145 else { declared_ft_param
with fp_type
= expected_ft_param
.fp_type
} in
2146 resolved_ft_param :: rest
2148 (* This means the expected_ft params list can have more parameters
2149 * than declared parameters in the lambda. For variadics, this is OK,
2150 * for non-variadics, this will be caught elsewhere in arity checks.
2154 let replace_non_declared_arity variadic declared_arity expected_arity
=
2156 | FVvariadicArg
{param_hint
= Some
(_); _} -> declared_arity
2157 | FVvariadicArg
_ ->
2159 match declared_arity
, expected_arity
with
2160 | Fvariadic
(min_arity
, declared
), Fvariadic
(_, expected) ->
2161 Fvariadic
(min_arity
, { declared
with fp_type
= expected.fp_type
})
2162 | _, _ -> declared_arity
2164 | _ -> declared_arity
2166 let expected_ft = { expected_ft with ft_arity
=
2167 replace_non_declared_arity
2168 f
.f_variadic
declared_ft.ft_arity
expected_ft.ft_arity
} in
2169 let expected_ft = { expected_ft with ft_params
=
2170 replace_non_declared_types f
.f_params
declared_ft.ft_params
expected_ft.ft_params
} in
2171 (* Don't bother passing in `void` if there is no explicit return *)
2173 match expected_ft.ft_ret
with
2174 | _, Tprim Tvoid
when not
is_explicit_ret -> None
2175 | _ -> Some
expected_ft.ft_ret
in
2176 Measure.sample
"Lambda [contextual params]" 1.0;
2177 check_body_under_known_params ?
ret_ty expected_ft
2179 let explicit_variadic_param_or_non_variadic =
2180 begin match f
.f_variadic
with
2181 | FVvariadicArg
{param_hint
; _} -> Option.is_some param_hint
2182 | FVellipsis
-> false
2186 (* If all parameters are annotated with explicit types, then type-check
2187 * the body under those assumptions and pick up the result type *)
2188 let all_explicit_params =
2189 List.for_all f
.f_params
(fun param
-> Option.is_some param
.param_hint
) in
2190 if all_explicit_params && explicit_variadic_param_or_non_variadic
2192 if List.is_empty f
.f_params
2193 then Measure.sample
"Lambda [no params]" 1.0
2194 else Measure.sample
"Lambda [explicit params]" 1.0;
2195 check_body_under_known_params declared_ft
2199 | Some
(_, _, (_, Tany
)) ->
2200 (* If the expected type is Tany env then we're passing a lambda to an untyped
2201 * function and we just assume every parameter has type Tany env *)
2202 Measure.sample
"Lambda [untyped context]" 1.0;
2203 check_body_under_known_params declared_ft
2205 (* If the expected type is something concrete but not a function
2206 * then we should reject in strict mode. Check body anyway *)
2207 if Env.is_strict
env && TypecheckerOptions.experimental_feature_enabled
(Env.get_options
env)
2208 TypecheckerOptions.experimental_disallow_untyped_lambda_as_non_function_type
2209 then Errors.untyped_lambda_strict_mode
p;
2210 Measure.sample
"Lambda [non-function typed context]" 1.0;
2211 check_body_under_known_params declared_ft
2213 Measure.sample
"Lambda [unknown params]" 1.0;
2214 Typing_log.log_types
1 p env
2216 ("Typing.expr Efun unknown params",
2217 [Typing_log.Log_type
("declared_ft", (Reason.Rwitness
p, Tfun
declared_ft))])];
2218 (* check for recursive function calls *)
2219 let is_coroutine, counter
, pos, anon
= anon_make
env p f
declared_ft idl
in
2220 let env, tefun
, _, anon_id
= Errors.try_with_error
2222 let reactivity, (_, tefun
, ty) =
2223 Env.check_lambda_reactive
2224 (fun () -> anon
env declared_ft.ft_params
declared_ft.ft_arity
) in
2225 let anon_fun = reactivity, is_coroutine, counter
, pos, anon
in
2226 let env, anon_id
= Env.add_anonymous
env anon_fun in
2227 env, tefun
, ty, anon_id
)
2229 (* If the anonymous function declaration has errors itself, silence
2230 them in any subsequent usages. *)
2231 let anon_ign ?el
:_ ?
ret_ty:_ env fun_params
=
2232 Errors.ignore_
(fun () -> (anon
env fun_params
)) in
2233 let reactivity, (_, tefun
, ty)
2234 = Env.check_lambda_reactive
(fun () -> anon_ign env declared_ft.ft_params
declared_ft.ft_arity
) in
2235 let anon_fun = reactivity, is_coroutine, counter
, pos, anon
in
2236 let env, anon_id
= Env.add_anonymous
env anon_fun in
2237 env, tefun
, ty, anon_id
) in
2238 env, tefun
, (Reason.Rwitness
p, Tanon
(declared_ft.ft_arity
, anon_id
))
2241 | Xml
(sid
, attrl
, el
) ->
2242 let cid = CI
(sid
, []) in
2243 let env, _te
, classes
= class_id_for_new
p env cid in
2244 let class_info = match classes
with
2246 (* OK to ignore rest of list; class_info only used for errors, and
2247 * cid = CI sid cannot produce a union of classes anyhow *)
2248 | (_, class_info, _)::_ -> Some
class_info
2250 let env, _te
, obj
= expr
env (fst sid
, New
(((), cid), [], [])) in
2251 let env, typed_attrs
, attr_types
= xhp_attribute_exprs
env class_info attrl
in
2252 let env, tel
= List.fold_left el ~init
:(env, []) ~f
:fold_xhp_body_elements
in
2253 let txml = T.Xml
(sid
, typed_attrs
, List.rev tel
) in
2254 (match class_info with
2255 | None
-> make_result env txml (Reason.Runknown_class
p, Tobject
)
2256 | Some
class_info ->
2257 let env = List.fold_left attr_types ~f
:begin fun env attr
->
2258 let namepstr, valpty
= attr
in
2259 let valp, valty
= valpty
in
2261 obj_get ~is_method
:false ~
nullsafe:None
env obj
cid
2262 namepstr (fun x -> x) in
2263 let ureason = Reason.URxhp
(class_info.tc_name
, snd
namepstr) in
2264 Type.sub_type
valp ureason env valty declty
2266 make_result env txml obj
2268 | Callconv
(kind
, e) ->
2269 let env, te, ty = expr
env e in
2270 let rec check_types env = function
2272 | _, T.Array_get
(((_, Some
ty1), _) as te1, Some
_) ->
2273 let rec iter = function
2275 | _, Tprim Tstring
-> true
2276 | _, (Tarraykind
_ | Ttuple
_ | Tshape
_) -> true
2277 | _, Tclass
((_, cn
), _)
2278 when cn
= SN.Collections.cDict
2279 || cn
= SN.Collections.cKeyset
2280 || cn
= SN.Collections.cVec
-> true
2281 | _, Tunresolved tyl
-> List.for_all ~f
:iter tyl
2283 let env, ety1
= Env.expand_type
env ty1 in
2284 if iter ety1
then check_types env te1 else begin
2285 let ty_str = Typing_print.error
(snd ety1
) in
2286 let msgl = Reason.to_string
("This is " ^
ty_str) (fst ety1
) in
2287 Errors.inout_argument_bad_type
(fst
e) msgl
2289 | _, T.Array_get
(te2
, Some
_) ->
2291 (* Other invalid expressions are caught in NastCheck. *)
2295 make_result env (T.Callconv
(kind
, te)) ty
2296 (* TODO TAST: change AST so that order of shape expressions is preserved.
2297 * At present, evaluation order is unspecified in TAST *)
2299 let env, fdm_with_expected
=
2300 match expand_expected
env expected with
2301 | env, Some
(pos, ur
, (_, Tshape
(_, expected_fdm
))) ->
2305 match ShapeMap.get k expected_fdm
with
2307 | Some sft
-> (v
, Some
(pos, ur
, sft
.sft_ty
))) fdm
in
2310 env, ShapeMap.map
(fun v
-> (v
, None
)) fdm
in
2312 (* allow_inter adds a type-variable *)
2315 (fun env (e, expected) ->
2316 let env, te, ty = expr ?
expected env e in env, (te,ty))
2317 env fdm_with_expected
in
2319 let convert_expr_and_type_to_shape_field_type env (_, ty) =
2320 let env, sft_ty
= TUtils.unresolved
env ty in
2321 (* An expression evaluation always corresponds to a shape_field_type
2322 with sft_optional = false. *)
2323 env, { sft_optional
= false; sft_ty
} in
2324 ShapeMap.map_env
convert_expr_and_type_to_shape_field_type env tfdm
in
2325 let env = check_shape_keys_validity
env p (ShapeMap.keys fdm
) in
2326 (* Fields are fully known, because this shape is constructed
2327 * using shape keyword and we know exactly what fields are set. *)
2328 make_result env (T.Shape
(ShapeMap.map
(fun (te,_) -> te) tfdm
))
2329 (Reason.Rwitness
p, Tshape
(FieldsFullyKnown
, fdm
))
2331 and class_const ?
(incl_tc
=false) env p (((), cid), mid
) =
2332 TUtils.process_static_find_ref
cid mid
;
2333 let env, ce
, cty
= static_class_id
p env cid in
2334 let env, const_ty
, cc_abstract_info
=
2335 class_get ~is_method
:false ~is_const
:true ~incl_tc
env cty mid
cid in
2336 match cc_abstract_info
with
2337 | Some
(cc_pos
, cc_name
) ->
2338 let () = match cid with
2339 | CIstatic
| CIexpr
_ -> ();
2340 | _ -> Errors.abstract_const_usage
p cc_pos cc_name
; ()
2341 in env, T.make_typed_expr
p const_ty
(T.Class_const
(ce
, mid
)), const_ty
2343 env, T.make_typed_expr
p const_ty
(T.Class_const
(ce
, mid
)), const_ty
2345 and anon_sub_type
pos ur
env ty_sub ty_super
=
2346 Errors.try_add_err
pos (Reason.string_of_ureason ur
)
2347 (fun () -> SubType.sub_type
env ty_sub ty_super
)
2350 (*****************************************************************************)
2351 (* XHP attribute/body helpers. *)
2352 (*****************************************************************************)
2354 * Process a spread operator by computing the intersection of XHP attributes
2355 * between the spread expression and the XHP constructor onto which we're
2358 and xhp_spread_attribute
env c_onto valexpr
=
2359 let (p, _) = valexpr
in
2360 let env, te, valty
= expr
env valexpr
in
2361 (* Build the typed attribute node *)
2362 let typed_attr = T.Xhp_spread
te in
2363 let env, attr_ptys
= match c_onto
with
2365 | Some
class_info -> Typing_xhp.get_spread_attributes
env p class_info valty
2366 in env, typed_attr, attr_ptys
2369 * Simple XHP attributes (attr={expr} form) are simply interpreted as a member
2370 * variable prefixed with a colon, the types of which will be validated later
2372 and xhp_simple_attribute
env id valexpr
=
2373 let (p, _) = valexpr
in
2374 let env, te, valty
= expr
env valexpr
in
2375 (* This converts the attribute name to a member name. *)
2376 let name = ":"^
(snd
id) in
2377 let attr_pty = ((fst
id, name), (p, valty
)) in
2378 let typed_attr = T.Xhp_simple
(id, te) in
2379 env, typed_attr, [attr_pty]
2383 * Typecheck the attribute expressions - this just checks that the expressions are
2384 * valid, not that they match the declared type for the attribute and,
2385 * in case of spreads, makes sure they are XHP.
2387 and xhp_attribute_exprs
env cid attrl
=
2388 let handle_attr (env, typed_attrl
, attr_ptyl
) attr
=
2389 let env, typed_attr, attr_ptys
= match attr
with
2390 | Xhp_simple
(id, valexpr
) -> xhp_simple_attribute
env id valexpr
2391 | Xhp_spread valexpr
-> xhp_spread_attribute
env cid valexpr
2393 env, typed_attr::typed_attrl
, attr_ptys
@ attr_ptyl
2395 let env, typed_attrl
, attr_ptyl
= List.fold_left ~f
:handle_attr ~init
:(env, [], []) attrl
in
2396 env, List.rev typed_attrl
, List.rev attr_ptyl
2398 and check_xhp_children
env pos ty =
2399 let tys = match ty with
2400 | _, Tunresolved ts
-> ts
2402 if List.for_all ~f
:(Typing_xhp.xhp_child
env pos) tys then env
2404 let ty_str = Typing_print.error
(snd
ty) in
2405 let msgl = Reason.to_string
("This is "^
ty_str) (fst
ty) in
2406 Errors.illegal_xhp_child
pos msgl; env
2409 and fold_xhp_body_elements
(env, tel
) body
=
2410 let expr_pos = fst body
in
2411 let env, te, ty = expr
env body
in
2412 let env, ty = Env.expand_type
env ty in
2413 let env, ty = TUtils.fold_unresolved
env ty in
2414 let env = check_xhp_children
env expr_pos ty in
2417 (*****************************************************************************)
2418 (* Anonymous functions. *)
2419 (*****************************************************************************)
2420 and anon_bind_param
params (env, t_params
) ty : Env.env * Tast.fun_param list
=
2423 (* This code cannot be executed normally, because the arity is wrong
2424 * and it will error later. Bind as many parameters as we can and carry
2427 | param
:: paraml
->
2429 match param
.param_hint
with
2432 let h = Decl_hint.hint
env.Env.decl_env
h in
2433 (* When creating a closure, the 'this' type will mean the
2434 * late bound type of the current enclosing class
2437 { (Phase.env_with_self
env) with from_class
= Some CIstatic
} in
2438 let env, h = Phase.localize ~
ety_env env h in
2439 let pos = Reason.to_pos
(fst
ty) in
2440 (* Don't use Type.sub_type as it resets env.Env.pos unnecessarily *)
2441 let env = anon_sub_type
pos Reason.URparam
env ty h in
2442 (* Closures are allowed to have explicit type-hints. When
2443 * that is the case we should check that the argument passed
2444 * is compatible with the type-hint.
2445 * The body of the function should be type-checked with the
2446 * hint and not the type of the argument passed.
2447 * Otherwise it leads to strange results where
2448 * foo(?string $x = null) is called with a string and fails to
2449 * type-check. If $x is a string instead of ?string, null is not
2450 * subtype of string ...
2452 let env, t_param
= bind_param env (h, param
) in
2453 env, t_params
@ [t_param
]
2455 let env, t_param
= bind_param env (ty, param
) in
2456 env, t_params
@ [t_param
]
2458 and anon_bind_variadic
env vparam variadic_ty
=
2460 match vparam
.param_hint
with
2462 (* if the hint is missing, use the type we expect *)
2463 env, variadic_ty
, Reason.to_pos
(fst variadic_ty
)
2465 let h = Decl_hint.hint
env.Env.decl_env hint
in
2467 { (Phase.env_with_self
env) with from_class
= Some CIstatic
; } in
2468 let env, h = Phase.localize ~
ety_env env h in
2469 let pos = Reason.to_pos
(fst variadic_ty
) in
2470 let env = anon_sub_type
pos Reason.URparam
env variadic_ty
h in
2471 env, h, vparam
.param_pos
2473 let r = Reason.Rvar_param
pos in
2474 let arr_values = r, (snd
ty) in
2476 if TypecheckerOptions.experimental_feature_enabled
2477 (Env.get_options
env)
2478 TypecheckerOptions.experimental_darray_and_varray
2479 then AKvarray
arr_values
2480 else AKvec
arr_values in
2481 let ty = r, Tarraykind
akind in
2482 let env, t_variadic
= bind_param env (ty, vparam
) in
2486 and anon_bind_opt_param
env param
: Env.env =
2487 match param
.param_expr
with
2489 let ty = Reason.Rwitness param
.param_pos
, Typing_utils.tany
env in
2490 let env, _ = bind_param env (ty, param
) in
2493 let env, _te
, ty = expr
env default
in
2494 Typing_sequencing.sequence_check_expr default
;
2495 let env, _ = bind_param env (ty, param
) in
2498 and anon_check_param
env param
=
2499 match param
.param_hint
with
2502 let env, hty
= Phase.hint_locl
env hty
in
2503 let paramty = Env.get_local
env (Local_id.get param
.param_name
) in
2504 let hint_pos = Reason.to_pos
(fst hty
) in
2505 let env = Type.sub_type
hint_pos Reason.URhint
env paramty hty
in
2508 (* Make a type-checking function for an anonymous function. *)
2509 and anon_make tenv
p f
ft idl
=
2510 let anon_lenv = tenv
.Env.lenv in
2511 let is_typing_self = ref false in
2512 let nb = Nast.assert_named_body f
.f_body
in
2513 let is_coroutine = f
.f_fun_kind
= Ast.FCoroutine
in
2517 (* Here ret_ty should include Awaitable wrapper *)
2518 fun ?el ?
ret_ty env supplied_params supplied_arity
->
2521 Errors.anonymous_recursive
p;
2522 expr_error env p (Reason.Rwitness
p)
2525 is_typing_self := true;
2526 Env.anon
anon_lenv env begin fun env ->
2527 let env = Env.clear_params
env in
2528 let make_variadic_arg env varg tyl
=
2529 let remaining_types =
2530 (* It's possible the variadic arg will capture the variadic
2531 * parameter of the supplied arity (if arity is Fvariadic)
2532 * and additional supplied params.
2534 * For example in cases such as:
2535 * lambda1 = (int $a, string...$c) ==> {};
2536 * lambda1(1, "hello", ...$y); (where $y is a variadic string)
2537 * lambda1(1, "hello", "world");
2538 * then ...$c will contain "hello" and everything in $y in the first
2539 * example, and "hello" and "world" in the second example.
2541 * To account for a mismatch in arity, we take the remaining supplied
2542 * parameters and return a list of all their types. We'll use this
2543 * to create a union type when creating the typed variadic arg.
2545 let remaining_params = List.drop supplied_params
(List.length f
.f_params
) in
2546 List.map ~f
:(fun param
-> param
.fp_type
) remaining_params
2548 let r = Reason.Rvar_param
(varg
.param_pos
) in
2549 let union = Tunresolved
(tyl
@ remaining_types) in
2550 let env, t_param
= anon_bind_variadic
env varg
(r, union) in
2551 env, T.FVvariadicArg t_param
2553 let env, t_variadic
=
2554 begin match f
.f_variadic
, supplied_arity
with
2555 | FVvariadicArg arg
, Fvariadic
(_, variadic
) ->
2556 make_variadic_arg env arg
[variadic
.fp_type
]
2557 | FVvariadicArg arg
, Fstandard
_ ->
2558 make_variadic_arg env arg
[]
2559 | _, _ -> env, T.FVnonVariadic
2561 let params = ref f
.f_params
in
2562 let env, t_params
= List.fold_left ~f
:(anon_bind_param
params) ~init
:(env, [])
2563 (List.map supplied_params
(fun x -> x.fp_type
)) in
2564 let env = List.fold_left ~f
:anon_bind_opt_param ~init
:env !params in
2565 let env = List.fold_left ~f
:anon_check_param ~init
:env f
.f_params
in
2566 let env = match el
with
2568 iter2_shortest
Unify.unify_param_modes
ft.ft_params supplied_params
;
2571 iter2_shortest param_modes
ft.ft_params
x;
2572 wfold_left2 inout_write_back
env ft.ft_params
x in
2573 let env = Env.set_fn_kind
env f
.f_fun_kind
in
2577 (* Do we have a contextual return type? *)
2578 begin match ret_ty with
2580 let env, ret_ty = Env.fresh_unresolved_type
env in
2581 env, Typing_return.wrap_awaitable
env p ret_ty
2583 (* We might need to force it to be Awaitable if it is a type variable *)
2584 Typing_return.force_awaitable
env p ret_ty
2587 let ret = TI.instantiable_hint
env x in
2588 (* If a 'this' type appears it needs to be compatible with the
2592 { (Phase.env_with_self
env) with
2593 from_class
= Some CIstatic
} in
2594 Phase.localize ~
ety_env env ret in
2595 let env = Env.set_return
env
2596 (Typing_return.make_info f
.f_fun_kind
[] env
2597 ~is_explicit
:(Option.is_some
ret_ty)
2598 ~is_by_ref
:f
.f_ret_by_ref
2600 let env, tb
= block
env nb.fnb_nast
in
2602 if Nast_terminality.Terminal.block tenv
nb.fnb_nast
2603 || nb.fnb_unsafe
|| !auto_complete
2605 else fun_implicit_return
env p hret f
.f_fun_kind
2607 (* We don't want the *uses* of the function to affect its return type *)
2608 let env, hret
= Env.unbind
env hret
in
2609 is_typing_self := false;
2611 T.f_annotation
= save_env env;
2612 T.f_mode
= f
.f_mode
;
2614 T.f_name
= f
.f_name
;
2615 T.f_tparams
= f
.f_tparams
;
2616 T.f_where_constraints
= f
.f_where_constraints
;
2617 T.f_fun_kind
= f
.f_fun_kind
;
2618 T.f_user_attributes
= List.map f
.f_user_attributes
(user_attribute
env);
2619 T.f_body
= T.NamedBody
{
2621 T.fnb_unsafe
= nb.fnb_unsafe
;
2623 T.f_params
= t_params
;
2624 T.f_variadic
= t_variadic
; (* TODO TAST: Variadic efuns *)
2625 T.f_ret_by_ref
= f
.f_ret_by_ref
;
2627 let ty = (Reason.Rwitness
p, Tfun
ft) in
2628 let te = T.make_typed_expr
p ty (T.Efun
(tfun_, idl
)) in
2633 (*****************************************************************************)
2634 (* End of anonymous functions. *)
2635 (*****************************************************************************)
2637 and special_func
env p func
=
2638 let env, tfunc
, ty = (match func
with
2640 let env, te, ety
= expr
env e in
2641 let env, ty = Async.gena
env p ety
in
2644 let env, tel
, etyl
= exprs env el
in
2645 let env, ty = Async.genva
env p etyl
in
2646 env, T.Genva tel
, ty
2647 | Gen_array_rec
e ->
2648 let env, te, ety
= expr
env e in
2649 let env, ty = Async.gen_array_rec
env p ety
in
2650 env, T.Gen_array_rec
te, ty
2653 (Reason.Rwitness
p, Tclass
((p, SN.Classes.cAwaitable
), [ty])) in
2654 env, T.make_typed_expr
p result_ty (T.Special_func tfunc
), result_ty
2656 and requires_consistent_construct
= function
2663 (* Caller will be looking for a particular form of expected type
2664 * e.g. a function type (when checking lambdas) or tuple type (when checking
2665 * tuples). First expand the expected type and elide single union; also
2666 * strip nullables, so ?t becomes t, as context will always accept a t if a ?t
2669 and expand_expected
env expected =
2673 | Some
(p, ur
, ty) ->
2674 let env, ty = Env.expand_type
env ty in
2676 | _, Tany
-> env, None
2677 | _, Tunresolved
[ty] -> env, Some
(p, ur
, ty)
2678 | _, Toption
ty -> env, Some
(p, ur
, ty)
2679 | _ -> env, Some
(p, ur
, ty)
2681 (* Do a subtype check of inferred type against expected type *)
2682 and check_expected_ty message
env inferred_ty expected =
2686 | Some
(p, ur
, expected_ty
) ->
2687 Typing_log.log_types
1 p env
2689 (Printf.sprintf
"Typing.check_expected_ty %s" message
,
2690 [Typing_log.Log_type
("inferred_ty", inferred_ty);
2691 Typing_log.Log_type
("expected_ty", expected_ty
)])];
2692 Type.sub_type
p ur
env inferred_ty expected_ty
2694 and new_object ~
expected ~check_parent ~check_not_abstract ~is_using_clause
p env cid el uel
=
2695 (* Obtain class info from the cid expression. We get multiple
2696 * results with a CIexpr that has a union type *)
2697 let env, tcid
, classes
= instantiable_cid
p env cid in
2698 let finish env tcid tel tuel
ty =
2700 if check_parent
then env, ty
2701 else ExprDepTy.make
env cid ty in
2702 env, tcid
, tel
, tuel
, new_ty
in
2703 let rec gather env tel tuel res classes
=
2709 let _ = exprs env el
in
2710 finish env tcid tel tuel
(Reason.Runknown_class
p, Tobject
)
2711 | [ty] -> finish env tcid tel tuel
ty
2712 | tyl
-> finish env tcid tel tuel
(Reason.Rwitness
p, Tunresolved tyl
)
2715 | (cname
, class_info, c_ty
)::classes
->
2716 if check_not_abstract
&& class_info.tc_abstract
2717 && not
(requires_consistent_construct
cid) then
2718 uninstantiable_error
p cid class_info.tc_pos
class_info.tc_name
p c_ty
;
2719 let env, obj_ty_
, params =
2720 match cid, snd c_ty
with
2721 | CI
(_, _::_), Tclass
(_, tyl
) -> env, (snd c_ty
), tyl
2723 let env, params = List.map_env
env class_info.tc_tparams
2724 (fun env _ -> Env.fresh_unresolved_type
env) in
2725 env, (Tclass
(cname
, params)), params in
2726 if not check_parent
&& not is_using_clause
&& class_info.tc_is_disposable
2727 then Errors.invalid_new_disposable
p;
2728 let r_witness = Reason.Rwitness
p in
2729 let obj_ty = (r_witness, obj_ty_
) in
2732 | CIstatic
-> (r_witness, TUtils.this_of
obj_ty)
2733 | CIexpr
_ -> (r_witness, snd
c_ty)
2738 else ExprDepTy.make
env cid c_ty in
2739 let env, _tcid
, tel
, tuel
=
2740 let env = check_expected_ty
"New" env new_ty
expected in
2741 call_construct
p env class_info params el uel
cid in
2742 if not
(snd
class_info.tc_construct
) then
2744 | CIstatic
-> Errors.new_inconsistent_construct
p cname `static
2745 | CIexpr
_ -> Errors.new_inconsistent_construct
p cname `classname
2749 (match (fst
class_info.tc_construct
) with
2750 | Some
{ce_type
= lazy ty; _ } ->
2752 type_expansions
= [];
2753 substs
= SMap.empty
;
2757 let _, ce_type
= Phase.localize ~
ety_env env ty in
2758 ignore
(check_abstract_parent_meth
SN.Members.__construct
p ce_type
)
2760 gather env tel tuel
(obj_ty::res
) classes
2761 | CIstatic
| CI
_ | CIself
-> gather env tel tuel
(c_ty::res
) classes
2763 (* When constructing from a (classname) variable, the variable
2764 * dictates what the constructed object is going to be. This allows
2765 * for generic and dependent types to be correctly carried
2766 * through the 'new $foo()' iff the constructed obj_ty is a
2767 * supertype of the variable-dictated c_ty *)
2768 let env = SubType.sub_type
env c_ty obj_ty in
2769 gather env tel tuel
(c_ty::res
) classes
2771 gather env [] [] [] classes
2773 (* FIXME: we need to separate our instantiability into two parts. Currently,
2774 * all this function is doing is checking if a given type is inhabited --
2775 * that is, whether there are runtime values of type T. However,
2776 * instantiability should be the stricter notion that T has a runtime
2777 * constructor; that is, `new T()` should be valid. In particular, interfaces
2778 * are inhabited, but not instantiable.
2779 * To make this work with classname, we likely need to add something like
2780 * concrete_classname<T>, where T cannot be an interface.
2782 and instantiable_cid
p env cid =
2783 let env, te, classes
= class_id_for_new
p env cid in
2785 List.iter classes
begin fun ((pos, name), class_info, c_ty) ->
2786 if class_info.tc_kind
= Ast.Ctrait
|| class_info.tc_kind
= Ast.Cenum
2789 | CIexpr
_ | CI
_ ->
2790 uninstantiable_error
p cid class_info.tc_pos
name pos c_ty
2791 | CIstatic
| CIparent
| CIself
-> ()
2792 else if class_info.tc_kind
= Ast.Cabstract
&& class_info.tc_final
2794 uninstantiable_error
p cid class_info.tc_pos
name pos c_ty
2799 and uninstantiable_error reason_pos
cid c_tc_pos c_name c_usage_pos
c_ty =
2800 let reason_msgl = match cid with
2802 let ty_str = "This would be "^
Typing_print.error
(snd
c_ty) in
2803 [(reason_pos
, ty_str)]
2805 Errors.uninstantiable_class c_usage_pos c_tc_pos c_name
reason_msgl
2807 and exception_ty
pos env ty =
2808 let exn_ty = Reason.Rthrow
pos, Tclass
((pos, SN.Classes.cThrowable
), []) in
2809 Type.sub_type
pos (Reason.URthrow
) env ty exn_ty
2811 and shape_field_pos
= function
2812 | Ast.SFlit
(p, _) -> p
2813 | Ast.SFclass_const
((cls_pos
, _), (mem_pos
, _)) -> Pos.btw cls_pos mem_pos
2815 and check_shape_keys_validity
env pos keys =
2816 (* If the key is a class constant, get its class name and type. *)
2817 let get_field_info env key
=
2818 let key_pos = shape_field_pos key
in
2819 (* Empty strings or literals that start with numbers are not
2820 permitted as shape field names. *)
2822 | Ast.SFlit
(_, key_name
) ->
2823 if (String.length key_name
= 0) then
2824 (Errors.invalid_shape_field_name_empty
key_pos)
2825 else if (key_name
.[0] >= '
0'
&& key_name
.[0] <='
9'
) then
2826 (Errors.invalid_shape_field_name_number
key_pos);
2828 | Ast.SFclass_const
(_, cls
as x, y
) ->
2829 let env, _te
, ty = class_const
env pos (((), CI
(x, [])), y
) in
2830 let env = Typing_enum.check_valid_array_key_type
2831 Errors.invalid_shape_field_type ~allow_any
:false
2833 env, key_pos, Some
(cls
, ty))
2836 let check_field witness_pos witness_info
env key
=
2837 let env, key_pos, key_info
= get_field_info env key
in
2838 (match witness_info
, key_info
with
2840 Errors.invalid_shape_field_literal
key_pos witness_pos
; env
2842 Errors.invalid_shape_field_const
key_pos witness_pos
; env
2844 | Some
(cls1
, ty1), Some
(cls2
, ty2) ->
2845 if cls1
<> cls2
then
2846 Errors.shape_field_class_mismatch
2847 key_pos witness_pos
(strip_ns cls2
) (strip_ns cls1
);
2848 (* We want to use our own error message here instead of the normal
2849 * unification one. *)
2851 (fun () -> Unify.iunify
env ty1 ty2)
2853 Errors.shape_field_type_mismatch
2855 (Typing_print.error
(snd
ty2)) (Typing_print.error
(snd
ty1));
2859 (* Sort the keys by their positions since the error messages will make
2860 * more sense if we take the one that appears first as canonical and if
2861 * they are processed in source order. *)
2862 let cmp_keys x y
= Pos.compare
(shape_field_pos
x) (shape_field_pos y
) in
2863 let keys = List.sort
cmp_keys keys in
2867 | witness
:: rest_keys
->
2868 let env, pos, info
= get_field_info env witness
in
2869 List.fold_left ~f
:(check_field pos info
) ~init
:env rest_keys
2871 and check_valid_rvalue
p env ty =
2872 let rec iter_over_types env tyl
=
2878 let env, ety
= Env.expand_type
env ty in
2880 | r, Tprim Tnoreturn
->
2881 Errors.noreturn_usage
p
2882 (Reason.to_string
"A noreturn function always throws or exits" r);
2883 env, (r, Typing_utils.terr
env)
2887 (Reason.to_string
"A void function doesn't return a value" r);
2888 env, (r, Typing_utils.terr
env)
2890 | _, Tunresolved tyl2
->
2891 iter_over_types env (tyl2
@ tyl
)
2894 iter_over_types env tyl
in
2895 iter_over_types env [ty]
2897 and set_valid_rvalue
p env x ty =
2898 let env, ty = check_valid_rvalue
p env ty in
2899 let env = set_local
env (p, x) ty in
2900 (* We are assigning a new value to the local variable, so we need to
2901 * generate a new expression id
2903 let env = Env.set_local_expr_id
env x (Ident.tmp
()) in
2906 (* Deal with assignment of a value of type ty2 to lvalue e1 *)
2907 and assign
p env e1
ty2 : _ * T.expr
* T.ty =
2908 assign_
p Reason.URassign
env e1
ty2
2910 and assign_
p ur
env e1
ty2 =
2911 let make_result env te1 ty1 = (env, T.make_typed_expr
(fst e1
) ty1 te1, ty1) in
2913 | (_, Lvar
((_, x) as id)) ->
2914 let env, ty1 = set_valid_rvalue
p env x ty2 in
2915 make_result env (T.Lvar
id) ty1
2916 | (_, Lplaceholder
id) ->
2917 let placeholder_ty = Reason.Rplaceholder
p, (Tprim Tvoid
) in
2918 make_result env (T.Lplaceholder
id) placeholder_ty
2920 let env, folded_ty2
= TUtils.fold_unresolved
env ty2 in
2922 try_over_concrete_supertypes env folded_ty2
2923 begin fun env ty2 ->
2925 (* Vector<t> or ImmVector<t> or ConstVector<t> or vec<T> *)
2926 | (_, Tclass
((_, x), [elt_type
]))
2927 when x = SN.Collections.cVector
2928 || x = SN.Collections.cImmVector
2929 || x = SN.Collections.cVec
2930 || x = SN.Collections.cConstVector
->
2931 (* Vector assignment is illegal in a reactive context
2932 but vec assignment is okay *)
2933 if x <> SN.Collections.cVec
2934 then Env.error_if_reactive_context
env @@ begin fun () ->
2935 Errors.nonreactive_append
p
2937 let env, tel
= List.map_env
env el
begin fun env e ->
2938 let env, te, _ = assign
(fst
e) env e elt_type
in
2941 make_result env (T.List tel
) ty2
2942 (* array<t> or varray<t> *)
2943 | (_, Tarraykind
(AKvec elt_type
))
2944 | (_, Tarraykind
(AKvarray elt_type
)) ->
2945 let env, tel
= List.map_env
env el
begin fun env e ->
2946 let env, te, _ = assign
(fst
e) env e elt_type
in
2949 make_result env (T.List tel
) ty2
2950 (* array or empty array or Tany *)
2951 | (r, (Tarraykind
(AKany
| AKempty
) | Tany
)) ->
2952 let env, tel
= List.map_env
env el
begin fun env e ->
2953 let env, te, _ = assign
(fst
e) env e (r, Typing_utils.tany
env) in
2956 make_result env (T.List tel
) ty2
2957 | (r, (Tdynamic
)) ->
2958 let env, tel
= List.map_env
env el
begin fun env e ->
2959 let env, te, _ = assign
(fst
e) env e (r, Tdynamic
) in
2962 make_result env (T.List tel
) ty2
2964 | ((r, Tclass
((_, coll
), [ty1; ty2])) as folded_ety2
)
2965 when coll
= SN.Collections.cPair
->
2968 let env, te1, _ = assign
p env x1
ty1 in
2969 let env, te2
, _ = assign
p env x2
ty2 in
2970 make_result env (T.List
[te1; te2
]) folded_ety2
2972 Errors.pair_arity
p;
2973 make_result env T.Any
(r, Typing_utils.terr
env))
2974 (* tuple-like array *)
2975 | (r, Tarraykind
(AKtuple fields
)) ->
2977 let p2 = Reason.to_pos
r in
2978 let tyl = List.rev
(IMap.values fields
) in
2979 let size1 = List.length el
in
2980 let size2 = List.length
tyl in
2983 Errors.tuple_arity
p2 size2 p1 size1;
2984 make_result env T.Any
(r, Typing_utils.terr
env)
2987 let env, reversed_tel
=
2988 List.fold2_exn el
tyl ~f
:begin fun (env,tel
) lvalue
ty2 ->
2989 let env, te, _ = assign
p env lvalue
ty2 in
2991 end ~init
:(env,[]) in
2992 make_result env (T.List
(List.rev reversed_tel
)) ty2
2993 (* Other, including tuples. Create a tuple type for the left hand
2994 * side and attempt subtype against it. In particular this deals with
2995 * types such as (string,int) | (int,bool) *)
2998 List.map_env
env el
(fun env _ -> Env.fresh_unresolved_type
env) in
2999 let env = Type.sub_type
p ur
env folded_ty2
3000 (Reason.Rwitness
(fst e1
), Ttuple
tyl) in
3001 let env, reversed_tel
=
3002 List.fold2_exn el
tyl ~init
:(env,[]) ~f
:(fun (env,tel
) lvalue
ty2 ->
3003 let env, te, _ = assign
p env lvalue
ty2 in
3005 make_result env (T.List
(List.rev reversed_tel
)) ty2
3007 begin match resl with
3009 | _ -> assign_simple
p ur
env e1
ty2
3014 Env.not_lambda_reactive
();
3015 let lenv = env.Env.lenv in
3016 let no_fakes = LEnv.env_with_empty_fakes
env in
3017 (* In this section, we check that the assignment is compatible with
3018 * the real type of a member. Remember that members can change
3019 * type (cf fake_members). But when we assign a value to $this->x,
3020 * we want to make sure that the type assign to $this->x is compatible
3021 * with the actual type hint. In this portion of the code, type-check
3022 * the assignment in an environment without fakes, and therefore
3023 * check that the assignment is compatible with the type of
3026 let env, te1, real_type
= lvalue
no_fakes e1
in
3027 let env, exp_real_type
= Env.expand_type
env real_type
in
3028 let env = { env with Env.lenv = lenv } in
3029 let env, ety2
= Env.expand_type
env ty2 in
3030 let real_type_list =
3031 match exp_real_type
with
3032 | _, Tunresolved
tyl -> tyl
3035 let env = List.fold_left
real_type_list ~f
:begin fun env real_type
->
3036 Type.sub_type
p ur
env ety2 real_type
3039 | _, Obj_get
((_, This
| _, Lvar
_ as obj
),
3040 (_, Id
(_, member_name
)),
3042 let env, local = Env.FakeMembers.make
p env obj member_name
in
3043 let () = (match obj
with
3045 Typing_suggest.save_member member_name
env exp_real_type
ty2
3048 let env, ty = set_valid_rvalue
p env local ty2 in
3050 | _, Class_get
(((), x), (_, y
)) ->
3051 let env, local = Env.FakeMembers.make_static
p env x y
in
3052 let env, ty3
= set_valid_rvalue
p env local ty2 in
3056 Typing_suggest.save_member y
env exp_real_type
ty2;
3059 | _ -> env, te1, ty2
3061 | _, Array_get
((_, Lvar
(_, lvar
)) as shape
, ((Some
_) as e2
)) ->
3062 let access_type = Typing_arrays.static_array_access
env e2
in
3063 (* In the case of an assignment of the form $x['new_field'] = ...;
3064 * $x could be a shape where the field 'new_field' is not yet defined.
3065 * When that is the case we want to add the field to its type.
3067 let env, _te
, shape_ty
= expr
env shape
in
3068 let env, shape_ty
= Typing_arrays.update_array_type_on_lvar_assignment
3069 p access_type env shape_ty
in
3070 let env, _ = set_valid_rvalue
p env lvar shape_ty
in
3071 (* We still need to call assign_simple in order to bind the freshly
3072 * created variable in added shape field. Moreover, it's needed because
3073 * shape_ty could be more than just a shape. It could be an unresolved
3074 * type where some elements are shapes and some others are not.
3076 assign_simple
p ur
env e1
ty2
3078 Errors.this_lvalue
p;
3079 make_result env T.Any
(Reason.Rwitness
p, Typing_utils.terr
env)
3080 | pref
, Unop
(Ast.Uref
, e1'
) ->
3081 (* references can be "lvalues" in foreach bindings *)
3082 if TypecheckerOptions.experimental_feature_enabled
3083 (Env.get_options
env)
3084 TypecheckerOptions.experimental_disallow_refs_in_array
3085 then Errors.binding_ref_in_array pref
3086 else if Env.is_strict
env
3087 then Errors.reference_expr pref
;
3088 let env, texpr
, ty = assign
p env e1'
ty2 in
3089 make_result env (T.Unop
(Ast.Uref
, texpr
)) ty
3091 assign_simple
p ur
env e1
ty2
3093 and assign_simple
pos ur
env e1
ty2 =
3094 let env, te1, ty1 = lvalue
env e1
in
3095 let env, ty2 = check_valid_rvalue
pos env ty2 in
3096 let env, ty2 = TUtils.unresolved
env ty2 in
3097 let env = Type.sub_type
pos ur
env ty2 ty1 in
3100 and array_field
env = function
3101 | Nast.AFvalue ve
->
3102 let env, tve
, tv
= expr
env ve
in
3103 let env, tv
= Typing_env.unbind
env tv
in
3104 env, (T.AFvalue tve
, None
, tv
)
3105 | Nast.AFkvalue
(ke
, ve
) ->
3106 let env, tke
, tk
= expr
env ke
in
3107 let env, tve
, tv
= expr
env ve
in
3108 let env, tv
= Typing_env.unbind
env tv
in
3109 env, (T.AFkvalue
(tke
, tve
), Some tk
, tv
)
3111 and array_value ~
expected env x =
3112 let env, te, ty = expr ?
expected ~
forbid_uref:true env x in
3113 let env, ty = Typing_env.unbind
env ty in
3116 and array_field_value ~
expected env = function
3118 | Nast.AFkvalue
(_, x) ->
3119 array_value ~
expected env x
3121 and array_field_key ~
expected env = function
3122 (* This shouldn't happen *)
3123 | Nast.AFvalue
(p, _) ->
3124 env, (T.make_implicitly_typed_expr
p T.Any
,
3125 (Reason.Rwitness
p, Tprim Tint
))
3126 | Nast.AFkvalue
(x, _) ->
3127 array_value ~
expected env x
3129 and akshape_field
env = function
3130 | Nast.AFkvalue
(k
, v
) ->
3131 let env, tek
, tk
= expr
env k
in
3132 let env, tk
= Typing_env.unbind
env tk
in
3133 let env, tk
= TUtils.unresolved
env tk
in
3134 let env, tev
, tv
= expr
env v
in
3135 let env, tv
= Typing_env.unbind
env tv
in
3136 let env, tv
= TUtils.unresolved
env tv
in
3138 match TUtils.shape_field_name
env Pos.none
(snd k
) with
3139 | Some
field_name -> field_name
3140 | None
-> assert false in (* Typing_arrays.is_shape_like_array
3141 * should have prevented this *)
3142 env, T.AFkvalue
(tek
, tev
), (field_name, (tk
, tv
))
3143 | Nast.AFvalue
_ -> assert false (* Typing_arrays.is_shape_like_array
3144 * should have prevented this *)
3146 and aktuple_afvalue
env v
=
3147 let env, tev
, tv
= expr
env v
in
3148 let env, tv
= Typing_env.unbind
env tv
in
3149 let env, ty = TUtils.unresolved
env tv
in
3152 and aktuple_field
env = function
3153 | Nast.AFvalue v
-> aktuple_afvalue
env v
3154 | Nast.AFkvalue
_ -> assert false (* check_consistent_fields
3155 * should have prevented this *)
3156 and check_parent_construct
pos env el uel env_parent
=
3157 let check_not_abstract = false in
3158 let env, env_parent
= Phase.localize_with_self
env env_parent
in
3159 let env, _tcid
, tel
, tuel
, parent
=
3160 new_object ~
expected:None ~check_parent
:true ~
check_not_abstract
3161 ~is_using_clause
:false
3162 pos env CIparent el uel
in
3163 let env, _ = Type.unify
pos (Reason.URnone
) env env_parent parent
in
3164 env, tel
, tuel
, (Reason.Rwitness
pos, Tprim Tvoid
)
3166 and call_parent_construct
pos env el uel
=
3167 let parent = Env.get_parent
env in
3170 check_parent_construct
pos env el uel
parent
3180 | Tvarray_or_darray
_
3190 ) -> (* continue here *)
3191 let default = env, [], [], (Reason.Rwitness
pos, Typing_utils.tany
env) in
3192 match Env.get_self
env with
3193 | _, Tclass
((_, self
), _) ->
3194 (match Env.get_class
env self
with
3195 | Some
({tc_kind
= Ast.Ctrait
; _}
3197 (match trait_most_concrete_req_class trait
env with
3198 | None
-> Errors.parent_in_trait
pos; default
3199 | Some
(_, parent_ty
) ->
3200 check_parent_construct
pos env el uel parent_ty
3203 if not self_tc
.tc_members_fully_known
3204 then () (* Don't know the hierarchy, assume it's correct *)
3205 else Errors.undefined_parent
pos;
3207 | None
-> assert false)
3208 | _, (Terr
| Tany
| Tmixed
| Tnonnull
| Tarraykind
_ | Toption
_
3209 | Tprim
_ | Tfun
_ | Ttuple
_ | Tshape
_ | Tvar
_ | Tdynamic
3210 | Tabstract
(_, _) | Tanon
(_, _) | Tunresolved
_ | Tobject
3212 Errors.parent_outside_class
pos;
3213 env, [], [], (Reason.Rwitness
pos, Typing_utils.terr
env)
3215 (* parent::method() in a class definition invokes the specific parent
3216 * version of the method ... it better be callable *)
3217 and check_abstract_parent_meth mname
pos fty =
3218 if is_abstract_ft
fty
3219 then Errors.parent_abstract_call mname
pos (Reason.to_pos
(fst
fty));
3222 and is_abstract_ft
fty = match fty with
3223 | _r, Tfun
{ ft_abstract
= true; _ } -> true
3224 | _r, (Terr
| Tany
| Tmixed
| Tnonnull
| Tarraykind
_ | Toption
_ | Tprim
_
3225 | Tvar
_ | Tfun
_ | Tclass
(_, _) | Tabstract
(_, _) | Ttuple
_
3226 | Tanon
_ | Tunresolved
_ | Tobject
| Tshape
_ | Tdynamic
3230 (* Depending on the kind of expression we are dealing with
3231 * The typing of call is different.
3234 and dispatch_call ~
expected ~is_using_clause
p env call_type
3235 (fpos
, fun_expr
as e) hl el uel ~in_suspend
=
3236 let make_call env te thl tel tuel
ty =
3237 env, T.make_typed_expr
p ty (T.Call
(call_type
, te, thl
, tel
, tuel
)), ty in
3238 let make_call_special env id tel
ty =
3239 make_call env (T.make_implicitly_typed_expr fpos
(T.Id
id)) [] tel
[] ty in
3241 let check_coroutine_call env fty =
3242 let () = if is_return_disposable_fun_type env fty && not is_using_clause
3243 then Errors.invalid_new_disposable
p else () in
3245 - Some true if type is definitely a coroutine
3246 - Some false if type is definitely not a coroutine
3247 - None if type is Tunresolved that contains
3248 both coroutine and non-coroutine constituents *)
3249 let rec is_coroutine ty =
3251 | Tfun
{ ft_is_coroutine
= true; _ } ->
3254 Some
(Option.value_map
(Env.get_anonymous
env id) ~
default:false ~f
:(fun (_,b
,_,_,_) -> b
) )
3256 begin match List.map ts ~f
:is_coroutine with
3259 (*if rest of the list has the same value as the first element
3260 return value of the first element or None otherwise*)
3261 if List.for_all xs ~f
:(Option.value_map ~
default:false ~f
:((=)x))
3268 match in_suspend
, is_coroutine fty with
3270 | false, Some
false -> ()
3272 (* non-coroutine call in suspend *)
3273 Errors.non_coroutine_call_in_suspend
3275 (Reason.to_string
("This is " ^
Typing_print.error
(snd
fty)) (fst
fty));
3277 (*coroutine call outside of suspend *)
3278 Errors.coroutine_call_outside_of_suspend
p; in
3280 let check_function_in_suspend name =
3282 then Errors.function_is_not_coroutine fpos
name in
3284 let check_class_function_in_suspend class_name function_name
=
3285 check_function_in_suspend (class_name ^
"::" ^ function_name
) in
3288 (* Special function `echo` *)
3289 | Id
((p, pseudo_func
) as id) when pseudo_func
= SN.SpecialFunctions.echo
->
3290 check_function_in_suspend SN.SpecialFunctions.echo
;
3291 Env.error_if_shallow_reactive_context
env @@ begin fun () ->
3292 Errors.echo_in_reactive_context
p;
3294 let env, tel
, _ = exprs ~accept_using_var
:true env el
in
3295 make_call_special env id tel
(Reason.Rwitness
p, Tprim Tvoid
)
3296 (* Special function `empty` *)
3297 | Id
((_, pseudo_func
) as id) when pseudo_func
= SN.PseudoFunctions.empty
->
3298 check_function_in_suspend SN.PseudoFunctions.empty
;
3299 let env, tel
, _ = exprs ~accept_using_var
:true env el
in
3301 Errors.unpacking_disallowed_builtin_function
p pseudo_func
;
3302 if Env.is_strict
env then
3303 Errors.empty_in_strict
p;
3304 make_call_special env id tel
(Reason.Rwitness
p, Tprim Tbool
)
3305 (* Special function `isset` *)
3306 | Id
((_, pseudo_func
) as id) when pseudo_func
= SN.PseudoFunctions.isset
->
3307 check_function_in_suspend SN.PseudoFunctions.isset
;
3308 let env, tel
, _ = exprs ~accept_using_var
:true env el
in
3310 Errors.unpacking_disallowed_builtin_function
p pseudo_func
;
3311 if Env.is_strict
env then
3312 Errors.isset_in_strict
p;
3313 make_call_special env id tel
(Reason.Rwitness
p, Tprim Tbool
)
3314 (* Special function `unset` *)
3315 | Id
((_, pseudo_func
) as id) when pseudo_func
= SN.PseudoFunctions.unset
->
3316 check_function_in_suspend SN.PseudoFunctions.unset
;
3317 let env, tel
, _ = exprs env el
in
3319 Errors.unpacking_disallowed_builtin_function
p pseudo_func
;
3320 let env = if Env.is_strict
env then
3322 | [(_, Array_get
((_, Class_const
_), Some
_))], [] ->
3323 Errors.const_mutation
p Pos.none
"";
3325 | [(_, Array_get
(ea
, Some
_))], [] ->
3326 let env, _te
, ty = expr
env ea
in
3327 let tany = Typing_utils.tany env in
3328 if List.exists ~f
:(fun super
-> SubType.is_sub_type
env ty super
) [
3329 (Reason.Rnone
, (Tclass
((Pos.none
, SN.Collections.cDict
),
3330 [(Reason.Rnone
, tany); (Reason.Rnone
, tany)])));
3331 (Reason.Rnone
, (Tclass
((Pos.none
, SN.Collections.cKeyset
),
3332 [(Reason.Rnone
, tany)])));
3333 (Reason.Rnone
, Tarraykind AKany
)
3336 let env, (r, ety
) = Env.expand_type
env ty in
3337 Errors.unset_nonidx_in_strict
3339 (Reason.to_string
("This is " ^
Typing_print.error ety
) r);
3342 | _ -> Errors.unset_nonidx_in_strict
p []; env)
3345 | [(p, Obj_get
(_, _, OG_nullsafe
))] ->
3347 Errors.nullsafe_property_write_context
p;
3348 make_call_special env id tel
(Reason.Rwitness
p, Typing_utils.terr
env)
3351 make_call_special env id tel
(Reason.Rwitness
p, Tprim Tvoid
))
3352 (* Special function `freeze` *)
3353 | Id
((_, pseudo_func
) as id) when pseudo_func
= SN.PseudoFunctions.freeze
->
3354 check_function_in_suspend SN.PseudoFunctions.freeze
;
3355 let env, tel
, _ = exprs env el
in
3357 Errors.unpacking_disallowed_builtin_function
p pseudo_func
;
3358 if not
(Env.env_local_reactive
env) then
3359 Errors.freeze_in_nonreactive_context
p;
3360 let env = Typing_mutability.freeze_local
p env tel
in
3361 make_call_special env id tel
(Reason.Rwitness
p, Tprim Tvoid
)
3362 (* Pseudo-function `get_called_class` *)
3363 | Id
(cp
, get_called_class
) when
3364 get_called_class
= SN.StdlibFunctions.get_called_class
3365 && el
= [] && uel
= [] ->
3366 check_function_in_suspend SN.StdlibFunctions.get_called_class
;
3367 (* get_called_class fetches the late-bound class *)
3368 if Env.is_outside_class
env then Errors.static_outside_class
p;
3369 class_const
env p (((), CIstatic
), (cp
, SN.Members.mClass
))
3370 (* Special function `array_filter` *)
3371 | Id
((_, array_filter
) as id)
3372 when array_filter
= SN.StdlibFunctions.array_filter
&& el
<> [] && uel
= [] ->
3373 check_function_in_suspend SN.StdlibFunctions.array_filter
;
3374 (* dispatch the call to typecheck the arguments *)
3375 let env, fty = fun_type_of_id
env id hl
in
3376 let env, tel
, tuel
, res
= call ~
expected p env fty el uel
in
3377 (* but ignore the result and overwrite it with custom return type *)
3378 let x = List.hd_exn el
in
3379 let env, _tx
, ty = expr
env x in
3380 let explain_array_filter (r, t
) =
3381 (Reason.Rarray_filter
(p, r), t
) in
3382 let get_value_type env tv
=
3384 if List.length el
> 1
3386 else TUtils.non_null
env tv
in
3387 env, explain_array_filter tv
in
3388 let rec get_array_filter_return_type env ty =
3389 let env, ety
= Env.expand_type
env ty in
3391 | (_, Tarraykind
(AKany
| AKempty
)) as array_type
->
3393 | (_, Tarraykind
(AKtuple
_)) ->
3394 let env, ty = Typing_arrays.downcast_aktypes
env ty in
3395 get_array_filter_return_type env ty
3396 | (r, Tarraykind
(AKvec tv
| AKvarray tv
)) ->
3397 let env, tv
= get_value_type env tv
in
3398 env, (r, Tarraykind
(AKvec tv
))
3399 | (r, Tunresolved
x) ->
3400 let env, x = List.map_env
env x get_array_filter_return_type in
3401 env, (r, Tunresolved
x)
3403 env, (r, Typing_utils.tany env)
3405 env, (r, Typing_utils.terr
env)
3407 let tk, tv
= Env.fresh_type
(), Env.fresh_type
() in
3410 let keyed_container = (
3413 (Pos.none
, SN.Collections.cKeyedContainer
), [tk; tv
]
3416 let env = SubType.sub_type
env ety
keyed_container in
3417 let env, tv
= get_value_type env tv
in
3418 env, (r, Tarraykind
(AKmap
(
3419 (explain_array_filter tk),
3422 (fun _ -> Errors.try_
3427 (Pos.none
, SN.Collections.cContainer
), [tv
]
3430 let env = SubType.sub_type
env ety
container in
3431 let env, tv
= get_value_type env tv
in
3432 env, (r, Tarraykind
(AKmap
(
3433 (explain_array_filter (r, Tprim Tarraykey
)),
3435 (fun _ -> env, res
)))
3436 in let env, rty = get_array_filter_return_type env ty in
3437 make_call env (T.make_implicitly_typed_expr fpos
(T.Id
id)) hl tel tuel
rty
3438 (* Special function `type_structure` *)
3439 | Id
(p, type_structure
)
3440 when type_structure
= SN.StdlibFunctions.type_structure
3441 && (List.length el
= 2) && uel
= [] ->
3442 check_function_in_suspend SN.StdlibFunctions.type_structure
;
3446 | p, Nast.String cst
->
3447 (* find the class constant implicitly defined by the typeconst *)
3448 let cid = (match e1
with
3449 | _, Class_const
(cid, (_, x))
3450 | _, Class_get
(cid, (_, x)) when x = SN.Members.mClass
-> cid
3451 | _ -> ((), Nast.CIexpr e1
)) in
3452 class_const ~incl_tc
:true env p (cid, cst
)
3454 Errors.illegal_type_structure
p "second argument is not a string";
3455 expr_error env p (Reason.Rwitness
p))
3456 | _ -> assert false)
3457 (* Special function `array_map` *)
3458 | Id
((_, array_map
) as x)
3459 when array_map
= SN.StdlibFunctions.array_map
&& el
<> [] && uel
= [] ->
3460 check_function_in_suspend SN.StdlibFunctions.array_map
;
3461 let env, fty = fun_type_of_id
env x [] in
3462 let env, fty = Env.expand_type
env fty in
3463 let env, fty = match fty, el
with
3464 | ((r_fty
, Tfun
fty), _::args
) when args
<> [] ->
3465 let arity = List.length args
in
3467 Builds a function with signature:
3469 function<T1, ..., Tn, Tr>(
3470 (function(T1, ..., Tn):Tr),
3476 where R is constructed by build_output_container applied to Tr
3478 let build_function env build_output_container
=
3480 (* If T1, ... Tn, Tr are provided explicitly, instantiate the function parameters with
3481 * those directly. *)
3482 if List.length hl
= 0
3484 let env, tr
= Env.fresh_unresolved_type
env in
3485 env, List.map args
(fun _ -> Env.fresh_type
()), tr
3486 else if List.length hl
<> List.length args
+ 1 then begin
3487 let env, tr
= Env.fresh_unresolved_type
env in
3488 Errors.expected_tparam
fty.ft_pos
(1 + (List.length args
));
3489 env, List.map args
(fun _ -> Env.fresh_type
()), tr
end
3491 let env, vars_and_tr
= List.map_env
env hl
Phase.hint_locl
in
3492 let vars, trl
= List.split_n vars_and_tr
(List.length vars_and_tr
- 1) in
3493 (* Since we split the arguments and return type at the last index and the length is
3494 non-zero this is safe. *)
3495 let tr = List.hd_exn trl
in
3498 let f = TUtils.default_fun_param
(
3501 ft_pos
= fty.ft_pos
;
3502 ft_deprecated
= None
;
3503 ft_abstract
= false;
3504 ft_is_coroutine
= false;
3505 ft_arity
= Fstandard
(arity, arity);
3507 ft_where_constraints
= [];
3508 ft_params
= List.map
vars TUtils.default_fun_param
;
3510 ft_ret_by_ref
= fty.ft_ret_by_ref
;
3511 ft_reactive
= fty.ft_reactive
;
3512 ft_mutable
= fty.ft_mutable
;
3513 ft_returns_mutable
= fty.ft_returns_mutable
;
3514 ft_return_disposable
= fty.ft_return_disposable
;
3515 ft_decl_errors
= None
;
3518 let containers = List.map
vars (fun var
->
3519 let tc = Tclass
((fty.ft_pos
, SN.Collections.cContainer
), [var
]) in
3520 TUtils.default_fun_param
(r_fty
, tc)
3522 env, (r_fty
, Tfun
{fty with
3523 ft_arity
= Fstandard
(arity+1, arity+1);
3524 ft_params
= f::containers;
3525 ft_ret
= build_output_container
tr;
3530 Takes a Container type and returns a function that can "pack" a type
3531 into an array of appropriate shape, preserving the key type, i.e.:
3532 array -> f, where f R = array
3533 array<X> -> f, where f R = array<R>
3534 array<X, Y> -> f, where f R = array<X, R>
3535 Vector<X> -> f where f R = array<R>
3536 KeyedContainer<X, Y> -> f, where f R = array<X, R>
3537 Container<X> -> f, where f R = array<arraykey, R>
3538 X -> f, where f R = Y
3540 let rec build_output_container
3541 (env:Env.env) (x:locl
ty) : (Env.env * (locl
ty -> locl
ty)) =
3542 let env, x = Env.expand_type
env x in (match x with
3543 | (_, Tarraykind
(AKany
| AKempty
)) as array_type
->
3544 env, (fun _ -> array_type
)
3545 | (_, Tarraykind
(AKtuple
_ )) ->
3546 let env, x = Typing_arrays.downcast_aktypes
env x in
3547 build_output_container env x
3548 | (r, Tarraykind
(AKvec
_ | AKvarray
_)) ->
3549 env, (fun tr -> (r, Tarraykind
(AKvec
(tr))) )
3551 env, (fun _ -> (r, Typing_utils.tany env))
3553 env, (fun _ -> (r, Typing_utils.terr
env))
3554 | (r, Tunresolved
x) ->
3555 let env, x = List.map_env
env x build_output_container in
3556 env, (fun tr -> (r, Tunresolved
(List.map
x (fun f -> f tr))))
3558 let tk, tv
= Env.fresh_type
(), Env.fresh_type
() in
3559 let try_vector env =
3563 (fty.ft_pos
, SN.Collections.cConstVector
), [tv
]
3566 let env = SubType.sub_type
env x vector in
3567 env, (fun tr -> (r, Tarraykind
(
3570 let try_keyed_container env =
3571 let keyed_container = (
3574 (fty.ft_pos
, SN.Collections.cKeyedContainer
), [tk; tv
]
3577 let env = SubType.sub_type
env x keyed_container in
3578 env, (fun tr -> (r, Tarraykind
(AKmap
(
3582 let try_container env =
3586 (fty.ft_pos
, SN.Collections.cContainer
), [tv
]
3589 let env = SubType.sub_type
env x container in
3590 env, (fun tr -> (r, Tarraykind
(AKmap
(
3591 (r, Tprim Tarraykey
),
3596 (fun _ -> Errors.try_
3598 try_keyed_container env)
3599 (fun _ -> Errors.try_
3602 (fun _ -> env, (fun _ -> (Reason.Rwitness
p, Typing_utils.tany env)))))) in
3604 Single argument calls preserve the key type, multi argument
3605 calls always return an array<Tr>
3609 let env, _tx
, x = expr
env x in
3610 let env, output_container
= build_output_container env x in
3611 build_function env output_container
3613 build_function env (fun tr ->
3614 (r_fty
, Tarraykind
(AKvec
(tr)))))
3616 let env, tel
, _tuel
, ty = call ~
expected p env fty el
[] in
3617 make_call_special env x tel
ty
3618 (* Special function `idx` *)
3619 | Id
((_, idx
) as id) when idx
= SN.FB.idx
->
3620 check_function_in_suspend SN.FB.idx
;
3621 (* Directly call get_fun so that we can muck with the type before
3622 * instantiation -- much easier to work in terms of Tgeneric Tk/Tv than
3623 * trying to figure out which Tvar is which. *)
3624 Typing_hooks.dispatch_fun_id_hook
(p, SN.FB.idx
);
3625 (match Env.get_fun
env (snd
id) with
3627 let param1, param2
, param3
=
3628 match fty.ft_params
with
3629 | [param1; param2
; param3
] -> param1, param2
, param3
3630 | _ -> assert false in
3631 let { fp_type
= (r2
, _); _ } = param2
in
3632 let { fp_type
= (r3
, _); _ } = param3
in
3633 let params, ret = match List.length el
with
3635 let ty1 = match param1.fp_type
with
3636 | (r11
, Toption
(r12
, Tapply
(coll
, [tk; (r13
, _) as tv
]))) ->
3637 (r11
, Toption
(r12
, Tapply
(coll
, [tk; (r13
, Toption tv
)])))
3638 | _ -> assert false in
3639 let param1 = { param1 with fp_type
= ty1 } in
3640 let ty2 = (r2
, Toption
(r2
, Tgeneric
"Tk")) in
3641 let param2 = { param2 with fp_type
= ty2 } in
3642 let rret = fst
fty.ft_ret
in
3643 let ret = (rret, Toption
(rret, Tgeneric
"Tv")) in
3644 [param1; param2], ret
3646 let param2 = { param2 with fp_type
= (r2
, Tgeneric
"Tk") } in
3647 let param3 = { param3 with fp_type
= (r3
, Tgeneric
"Tv") } in
3648 let ret = (fst
fty.ft_ret
, Tgeneric
"Tv") in
3649 [param1; param2; param3], ret
3650 | _ -> fty.ft_params
, fty.ft_ret
in
3651 let fty = { fty with ft_params
= params; ft_ret
= ret } in
3652 let ety_env = Phase.env_with_self
env in
3653 let env, fty = Phase.localize_ft ~use_pos
:p ~
ety_env env fty in
3654 let tfun = Reason.Rwitness
fty.ft_pos
, Tfun
fty in
3655 let env, tel
, _tuel
, ty = call ~
expected p env tfun el
[] in
3656 let env, ty = match ty with
3658 let env, ty = TUtils.non_null
env ty in
3659 env, (r, Toption
ty)
3661 make_call_special env id tel
ty
3662 | None
-> unbound_name env id)
3664 (* Special function `Shapes::idx` *)
3665 | Class_const
(((), CI
((_, shapes
), _)) as class_id
, ((_, idx
) as method_id
))
3666 when shapes
= SN.Shapes.cShapes
&& idx
= SN.Shapes.idx
->
3667 check_class_function_in_suspend SN.Shapes.cShapes
SN.Shapes.idx
;
3668 overload_function
p env class_id method_id el uel
3669 begin fun env fty res el
-> match el
with
3671 let env, _ts
, shape_ty
= expr
env shape
in
3672 Typing_shapes.idx
env p fty shape_ty field None
3673 | [shape
; field
; default] ->
3674 let env, _ts
, shape_ty
= expr
env shape
in
3675 let env, _td
, default_ty
= expr
env default in
3676 Typing_shapes.idx
env p fty shape_ty field
3677 (Some
((fst
default), default_ty
))
3680 (* Special function `Shapes::keyExists` *)
3681 | Class_const
(((), CI
((_, shapes
), _)) as class_id
, ((_, key_exists
) as method_id
))
3682 when shapes
= SN.Shapes.cShapes
&& key_exists
= SN.Shapes.keyExists
->
3683 check_class_function_in_suspend SN.Shapes.cShapes
SN.Shapes.keyExists
;
3684 overload_function
p env class_id method_id el uel
3685 begin fun env fty res el
-> match el
with
3687 let env, _te
, shape_ty
= expr
env shape
in
3688 (* try accessing the field, to verify existence, but ignore
3689 * the returned type and keep the one coming from function
3690 * return type hint *)
3691 let env, _ = Typing_shapes.idx
env p fty shape_ty field None
in
3695 (* Special function `Shapes::removeKey` *)
3696 | Class_const
(((), CI
((_, shapes
), _)) as class_id
, ((_, remove_key
) as method_id
))
3697 when shapes
= SN.Shapes.cShapes
&& remove_key
= SN.Shapes.removeKey
->
3698 check_class_function_in_suspend SN.Shapes.cShapes
SN.Shapes.removeKey
;
3699 overload_function
p env class_id method_id el uel
3700 begin fun env _ res el
-> match el
with
3701 | [shape
; field
] -> begin match shape
with
3702 | (_, Lvar
(_, lvar
))
3703 | (_, Unop
(Ast.Uref
, (_, Lvar
(_, lvar
)))) ->
3704 let env, _te
, shape_ty
= expr ~is_func_arg
:true env shape
in
3706 Typing_shapes.remove_key
p env shape_ty field
in
3707 let env, _ = set_valid_rvalue
p env lvar shape_ty
in
3710 Errors.invalid_shape_remove_key
(fst shape
);
3715 (* Special function `Shapes::toArray` *)
3716 | Class_const
(((), CI
((_, shapes
), _)) as class_id
, ((_, to_array
) as method_id
))
3717 when shapes
= SN.Shapes.cShapes
&& to_array
= SN.Shapes.toArray
->
3718 check_class_function_in_suspend SN.Shapes.cShapes
SN.Shapes.toArray
;
3719 overload_function
p env class_id method_id el uel
3720 begin fun env _ res el
-> match el
with
3722 let env, _te
, shape_ty
= expr
env shape
in
3723 Typing_shapes.to_array
env shape_ty res
3727 (* Special function `parent::__construct` *)
3728 | Class_const
(((), CIparent
), ((callee_pos
, construct
) as id))
3729 when construct
= SN.Members.__construct
->
3730 check_class_function_in_suspend "parent" SN.Members.__construct
;
3731 Typing_hooks.dispatch_parent_construct_hook
env callee_pos
;
3732 let env, tel
, tuel
, ty = call_parent_construct
p env el uel
in
3733 make_call env (T.make_implicitly_typed_expr fpos
3734 (T.Class_const
((Some
ty, T.CIparent
), id))) hl tel tuel
ty
3736 (* Calling parent method *)
3737 | Class_const
(((), CIparent
), m
) ->
3738 let env, _te
, ty1 = static_class_id
p env CIparent
in
3739 if Env.is_static
env
3741 (* in static context, you can only call parent::foo() on static
3744 class_get ~is_method
:true ~is_const
:false ~explicit_tparams
:hl
env ty1 m CIparent
in
3745 let fty = check_abstract_parent_meth
(snd m
) p fty in
3746 check_coroutine_call env fty;
3747 let env, tel
, tuel
, ty = call ~
expected ~receiver_type
:ty1 p env fty el uel
in
3748 make_call env (T.make_typed_expr fpos
fty
3749 (T.Class_const
((Some
ty1, T.CIparent
), m
))) hl tel tuel
ty
3752 (* in instance context, you can call parent:foo() on static
3753 * methods as well as instance methods *)
3754 if not
(class_contains_smethod
env ty1 m
)
3756 (* parent::nonStaticFunc() is really weird. It's calling a method
3757 * defined on the parent class, but $this is still the child class.
3758 * We can deal with this by hijacking the continuation that
3759 * calculates the SN.Typehints.this type *)
3760 let env, this_ty
= ExprDepTy.make
env CIstatic
3761 (Reason.Rwitness fpos
, TUtils.this_of
(Env.get_self
env)) in
3762 let k_lhs _ = this_ty
in
3763 let env, method_
, _ =
3764 obj_get_ ~is_method
:true ~
nullsafe:None ~pos_params
:(Some el
) ~
valkind:`other
env ty1
3766 begin fun (env, fty, _) ->
3767 let fty = check_abstract_parent_meth
(snd m
) p fty in
3768 check_coroutine_call env fty;
3769 let env, _tel
, _tuel
, method_
= call ~
expected
3770 ~receiver_type
:ty1 p env fty el uel
in
3775 make_call env (T.make_typed_expr fpos this_ty
3776 (T.Class_const
((Some
ty1, T.CIparent
), m
))) hl
[] [] method_
3779 class_get ~is_method
:true ~is_const
:false ~explicit_tparams
:hl
env ty1 m CIparent
in
3780 let fty = check_abstract_parent_meth
(snd m
) p fty in
3781 check_coroutine_call env fty;
3782 let env, tel
, tuel
, ty = call ~
expected ~receiver_type
:ty1 p env fty el uel
in
3783 make_call env (T.make_typed_expr fpos
fty
3784 (T.Class_const
((Some
ty1, T.CIparent
), m
))) hl tel tuel
ty
3786 (* Call class method *)
3787 | Class_const
(((), e1
), m
) ->
3788 TUtils.process_static_find_ref e1 m
;
3789 let env, te1, ty1 = static_class_id
p env e1
in
3791 class_get ~is_method
:true ~is_const
:false ~explicit_tparams
:hl
3792 ~pos_params
:el
env ty1 m e1
in
3793 let () = match e1
with
3794 | CIself
when is_abstract_ft
fty ->
3795 (match Env.get_self
env with
3796 | _, Tclass
((_, self
), _) ->
3797 (* at runtime, self:: in a trait is a call to whatever
3798 * self:: is in the context of the non-trait "use"-ing
3799 * the trait's code *)
3800 (match Env.get_class
env self
with
3801 | Some
{ tc_kind
= Ast.Ctrait
; _ } -> ()
3802 | _ -> Errors.self_abstract_call
(snd m
) p (Reason.to_pos
(fst
fty))
3805 | CI
(c, _) when is_abstract_ft
fty ->
3806 Errors.classname_abstract_call
(snd
c) (snd m
) p (Reason.to_pos
(fst
fty))
3807 | CI
((_, classname
), _) ->
3808 (match Typing_heap.Classes.get classname
with
3810 let (_, method_name
) = m
in
3811 (match SMap.get method_name class_def
.tc_smethods
with
3814 if elt
.ce_synthesized
then
3815 Errors.static_synthetic_method classname
(snd m
) p (Reason.to_pos
(fst
fty)))
3817 (* This technically should be an error, but if we throw here we'll break a ton of our
3818 tests since they reference classes that only exist in www, and any missing classes will
3819 get caught elsewhere in the pipeline. *)
3822 check_coroutine_call env fty;
3823 let env, tel
, tuel
, ty = call ~
expected ~receiver_type
:ty1 p env fty el uel
in
3824 make_call env (T.make_typed_expr fpos
fty
3825 (T.Class_const
(te1, m
))) hl tel tuel
ty
3827 (* Call instance method *)
3828 | Obj_get
(e1
, (pos_id
, Id m
), nullflavor
) ->
3829 let is_method = call_type
= Cnormal
in
3830 let env, te1, ty1 = expr ~accept_using_var
:true env e1
in
3832 (match nullflavor
with
3833 | OG_nullthrows
-> None
3834 | OG_nullsafe
-> Some
p
3836 let tel = ref [] and tuel
= ref [] and tftyl
= ref [] in
3837 let fn = (fun (env, fty, _) ->
3838 check_coroutine_call env fty;
3839 let env, tel_
, tuel_
, method_
= call ~
expected ~receiver_type
:ty1 p env fty el uel
in
3840 tel := tel_
; tuel
:= tuel_
;
3841 tftyl
:= fty :: !tftyl
;
3842 env, method_
, None
) in
3843 let env, ty = obj_get ~
is_method ~
nullsafe ~pos_params
:el
3844 ~explicit_tparams
:hl
env ty1 (CIexpr e1
) m
fn in
3848 | tftyl
-> (Reason.none
, Tunresolved tftyl
)
3850 make_call env (T.make_typed_expr fpos
tfty (T.Obj_get
(te1,
3851 T.make_implicitly_typed_expr pos_id
(T.Id m
), nullflavor
))) hl
!tel !tuel
ty
3853 (* Function invocation *)
3855 Typing_hooks.dispatch_id_hook
x env;
3856 let env, fty = fun_type_of_id
env x hl
in
3857 check_coroutine_call env fty;
3858 let env, tel, tuel
, ty = call ~
expected p env fty el uel
in
3859 make_call env (T.make_typed_expr fpos
fty (T.Fun_id
x)) hl
tel tuel
ty
3861 Typing_hooks.dispatch_id_hook
x env;
3862 let env, fty = fun_type_of_id
env x hl
in
3863 check_coroutine_call env fty;
3864 let env, tel, tuel
, ty = call ~
expected p env fty el uel
in
3865 make_call env (T.make_typed_expr fpos
fty (T.Id
x)) hl
tel tuel
ty
3867 let env, te, fty = expr
env e in
3868 check_coroutine_call env fty;
3869 let env, tel, tuel
, ty = call ~
expected p env fty el uel
in
3870 make_call env te hl
tel tuel
ty
3872 and fun_type_of_id
env x hl
=
3873 Typing_hooks.dispatch_fun_id_hook
x;
3875 match Env.get_fun
env (snd
x) with
3876 | None
-> let env, _, ty = unbound_name env x in env, ty
3878 let ety_env = Phase.env_with_self
env in
3879 let env, fty = Phase.localize_ft ~use_pos
:(fst
x) ~explicit_tparams
:hl ~
ety_env env fty in
3880 env, (Reason.Rwitness
fty.ft_pos
, Tfun
fty)
3884 (*****************************************************************************)
3885 (* Function type-checking expressions accessing an array (example: $x[...]).
3886 * The parameter is_lvalue is true when the expression is on the left hand
3887 * side of an assignment (example: $x[...] = 0).
3889 (*****************************************************************************)
3890 and array_get ?
(lhs_of_null_coalesce
=false) is_lvalue p env ty1 e2
ty2 =
3891 (* This is a little weird -- we enforce the right arity when you use certain
3892 * collections, even in partial mode (where normally completely omitting the
3893 * type parameter list is admitted). Basically the "omit type parameter"
3894 * hole was for compatibility with certain interfaces like ArrayAccess, not
3895 * for collections! But it's hard to go back on now, so since we've always
3896 * errored (with an inscrutable error message) when you try to actually use
3897 * a collection with omitted type parameters, we can continue to error and
3898 * give a more useful error message. *)
3899 let env, ety1
= Env.expand_type
env ty1 in
3900 let arity_error (_, name) =
3901 Errors.array_get_arity
p name (Reason.to_pos
(fst ety1
)) in
3903 | Tunresolved
tyl ->
3904 let env, tyl = List.map_env
env tyl begin fun env ty1 ->
3905 array_get ~lhs_of_null_coalesce
is_lvalue p env ty1 e2
ty2
3907 env, (fst ety1
, Tunresolved
tyl)
3908 | Tarraykind
(AKvarray
ty | AKvec
ty) ->
3909 let ty1 = Reason.Ridx
(fst e2
, fst ety1
), Tprim Tint
in
3910 let env = Type.sub_type
p Reason.index_array
env ty2 ty1 in
3912 | Tarraykind
(AKvarray_or_darray
ty) ->
3913 let ty1 = Reason.Rvarray_or_darray_key
p, Tprim Tarraykey
in
3914 let env = Type.sub_type
p Reason.index_array
env ty2 ty1 in
3916 | Tclass
((_, cn
) as id, argl
)
3917 when cn
= SN.Collections.cVector
3918 || cn
= SN.Collections.cVec
->
3919 let ty = match argl
with
3921 | _ -> arity_error id; err_witness env p in
3922 let ty1 = Reason.Ridx_vector
(fst e2
), Tprim Tint
in
3923 let env = Type.sub_type
p (Reason.index_class cn
) env ty2 ty1 in
3925 | Tclass
((_, cn
) as id, argl
)
3926 when cn
= SN.Collections.cMap
3927 || cn
= SN.Collections.cStableMap
3928 || cn
= SN.Collections.cDict
3929 || cn
= SN.Collections.cKeyset
->
3930 if cn
= SN.Collections.cKeyset
&& is_lvalue then begin
3931 Errors.keyset_set
p (Reason.to_pos
(fst ety1
));
3932 env, (Reason.Rwitness
p, Typing_utils.terr
env)
3934 let (k
, v
) = match argl
with
3935 | [t
] when cn
= SN.Collections.cKeyset
-> (t
, t
)
3936 | [k
; v
] when cn
<> SN.Collections.cKeyset
-> (k
, v
)
3939 let any = err_witness env p in
3942 let env, ty2 = TUtils.unresolved
env ty2 in
3943 let env = Type.sub_type
p (Reason.index_class cn
) env ty2 k
in
3945 (* Certain container/collection types are intended to be immutable/const,
3946 * thus they should never appear as a lvalue when indexing i.e.
3948 * $x[0] = 100; // ERROR
3951 | Tclass
((_, cn
) as id, argl
)
3952 when cn
= SN.Collections.cConstMap
3953 || cn
= SN.Collections.cImmMap
3954 || cn
= SN.Collections.cIndexish
3955 || cn
= SN.Collections.cKeyedContainer
->
3957 error_const_mutation
env p ety1
3959 let (k
, v
) = match argl
with
3963 let any = err_witness env p in
3966 let env = Type.sub_type
p (Reason.index_class cn
) env ty2 k
in
3968 | Tclass
((_, cn
) as id, argl
)
3969 when not
is_lvalue &&
3970 (cn
= SN.Collections.cConstVector
|| cn
= SN.Collections.cImmVector
) ->
3971 let ty = match argl
with
3973 | _ -> arity_error id; err_witness env p in
3974 let ty1 = Reason.Ridx
(fst e2
, fst ety1
), Tprim Tint
in
3975 let env = Type.sub_type
p (Reason.index_class cn
) env ty2 ty1 in
3977 | Tclass
((_, cn
), _)
3979 (cn
= SN.Collections.cConstVector
|| cn
= SN.Collections.cImmVector
) ->
3980 error_const_mutation
env p ety1
3981 | Tarraykind
(AKdarray
(k
, v
) | AKmap
(k
, v
)) ->
3982 let env, ty2 = TUtils.unresolved
env ty2 in
3983 let env = Type.sub_type
p Reason.index_array
env ty2 k
in
3985 | Tarraykind
((AKshape
_ | AKtuple
_) as akind) ->
3986 let key = Typing_arrays.static_array_access
env (Some e2
) in
3987 let env, result
= match key, akind with
3988 | Typing_arrays.AKtuple_index index
, AKtuple fields
->
3989 begin match IMap.get index fields
with
3991 let ty1 = Reason.Ridx
(fst e2
, fst ety1
), Tprim Tint
in
3992 let env = Type.sub_type
p Reason.index_array
env ty2 ty1 in
3996 | Typing_arrays.AKshape_key
field_name, AKshape fdm
->
3997 begin match Nast.ShapeMap.get
field_name fdm
with
3999 let env, ty2 = TUtils.unresolved
env ty2 in
4000 let env = Type.sub_type
p Reason.index_array
env ty2 k
in
4005 begin match result
with
4006 | Some
ty -> env, ty
4008 (* Key is dynamic, or static and not in the array - treat it as
4009 regular map or vec like array *)
4010 let env, ty1 = Typing_arrays.downcast_aktypes
env ety1
in
4011 array_get
is_lvalue p env ty1 e2
ty2
4013 | Terr
-> env, (Reason.Rwitness
p, Typing_utils.terr
env)
4014 | Tdynamic
-> env, ety1
4015 | Tany
| Tarraykind
(AKany
| AKempty
) ->
4016 env, (Reason.Rnone
, Typing_utils.tany env)
4018 let ty = Reason.Rwitness
p, Tprim Tstring
in
4019 let int = Reason.Ridx
(fst e2
, fst ety1
), Tprim Tint
in
4020 let env = Type.sub_type
p Reason.index_array
env ty2 int in
4026 let idx = int_of_string
(snd n
) in
4027 let nth = List.nth_exn
tyl idx in
4030 Errors.typing_error
p (Reason.string_of_ureason
Reason.index_tuple
);
4031 env, (Reason.Rwitness
p, Typing_utils.terr
env)
4034 Errors.typing_error
p (Reason.string_of_ureason
Reason.URtuple_access
);
4035 env, (Reason.Rwitness
p, Typing_utils.terr
env)
4037 | Tclass
((_, cn
) as id, argl
) when cn
= SN.Collections.cPair
->
4038 let (ty1, ty2) = match argl
with
4039 | [ty1; ty2] -> (ty1, ty2)
4042 let any = err_witness env p in
4048 let idx = int_of_string
(snd n
) in
4049 let nth = List.nth_exn
[ty1; ty2] idx in
4052 Errors.typing_error
p @@
4053 Reason.string_of_ureason
(Reason.index_class cn
);
4054 env, (Reason.Rwitness
p, Typing_utils.terr
env)
4057 Errors.typing_error
p (Reason.string_of_ureason
Reason.URpair_access
);
4058 env, (Reason.Rwitness
p, Typing_utils.terr
env)
4060 | Tshape
(_, fdm
) ->
4062 (match TUtils.shape_field_name
env p e2'
with
4064 (* there was already an error in shape_field name,
4065 don't report another one for a missing field *)
4066 env, (Reason.Rwitness
p, Typing_utils.terr
env)
4067 | Some field
-> (match ShapeMap.get field fdm
with
4069 Errors.undefined_field
4071 ~
name:(TUtils.get_printable_shape_field_name field
)
4072 ~shape_type_pos
:(Reason.to_pos
(fst ety1
));
4073 env, (Reason.Rwitness
p, Typing_utils.terr
env)
4074 | Some
{ sft_optional
= true; _ }
4075 when not
is_lvalue && not lhs_of_null_coalesce
->
4076 let declared_field =
4078 ~
f:(fun x -> Ast.ShapeField.compare field
x = 0)
4079 (ShapeMap.keys fdm
) in
4080 let declaration_pos = match declared_field with
4081 | Ast.SFlit
(p, _) | Ast.SFclass_const
((p, _), _) -> p in
4082 Errors.array_get_with_optional_field
4085 (TUtils.get_printable_shape_field_name field
);
4086 env, (Reason.Rwitness
p, Typing_utils.terr
env)
4087 | Some
{ sft_optional
= _; sft_ty
} -> env, sft_ty
)
4089 | Toption
tyl when lhs_of_null_coalesce
->
4090 (* Normally, we would not allow indexing into a nullable container,
4091 however, because the pattern shows up so frequently, we are allowing
4092 indexing into a nullable container as long as it is on the lhs of a
4094 array_get ~lhs_of_null_coalesce
is_lvalue p env tyl e2
ty2
4096 Errors.null_container
p
4098 "This is what makes me believe it can be null"
4101 env, (Reason.Rwitness
p, Typing_utils.terr
env)
4103 if Env.is_strict
env
4104 then error_array
env p ety1
4105 else env, (Reason.Rwitness
p, Typing_utils.tany env)
4106 | Tabstract
(AKnewtype
(ts
, [ty]), Some
(r, Tshape
(fk
, fields
)))
4107 when ts
= SN.FB.cTypeStructure
->
4108 let env, fields
= TS.transform_shapemap
env ty fields
in
4109 let ty = r, Tshape
(fk
, fields
) in
4110 array_get ~lhs_of_null_coalesce
is_lvalue p env ty e2
ty2
4113 try_over_concrete_supertypes env ety1
4115 array_get ~lhs_of_null_coalesce
is_lvalue p env ty e2
ty2
4117 begin match resl with
4120 when List.for_all
rest ~
f:(fun x -> ty_equal
(snd
x) (snd res
)) -> res
4121 | _ -> error_array
env p ety1
4123 | Tmixed
| Tnonnull
| Tprim
_ | Tvar
_ | Tfun
_
4124 | Tclass
(_, _) | Tanon
(_, _) ->
4125 error_array
env p ety1
4127 and array_append
p env ty1 =
4128 let env, ty1 = TUtils.fold_unresolved
env ty1 in
4129 let resl = try_over_concrete_supertypes env ty1
4134 | Tany
| Tarraykind
(AKany
| AKempty
) ->
4135 env, (r, Typing_utils.tany env)
4138 env, (r, Typing_utils.terr
env)
4139 (* No reactive append on vector and set *)
4140 | Tclass
((_, n
), [ty])
4141 when (n
= SN.Collections.cVector
4142 || n
= SN.Collections.cSet
) ->
4143 Env.error_if_reactive_context
env @@ begin fun () ->
4144 Errors.nonreactive_append
p;
4148 | Tclass
((_, n
), [ty])
4149 when n
= SN.Collections.cVec
|| n
= SN.Collections.cKeyset
->
4151 | Tclass
((_, n
), [])
4152 when n
= SN.Collections.cVector
|| n
= SN.Collections.cSet
->
4153 (* Handle the case where "Vector" or "Set" was used as a typehint
4154 without type parameters *)
4155 env, (r, Typing_utils.tany env)
4156 | Tclass
((_, n
), [tkey
; tvalue
]) when n
= SN.Collections.cMap
->
4157 Env.error_if_reactive_context
env @@ begin fun () ->
4158 Errors.nonreactive_append
p;
4160 (* You can append a pair to a map *)
4161 env, (Reason.Rmap_append
p, Tclass
((p, SN.Collections.cPair
),
4163 | Tclass
((_, n
), []) when n
= SN.Collections.cMap
->
4164 Env.error_if_reactive_context
env @@ begin fun () ->
4165 Errors.nonreactive_append
p;
4167 (* Handle the case where "Map" was used as a typehint without
4169 env, (Reason.Rmap_append
p,
4170 Tclass
((p, SN.Collections.cPair
), []))
4171 | Tarraykind
(AKvec
ty | AKvarray
ty) ->
4173 | Tdynamic
-> env, ty
4175 if Env.is_strict
env
4176 then error_array_append
env p ty1
4177 else env, (Reason.Rwitness
p, Typing_utils.tany env)
4178 | Tmixed
| Tnonnull
| Tarraykind
_ | Toption
_ | Tprim
_
4179 | Tvar
_ | Tfun
_ | Tclass
(_, _) | Ttuple
_
4180 | Tanon
(_, _) | Tunresolved
_ | Tshape
_ | Tabstract
_ ->
4181 error_array_append
env p ty1
4185 | _ -> error_array_append
env p ty1
4188 and error_array
env p (r, ty) =
4189 Errors.array_access
p (Reason.to_pos
r) (Typing_print.error
ty);
4190 env, err_witness env p
4192 and error_array_append
env p (r, ty) =
4193 Errors.array_append
p (Reason.to_pos
r) (Typing_print.error
ty);
4194 env, err_witness env p
4196 and error_const_mutation
env p (r, ty) =
4197 Errors.const_mutation
p (Reason.to_pos
r) (Typing_print.error
ty);
4198 env, err_witness env p
4201 * Checks if a class (given by cty) contains a given static method.
4203 * We could refactor this + class_get
4205 and class_contains_smethod
env cty
(_pos
, mid
) =
4206 let lookup_member ty =
4208 | _, Tclass
((_, c), _) ->
4209 (match Env.get_class
env c with
4212 Option.is_some
@@ Env.get_static_member
true env class_ mid
4215 let _env, tyl = TUtils.get_concrete_supertypes
env cty
in
4216 List.exists
tyl ~
f:lookup_member
4218 and class_get ~
is_method ~is_const ?
(explicit_tparams
=[]) ?
(incl_tc
=false)
4219 ?
(pos_params
: expr list
option) env cty
(p, mid
) cid =
4222 this_for_method
env cid cty
4226 type_expansions
= [];
4228 substs
= SMap.empty
;
4229 from_class
= Some
cid;
4231 class_get_ ~
is_method ~is_const ~
ety_env ~explicit_tparams ~incl_tc
4232 ~pos_params
env cid cty
(p, mid
)
4234 and class_get_ ~
is_method ~is_const ~
ety_env ?
(explicit_tparams
=[])
4235 ?
(incl_tc
=false) ~pos_params
env cid cty
4237 let env, cty
= Env.expand_type
env cty
in
4239 | r, Tany
-> env, (r, Typing_utils.tany env), None
4240 | r, Terr
-> env, err_witness env (Reason.to_pos
r), None
4241 | _, Tdynamic
-> env, cty
, None
4242 | _, Tunresolved
tyl ->
4243 let env, tyl = List.map_env
env tyl begin fun env ty ->
4245 class_get_ ~
is_method ~is_const ~
ety_env ~explicit_tparams ~incl_tc
4246 ~pos_params
env cid ty (p, mid
)
4249 let env, method_
= TUtils.in_var
env (fst cty
, Tunresolved
tyl) in
4253 begin match TUtils.get_concrete_supertypes
env cty
with
4255 class_get_ ~
is_method ~is_const ~
ety_env ~explicit_tparams ~incl_tc
4256 ~pos_params
env cid cty
(p, mid
)
4258 env, (Reason.Rwitness
p, Typing_utils.tany env), None
4260 | _, Tclass
((_, c), paraml
) ->
4261 let class_ = Env.get_class
env c in
4263 | None
-> env, (Reason.Rwitness
p, Typing_utils.tany env), None
4265 Typing_hooks.dispatch_smethod_hook
class_ paraml ~pos_params
(p, mid
)
4266 env ety_env.from_class ~
is_method ~is_const
;
4267 (* We need to instantiate generic parameters in the method signature *)
4270 substs
= Subst.make
class_.tc_tparams paraml
} in
4271 if is_const
then begin
4273 if incl_tc
then Env.get_const
env class_ mid
else
4274 match Env.get_typeconst
env class_ mid
with
4276 Errors.illegal_typeconst_direct_access
p;
4279 Env.get_const
env class_ mid
4283 smember_not_found
p ~is_const ~
is_method class_ mid
;
4284 env, (Reason.Rnone
, Typing_utils.terr
env), None
4285 | Some
{ cc_type
; cc_abstract
; cc_pos
; _ } ->
4286 let env, cc_type
= Phase.localize ~
ety_env env cc_type
in
4289 then Some
(cc_pos
, class_.tc_name ^
"::" ^ mid
)
4292 let smethod = Env.get_static_member
is_method env class_ mid
in
4295 (match Env.get_static_member
is_method env class_
4296 SN.Members.__callStatic
with
4298 smember_not_found
p ~is_const ~
is_method class_ mid
;
4299 env, (Reason.Rnone
, Typing_utils.terr
env), None
4300 | Some
{ce_visibility
= vis
; ce_type
= lazy (r, Tfun
ft); _} ->
4301 let p_vis = Reason.to_pos
r in
4302 TVis.check_class_access
p env (p_vis, vis
) cid class_;
4304 Phase.localize_ft ~use_pos
:p ~
ety_env ~explicit_tparams
:explicit_tparams
env ft in
4306 ft_arity
= Fellipsis
0;
4307 ft_tparams
= []; ft_params
= [];
4309 env, (r, Tfun
ft), None
4310 | _ -> assert false)
4311 | Some
{ ce_visibility
= vis
; ce_type
= lazy method_
; _ } ->
4312 let p_vis = Reason.to_pos
(fst method_
) in
4313 TVis.check_class_access
p env (p_vis, vis
) cid class_;
4315 begin match method_
with
4316 (* We special case Tfun here to allow passing in explicit tparams to localize_ft. *)
4319 Phase.localize_ft ~use_pos
:p ~
ety_env ~explicit_tparams
:explicit_tparams
env ft
4320 in env, (r, Tfun
ft)
4321 | _ -> Phase.localize ~
ety_env env method_
4326 | _, (Tmixed
| Tnonnull
| Tarraykind
_ | Toption
_
4327 | Tprim
_ | Tvar
_ | Tfun
_ | Ttuple
_ | Tanon
(_, _) | Tobject
4329 (* should never happen; static_class_id takes care of these *)
4330 env, (Reason.Rnone
, Typing_utils.tany env), None
4332 and smember_not_found
pos ~is_const ~
is_method class_ member_name
=
4334 if is_const
then `class_constant
4335 else if is_method then `static_method
4336 else `class_variable
in
4338 let cid = (class_.tc_pos
, class_.tc_name
) in
4339 Errors.smember_not_found
kind pos cid member_name hint
4341 match Env.suggest_static_member
is_method class_ member_name
with
4343 (match Env.suggest_member
is_method class_ member_name
with
4344 | None
when not
class_.tc_members_fully_known
->
4345 (* no error in this case ... the member might be present
4346 * in one of the parents of class_ that the typing cannot see *)
4351 error (`closest
(pos2
, v
))
4354 error (`did_you_mean
(pos2
, v
))
4356 and member_not_found
pos ~
is_method class_ member_name
r =
4357 let kind = if is_method then `method_
else `member
in
4358 let cid = class_.tc_pos
, class_.tc_name
in
4359 let reason = Reason.to_string
4360 ("This is why I think it is an object of type "^strip_ns
class_.tc_name
) r
4363 Errors.member_not_found
kind pos cid member_name hint
reason in
4364 match Env.suggest_member
is_method class_ member_name
with
4366 (match Env.suggest_static_member
is_method class_ member_name
with
4367 | None
when not
class_.tc_members_fully_known
->
4368 (* no error in this case ... the member might be present
4369 * in one of the parents of class_ that the typing cannot see *)
4373 | Some
(def_pos
, v
) ->
4374 error (`closest
(def_pos
, v
))
4376 | Some
(def_pos
, v
) ->
4377 error (`did_you_mean
(def_pos
, v
))
4379 and obj_get ~
is_method ~
nullsafe ?
(valkind = `other
) ?
(explicit_tparams
=[])
4380 ?
(pos_params
: expr list
option) env ty1 cid id k
=
4383 | Some
p when not
(type_could_be_null
env ty1) ->
4384 let env, (r, _) = Env.expand_type
env ty1 in
4385 Errors.nullsafe_not_needed
p
4387 "This is what makes me believe it cannot be null" r);
4390 let env, method_
, _ =
4391 obj_get_with_visibility ~
is_method ~
nullsafe ~
valkind ~pos_params
4392 ~explicit_tparams
env ty1 cid id k
in
4395 and obj_get_with_visibility ~
is_method ~
nullsafe ~
valkind ~pos_params
4396 ?
(explicit_tparams
=[]) env ty1 cid id k
=
4397 obj_get_ ~
is_method ~
nullsafe ~
valkind ~pos_params ~explicit_tparams
env ty1
4398 cid id k
(fun ty -> ty)
4400 (* We know that the receiver is a concrete class: not a generic with
4401 * bounds, or a Tunresolved. *)
4402 and obj_get_concrete_ty ~
is_method ~
valkind ~pos_params ?
(explicit_tparams
=[])
4403 env concrete_ty class_id
(id_pos
, id_str
as id) k_lhs =
4404 let default () = env, (Reason.Rwitness id_pos
, Typing_utils.tany env), None
in
4405 match concrete_ty
with
4406 | (r, Tclass
(x, paraml
)) ->
4408 match Env.get_class
env (snd
x) with
4412 | Some
class_info when not
is_method
4413 && not
(Env.is_strict
env)
4414 && class_info.tc_name
= SN.Classes.cStdClass
->
4417 | Some
class_info ->
4419 if List.length
paraml = 0
4420 then List.map
class_info.tc_tparams
4421 (fun _ -> Reason.Rwitness id_pos
, Typing_utils.tany env)
4423 let member_info = Env.get_member
is_method env class_info id_str
in
4424 Typing_hooks.dispatch_cmethod_hook
class_info paraml ~pos_params
id
4425 env (Some class_id
) ~
is_method;
4427 match member_info with
4428 | None
when not
is_method ->
4429 if not
(SN.Members.is_special_xhp_attribute id_str
)
4430 then member_not_found id_pos ~
is_method class_info id_str
r;
4435 match Env.get_member
is_method env class_info SN.Members.__call
with
4437 member_not_found id_pos ~
is_method class_info id_str
r;
4440 | Some
{ce_visibility
= vis
; ce_type
= lazy (r, Tfun
ft); _} ->
4441 let mem_pos = Reason.to_pos
r in
4442 TVis.check_obj_access id_pos
env (mem_pos, vis
);
4444 (* the return type of __call can depend on the
4445 * class params or be this *)
4446 let this_ty = k_lhs (r, (Tclass
(x, paraml))) in
4448 type_expansions
= [];
4450 substs
= Subst.make
class_info.tc_tparams
paraml;
4451 from_class
= Some class_id
;
4453 let env, ft = Phase.localize_ft ~use_pos
:id_pos ~
ety_env env ft in
4455 (* we change the params of the underlying
4456 * declaration to act as a variadic function
4457 * ... this transform cannot be done when
4458 * processing the declaration of call because
4459 * direct calls to $inst->__call are also
4462 ft_arity
= Fellipsis
0; ft_tparams
= []; ft_params
= []; } in
4464 let member_ty = (r, Tfun
ft) in
4465 env, member_ty, Some
(mem_pos, vis
)
4470 | Some
({ce_visibility
= vis
; ce_type
= lazy member_
; ce_const
; _ } as member_ce
) ->
4471 let mem_pos = Reason.to_pos
(fst member_
) in
4472 TVis.check_obj_access id_pos
env (mem_pos, vis
);
4473 let member_ty = Typing_enum.member_type
env member_ce
in
4474 let this_ty = k_lhs (r, (Tclass
(x, paraml))) in
4476 type_expansions
= [];
4478 substs
= Subst.make
class_info.tc_tparams
paraml;
4479 from_class
= Some class_id
;
4481 let env, member_ty =
4482 begin match member_ty with
4484 (* We special case function types here to be able to pass explicit type parameters. *)
4485 let (env, ft) = Phase.localize_ft ~use_pos
:id_pos ~explicit_tparams ~
ety_env env ft in
4487 | _ -> Phase.localize ~
ety_env env member_ty
4490 if ce_const
&& valkind = `lvalue
then
4491 if not
(env.Env.inside_constructor
&&
4492 (* expensive call behind short circuiting && *)
4493 SubType.is_sub_type
env (Env.get_self
env) concrete_ty
) then
4494 Errors.assigning_to_const id_pos
;
4496 env, member_ty, Some
(mem_pos, vis
)
4499 let ty = Reason.Rdynamic_prop id_pos
, Tdynamic
in
4507 Errors.non_object_member
4508 id_str id_pos
(Typing_print.error (snd concrete_ty
))
4509 (Reason.to_pos
(fst concrete_ty
));
4513 (* k_lhs takes the type of the object receiver *)
4514 and obj_get_ ~
is_method ~
nullsafe ~
valkind ~
(pos_params
: expr list
option) ?
(explicit_tparams
=[])
4515 env ty1 cid (id_pos
, id_str
as id) k
k_lhs =
4516 let env, ety1
= Env.expand_type
env ty1 in
4518 | _, Tunresolved
tyl ->
4519 let (env, vis
), tyl = List.map_env
(env, None
) tyl
4520 begin fun (env, vis
) ty ->
4522 obj_get_ ~
is_method ~
nullsafe ~
valkind ~pos_params
4523 ~explicit_tparams
env ty cid id k
k_lhs in
4524 (* There is one special case where we need to expose the
4525 * visibility outside of obj_get (checkout inst_meth special
4527 * We keep a witness of the "most restrictive" visibility
4528 * we encountered (position + visibility), to be able to
4529 * special case inst_meth.
4531 let vis = TVis.min_vis_opt
vis vis'
in
4534 let env, method_
= TUtils.in_var
env (fst ety1
, Tunresolved
(tyl)) in
4537 | p'
, (Tabstract
(ak
, Some
ty)) ->
4538 let k_lhs'
ty = match ak
with
4539 | AKnewtype
(_, _) -> k_lhs ty
4540 | _ -> k_lhs (p'
, Tabstract
(ak
, Some
ty)) in
4541 obj_get_ ~
is_method ~
nullsafe ~
valkind ~pos_params ~explicit_tparams
env ty cid id k
k_lhs'
4543 | p'
, (Tabstract
(ak
,_)) ->
4545 try_over_concrete_supertypes env ety1
4547 (* We probably don't want to rewrap new types for the 'this' closure *)
4548 (* TODO AKENN: we shouldn't refine constraints by changing
4549 * the type like this *)
4550 let k_lhs'
ty = match ak
with
4551 | AKnewtype
(_, _) -> k_lhs ty
4552 | _ -> k_lhs (p'
, Tabstract
(ak
, Some
ty)) in
4553 obj_get_concrete_ty ~
is_method ~
valkind ~pos_params ~explicit_tparams
env ty cid id k_lhs'
4555 begin match resl with
4557 Errors.non_object_member
4558 id_str id_pos
(Typing_print.error (snd ety1
))
4559 (Reason.to_pos
(fst ety1
));
4560 k
(env, err_witness env id_pos
, None
)
4562 | ((_env, (_, ty), _vis
) as res
)::rest ->
4563 if List.exists
rest (fun (_, (_,ty'
), _) -> ty'
<> ty)
4566 Errors.ambiguous_member
4567 id_str id_pos
(Typing_print.error (snd ety1
))
4568 (Reason.to_pos
(fst ety1
));
4569 k
(env, err_witness env id_pos
, None
)
4574 | _, Toption
ty -> begin match nullsafe with
4576 let env, method_
, x = obj_get_ ~
is_method ~
nullsafe ~
valkind
4577 ~pos_params ~explicit_tparams
env ty cid id k
k_lhs in
4578 let env, method_
= TUtils.non_null
env method_
in
4579 env, (Reason.Rnullsafe_op
p1, Toption method_
), x
4581 Errors.null_member id_str id_pos
4583 "This is what makes me believe it can be null"
4586 k
(env, (fst ety1
, Typing_utils.terr
env), None
)
4589 k
(obj_get_concrete_ty ~
is_method ~
valkind ~pos_params ~explicit_tparams
env ety1
cid id k_lhs)
4592 (* Return true if the type ty1 contains the null value *)
4593 and type_could_be_null
env ty1 =
4594 let _, tyl = TUtils.get_concrete_supertypes
env ty1 in
4598 Toption
_ | Tunresolved
_ | Tmixed
| Tany
| Terr
| Tdynamic
-> true
4599 | Tarraykind
_ | Tprim
_ | Tvar
_ | Tfun
_ | Tabstract
_
4600 | Tclass
(_, _) | Ttuple
_ | Tanon
(_, _) | Tobject
4601 | Tshape
_ | Tnonnull
-> false)
4603 and class_id_for_new
p env cid =
4604 let env, te, ty = static_class_id
p env cid in
4605 (* Need to deal with union case *)
4606 let rec get_info res
tyl =
4608 | [] -> env, te, res
4611 | Tunresolved
tyl'
->
4612 get_info res
(tyl'
@ tyl)
4614 (* Instantiation on an abstract class (e.g. from classname<T>) is
4615 * via the base type (to check constructor args), but the actual
4616 * type `ty` must be preserved. *)
4617 match TUtils.get_base_type
env ty with
4618 | _, Tclass
(sid
, _) ->
4620 let class_ = Env.get_class
env (snd sid
) in
4622 | None
-> get_info res
tyl
4623 | Some
class_info -> get_info ((sid
, class_info, ty)::res
) tyl
4625 | _, (Tany
| Terr
| Tmixed
| Tnonnull
| Tarraykind
_ | Toption
_
4626 | Tprim
_ | Tvar
_ | Tfun
_ | Tabstract
(_, _) | Ttuple
_
4627 | Tanon
(_, _) | Tunresolved
_ | Tobject
| Tshape
_ | Tdynamic
) ->
4631 (* To be a valid trait declaration, all of its 'require extends' must
4632 * match; since there's no multiple inheritance, it follows that all of
4633 * the 'require extends' must belong to the same inheritance hierarchy
4634 * and one of them should be the child of all the others *)
4635 and trait_most_concrete_req_class trait
env =
4636 List.fold_left trait
.tc_req_ancestors ~
f:begin fun acc
(_p
, ty) ->
4637 let _r, (_p
, name), _paraml
= TUtils.unwrap_class_type
ty in
4638 let keep = match acc
with
4639 | Some
(c, _ty
) -> SMap.mem
name c.tc_ancestors
4644 let class_ = Env.get_class
env name in
4647 | Some
{ tc_kind
= Ast.Cinterface
; _ } -> acc
4648 | Some
{ tc_kind
= Ast.Ctrait
; _ } ->
4649 (* this is an error case for which the nastCheck spits out
4650 * an error, but does *not* currently remove the offending
4651 * 'require extends' or 'require implements' *)
4653 | Some
c -> Some
(c, ty)
4657 (* For explicit type arguments we support a wildcard syntax `_` for which
4658 * Hack will generate a fresh type variable
4660 and type_argument
env hint
=
4662 | (_, Happly
((_, id), [])) when id = SN.Typehints.wildcard
->
4663 Env.fresh_unresolved_type
env
4665 Phase.hint_locl
env hint
4667 (* If there are no explicit type arguments then generate fresh type variables
4668 * for all of them. Otherwise, check the arity, and use the explicit types *)
4669 and type_arguments
env p class_name tparams hintl
=
4670 let default () = List.map_env
env tparams
begin fun env _ ->
4671 Env.fresh_unresolved_type
env end in
4674 else if List.length hintl
!= List.length tparams
4676 Errors.type_arity
p class_name
(string_of_int
(List.length tparams
));
4680 List.map_env
env hintl type_argument
4682 (* When invoking a method the class_id is used to determine what class we
4683 * lookup the method in, but the type of 'this' will be the late bound type.
4687 * public static function get(): this { return new static(); }
4689 * public static function alias(): this { return self::get(); }
4692 * In C::alias, when we invoke self::get(), 'self' is resolved to the class
4693 * in the lexical scope (C), so call C::get. However the method is executed in
4694 * the current context, so static inside C::get will be resolved to the late
4695 * bound type (get_called_class() within C::alias).
4697 * This means when determining the type of this, CIparent and CIself should be
4698 * changed to CIstatic. For the other cases of C::get() or $c::get(), we only
4699 * look at the left hand side of the '::' and use the type type associated
4702 * Thus C::get() will return a type C, while $c::get() will return the same
4705 and this_for_method
env cid default_ty
= match cid with
4706 | CIparent
| CIself
| CIstatic
->
4707 let p = Reason.to_pos
(fst default_ty
) in
4708 let env, _te
, ty = static_class_id
p env CIstatic
in
4709 ExprDepTy.make
env CIstatic
ty
4713 and static_class_id
p env =
4714 let make_result env te ty =
4715 env, (Some
ty, te), ty in
4718 (match Env.get_self
env with
4719 | _, Tclass
((_, self
), _) ->
4720 (match Env.get_class
env self
with
4722 {tc_kind
= Ast.Ctrait
; _}
4724 (match trait_most_concrete_req_class trait
env with
4726 Errors.parent_in_trait
p;
4727 make_result env T.CIparent
(Reason.Rwitness
p, Typing_utils.terr
env)
4728 | Some
(_, parent_ty
) ->
4729 (* inside a trait, parent is SN.Typehints.this, but with the
4730 * type of the most concrete class that the trait has
4731 * "require extend"-ed *)
4732 let r = Reason.Rwitness
p in
4733 let env, parent_ty
= Phase.localize_with_self
env parent_ty
in
4734 make_result env T.CIparent
(r, TUtils.this_of parent_ty
)
4737 let parent = Env.get_parent
env in
4738 let parent_defined = snd
parent <> Typing_utils.tany env in
4739 if not
parent_defined
4740 then Errors.parent_undefined
p;
4741 let r = Reason.Rwitness
p in
4742 let env, parent = Phase.localize_with_self
env parent in
4743 (* parent is still technically the same object. *)
4744 make_result env T.CIparent
(r, TUtils.this_of
(r, snd
parent))
4746 | _, (Terr
| Tany
| Tmixed
| Tnonnull
| Tarraykind
_ | Toption
_ | Tprim
_
4747 | Tfun
_ | Ttuple
_ | Tshape
_ | Tvar
_ | Tdynamic
4748 | Tanon
(_, _) | Tunresolved
_ | Tabstract
(_, _) | Tobject
4750 let parent = Env.get_parent
env in
4751 let parent_defined = snd
parent <> Typing_utils.tany env in
4752 if not
parent_defined
4753 then Errors.parent_undefined
p;
4754 let r = Reason.Rwitness
p in
4755 let env, parent = Phase.localize_with_self
env parent in
4756 (* parent is still technically the same object. *)
4757 make_result env T.CIparent
(r, TUtils.this_of
(r, snd
parent))
4760 make_result env T.CIstatic
4761 (Reason.Rwitness
p, TUtils.this_of
(Env.get_self
env))
4763 make_result env T.CIself
4764 (Reason.Rwitness
p, snd
(Env.get_self
env))
4766 let class_ = Env.get_class
env (snd
c) in
4769 make_result env (T.CI
(c, hl
)) (Reason.Rwitness
p, Typing_utils.tany env)
4771 let env, tyl = type_arguments
env p (snd
c) class_.tc_tparams hl
in
4772 make_result env (T.CI
(c, hl
))
4773 (Reason.Rwitness
(fst
c), Tclass
(c, tyl))
4775 | CIexpr
(p, _ as e) ->
4776 let env, te, ty = expr
env e in
4777 let rec resolve_ety ty =
4778 let env, ty = TUtils.fold_unresolved
env ty in
4779 let _, ty = Env.expand_type
env ty in
4780 match TUtils.get_base_type
env ty with
4781 | _, Tabstract
(AKnewtype
(classname
, [the_cls
]), _) when
4782 classname
= SN.Classes.cClassname
-> resolve_ety the_cls
4783 | _, Tabstract
(AKgeneric
_, _)
4785 | r, Tunresolved
tyl -> r, Tunresolved
(List.map
tyl resolve_ety)
4786 | _, Tvar
_ as ty -> resolve_ety ty
4787 | _, Tdynamic
as ty -> ty
4788 | _, (Tany
| Tprim Tstring
| Tabstract
(_, None
) | Tmixed
| Tobject
)
4789 when not
(Env.is_strict
env) ->
4790 Reason.Rwitness
p, Typing_utils.tany env
4791 | _, (Terr
| Tany
| Tmixed
| Tnonnull
| Tarraykind
_ | Toption
_
4792 | Tprim
_ | Tfun
_ | Ttuple
_
4793 | Tabstract
((AKenum
_ | AKdependent
_ | AKnewtype
_), _)
4794 | Tanon
(_, _) | Tobject
| Tshape
_ as ty
4796 Errors.expected_class ~suffix
:(", but got "^
Typing_print.error ty) p;
4797 Reason.Rwitness
p, Typing_utils.terr
env in
4798 let result_ty = resolve_ety ty in
4799 make_result env (T.CIexpr
te) result_ty
4801 and call_construct
p env class_ params el uel
cid =
4802 let cid = if cid = CIparent
then CIstatic
else cid in
4803 let env, tcid
, cid_ty
= static_class_id
p env cid in
4805 type_expansions
= [];
4807 substs
= Subst.make
class_.tc_tparams
params;
4808 from_class
= Some
cid;
4810 let env = Phase.check_tparams_constraints ~use_pos
:p ~
ety_env env class_.tc_tparams
in
4811 if class_.tc_is_xhp
then env, tcid
, [], [] else
4812 let cstr = Env.get_construct
env class_ in
4813 let mode = Env.get_mode
env in
4814 Typing_hooks.dispatch_constructor_hook
class_ params env p;
4815 match (fst
cstr) with
4818 (mode = FileInfo.Mstrict
|| mode = FileInfo.Mpartial
) &&
4819 class_.tc_members_fully_known
4820 then Errors.constructor_no_args
p;
4821 let env, tel, _tyl
= exprs env el
in
4823 | Some
{ ce_visibility
= vis; ce_type
= lazy m
; _ } ->
4824 TVis.check_obj_access
p env (Reason.to_pos
(fst m
), vis);
4825 let env, m
= Phase.localize ~
ety_env env m
in
4826 let env, tel, tuel
, _ty
= call ~
expected:None
p env m el uel
in
4827 env, tcid
, tel, tuel
4829 and check_arity ?
(check_min
=true) ?
(did_unpack
=false) pos pos_def
(arity:int) exp_arity
=
4830 let exp_min = (Typing_defs.arity_min exp_arity
) in
4831 if check_min
&& arity < exp_min then
4832 Errors.typing_too_few_args
pos pos_def
;
4833 match exp_arity
with
4834 | Fstandard
(_, exp_max
) ->
4835 let arity = if did_unpack
then arity + 1 else arity in
4836 if (arity > exp_max
)
4837 then Errors.typing_too_many_args
pos pos_def
;
4838 | Fvariadic
_ | Fellipsis
_ -> ()
4840 and check_lambda_arity lambda_pos def_pos lambda_arity expected_arity
=
4841 let expected_min = Typing_defs.arity_min expected_arity
in
4842 match lambda_arity
, expected_arity
with
4843 | Fstandard
(lambda_min
, _), Fstandard
_ ->
4844 if lambda_min
< expected_min
4845 then Errors.typing_too_few_args lambda_pos def_pos
;
4846 if lambda_min
> expected_min
4847 then Errors.typing_too_many_args lambda_pos def_pos
4850 and check_deprecated
p { ft_pos
; ft_deprecated
; _ } =
4851 match ft_deprecated
with
4852 | Some s
-> Errors.deprecated_use
p ft_pos s
4855 (* The variadic capture argument is an array listing the passed
4856 * variable arguments for the purposes of the function body; callsites
4857 * should not unify with it *)
4858 and variadic_param
env ft =
4859 match ft.ft_arity
with
4860 | Fvariadic
(_, p_ty
) -> env, Some p_ty
4861 | Fellipsis
_ | Fstandard
_ -> env, None
4863 and param_modes
{ fp_pos
; fp_kind
; _ } (pos, e) =
4864 match fp_kind
, e with
4865 | FPnormal
, Unop
(Ast.Uref
, _) ->
4866 Errors.pass_by_ref_annotation_unexpected
pos fp_pos
4867 | FPnormal
, Callconv
_ ->
4868 Errors.inout_annotation_unexpected
pos fp_pos
4870 | FPref
, Unop
(Ast.Uref
, _) -> ()
4871 | FPref
, Callconv
(kind, _) ->
4873 (* HHVM supports pass-by-ref for arguments annotated as 'inout'. *)
4877 Errors.pass_by_ref_annotation_missing
pos fp_pos
4878 (* HHVM also allows '&' on arguments to inout parameters via interop layer. *)
4879 | FPinout
, Unop
(Ast.Uref
, _)
4880 | FPinout
, Callconv
(Ast.Pinout
, _) -> ()
4882 Errors.inout_annotation_missing
pos fp_pos
4884 and inout_write_back
env { fp_type
; _ } (_, e) =
4886 | Callconv
(Ast.Pinout
, e1
) ->
4887 (* Translate the write-back semantics of inout parameters.
4889 * This matters because we want to:
4890 * (1) make sure we can write to the original argument
4891 * (modifiable lvalue check)
4892 * (2) allow for growing of locals / Tunresolveds (type side effect)
4893 * but otherwise unify the argument type with the parameter hint
4895 let env, _te
, _ty
= assign_
(fst e1
) Reason.URparam_inout
env e1 fp_type
in
4899 and call ~
expected ?receiver_type
pos env fty el uel
=
4900 let env, tel, tuel
, ty = call_ ~
expected ~receiver_type
pos env fty el uel
in
4901 (* We need to solve the constraints after every single function call.
4902 * The type-checker is control-flow sensitive, the same value could
4903 * have different type depending on the branch that we are in.
4904 * When this is the case, a call could violate one of the constraints
4906 let env = Env.check_todo
env in
4909 and call_ ~
expected ~receiver_type
pos env fty el uel
=
4910 let make_unpacked_traversable_ty pos ty =
4911 let unpack_r = Reason.Runpack_param
pos in
4912 unpack_r, Tclass
((pos, SN.Collections.cTraversable
), [ty])
4914 let env, efty
= Env.expand_type
env fty in
4916 | _, (Terr
| Tany
| Tunresolved
[] | Tdynamic
) ->
4917 let el = el @ uel
in
4918 let env, tel = List.map_env
env el begin fun env elt
->
4919 let env, te, arg_ty
=
4920 expr ~
expected:(pos, Reason.URparam
, (Reason.Rnone
, Typing_utils.tany env)) ~is_func_arg
:true env elt
4924 | _, Callconv
(Ast.Pinout
, e1
) ->
4925 let env, _te
, _ty
= assign_
(fst e1
) Reason.URparam_inout
env e1 efty
in
4927 | _, Unop
(Ast.Uref
, e1
) ->
4928 let env, _te
, _ty
= assign_
(fst e1
) Reason.URparam
env e1 efty
in
4931 let env, _arg_ty
= check_valid_rvalue
pos env arg_ty
in
4934 let env = call_untyped_unpack
env uel
in
4935 Typing_hooks.dispatch_fun_call_hooks
[] (List.map
(el @ uel
) fst
) env;
4937 if snd efty
= Tdynamic
then
4938 (Reason.Rdynamic_call
pos, Tdynamic
)
4939 else (Reason.Rnone
, Typing_utils.tany env)
4942 | _, Tunresolved
[ty] ->
4943 call ~
expected pos env ty el uel
4944 | r, Tunresolved
tyl ->
4945 let env, retl
= List.map_env
env tyl begin fun env ty ->
4946 let env, _, _, ty = call ~
expected pos env ty el uel
in env, ty
4948 let env, ty = TUtils.in_var
env (r, Tunresolved retl
) in
4951 (* Typing of format string functions. It is dependent on the arguments (el)
4952 * so it cannot be done earlier.
4954 let env, ft = Typing_exts.retype_magic_func
env ft el in
4955 check_deprecated
pos ft;
4956 let pos_def = Reason.to_pos r2
in
4957 let env, var_param
= variadic_param
env ft in
4959 Typing_reactivity.check_call env receiver_type
pos r2
ft;
4961 (* Force subtype with expected result *)
4962 let env = check_expected_ty
"Call result" env ft.ft_ret
expected in
4964 let is_lambda e = match snd
e with Efun
_ -> true | _ -> false in
4966 let get_next_param_info paraml =
4971 var_param
, paraml in
4973 (* Given an expected function type ft, check types for the non-unpacked
4974 * arguments. Don't check lambda expressions if check_lambdas=false *)
4975 let rec check_args check_lambdas
env el paraml =
4977 (* We've got an argument *)
4978 | ((pos, _ as e), opt_result
) :: el ->
4979 (* Pick up next parameter type info *)
4980 let opt_param, paraml = get_next_param_info paraml in
4981 let env, one_result
=
4982 if is_lambda e && not check_lambdas
|| Option.is_some opt_result
4983 then env, opt_result
4985 begin match opt_param with
4988 expr ~is_func_arg
:true ~accept_using_var
:param
.fp_accept_disposable
4989 ~
expected:(pos, Reason.URparam
, param
.fp_type
) env e in
4990 let env = call_param
env param
(e, ty) in
4993 let env, te, ty = expr ~
expected:(pos, Reason.URparam
, (Reason.Rnone
, Typing_utils.tany env))
4994 ~is_func_arg
:true env e in
4997 let env, rl
, paraml = check_args check_lambdas
env el paraml in
4998 env, (e, one_result
)::rl
, paraml
5003 (* First check the non-lambda arguments. For generic functions, this
5004 * is likely to resolve type variables to concrete types *)
5005 let rl = List.map
el (fun e -> (e, None
)) in
5006 let env, rl, _ = check_args false env rl ft.ft_params
in
5007 (* Now check the lambda arguments, hopefully with type variables resolved *)
5008 let env, rl, paraml = check_args true env rl ft.ft_params
in
5009 (* We expect to see results for all arguments after this second pass *)
5013 | None
-> failwith
"missing parameter in check_args" in
5014 let tel = List.map
rl (fun (_, opt
) -> fst
(get_param opt
)) in
5015 let env, tuel
, arity, check_min
, did_unpack
=
5017 | [] -> env, [], List.length
el, true, false
5019 (* Enforces that e is unpackable. If e is a tuple, check types against
5020 * parameter types *)
5021 let env, te, ety
= expr
env e in
5024 let rec check_elements env tyl paraml =
5028 let opt_param, paraml = get_next_param_info paraml in
5029 match opt_param with
5032 let env = call_param
env param
(e, ty) in
5033 check_elements env tyl paraml in
5034 let env = check_elements env tyl paraml in
5035 env, [te], List.length
el + List.length
tyl, true, false
5037 let param_tyl = List.map
paraml (fun param
-> param
.fp_type
) in
5038 let add_variadic_param_ty param_tyl =
5039 match var_param
with
5040 | Some param
-> param
.fp_type
:: param_tyl
5041 | None
-> param_tyl in
5042 let param_tyl = add_variadic_param_ty param_tyl in
5044 let env = List.fold_right
param_tyl ~init
:env
5045 ~
f:(fun param_ty env ->
5046 let traversable_ty = make_unpacked_traversable_ty pos param_ty in
5047 Type.sub_type
pos Reason.URparam
env ety
traversable_ty)
5049 let check_min = (TypecheckerOptions.experimental_feature_enabled
5050 (Env.get_options
env)
5051 TypecheckerOptions.experimental_unpacking_check_arity
) in
5052 env, [te], List.length
el, check_min, true
5054 (* If we unpacked an array, we don't check arity exactly. Since each
5055 * unpacked array consumes 1 or many parameters, it is nonsensical to say
5056 * that not enough args were passed in (so we don't do the min check).
5058 let () = check_arity ~
check_min ~did_unpack
pos pos_def arity ft.ft_arity
in
5059 (* Variadic params cannot be inout so we can stop early *)
5060 let env = wfold_left2 inout_write_back
env ft.ft_params
el in
5061 Typing_hooks.dispatch_fun_call_hooks
5062 ft.ft_params
(List.map
(el @ uel
) fst
) env;
5063 env, tel, tuel
, ft.ft_ret
5064 | r2
, Tanon
(arity, id) ->
5065 let env, tel, tyl = exprs ~is_func_arg
:true env el in
5066 let expr_for_unpacked_expr_list env = function
5067 | [] -> env, [], None
, Pos.none
5068 | (pos, _) as e :: _ ->
5069 let env, te, ety
= expr
env e in
5070 env, [te], Some ety
, pos
5072 let append_tuple_types tyl = function
5073 | Some
(_, Ttuple tuple_tyl
) -> tyl @ tuple_tyl
5076 let determine_arity env min_arity
pos = function
5078 | Some
(_, Ttuple
_) ->
5079 env, Fstandard
(min_arity
, min_arity
)
5081 (* We need to figure out the underlying type of the unpacked expr type.
5083 * For example, assume the call is:
5085 * where $y is a variadic or collection of strings.
5087 * $y may have the type Tarraykind or Traversable, however we need to
5088 * pass Fvariadic a param of type string.
5090 * Assuming $y has type Tarraykind, in order to get the underlying type
5091 * we create a fresh_type(), wrap it in a Traversable and make that
5092 * Traversable a super type of the expr type (Tarraykind). This way
5093 * we can infer the underlying type and create the correct param for
5096 let ty = Env.fresh_type
() in
5097 let traversable_ty = make_unpacked_traversable_ty pos ty in
5098 let env = Type.sub_type
pos Reason.URparam
env ety
traversable_ty in
5104 fp_accept_disposable
= false;
5108 env, Fvariadic
(min_arity
, param)
5110 let env, tuel
, uety_opt
, uepos
= expr_for_unpacked_expr_list env uel
in
5111 let tyl = append_tuple_types tyl uety_opt
in
5112 let env, call_arity
= determine_arity env (List.length
tyl) uepos uety_opt
in
5113 let anon = Env.get_anonymous
env id in
5114 let fpos = Reason.to_pos r2
in
5117 Errors.anonymous_recursive_call
pos;
5118 env, tel, tuel
, err_witness env pos
5119 | Some
(_, _, counter
, _, anon) ->
5120 let () = check_arity
pos fpos (Typing_defs.arity_min call_arity
) arity in
5121 let tyl = List.map
tyl TUtils.default_fun_param
in
5122 counter
:= !counter
+ 1;
5123 let env, _, ty = anon ~
el env tyl call_arity
in
5125 | _, Tarraykind
_ when not
(Env.is_strict
env) ->
5126 (* Relaxing call_user_func to work with an array in partial mode *)
5127 let env = call_untyped_unpack
env uel
in
5128 env, [], [], (Reason.Rnone
, Typing_utils.tany env)
5131 let env = call_untyped_unpack
env uel
in
5132 env, [], [], err_witness env pos
5135 and call_param
env param ((pos, _ as e), arg_ty
) =
5136 (match param.fp_name
with
5138 | Some
name -> Typing_suggest.save_param
name env param.fp_type arg_ty
5140 param_modes
param e;
5141 let env, arg_ty
= check_valid_rvalue
pos env arg_ty
in
5143 (* When checking params the type 'x' may be expression dependent. Since
5144 * we store the expression id in the local env for Lvar, we want to apply
5147 let env, dep_ty
= match snd
e with
5148 | Lvar
_ -> ExprDepTy.make
env (CIexpr
e) arg_ty
5149 | _ -> env, arg_ty
in
5150 Type.sub_type
pos Reason.URparam
env dep_ty
param.fp_type
5152 and call_untyped_unpack
env uel
= match uel
with
5153 (* In the event that we don't have a known function call type, we can still
5154 * verify that any unpacked arguments (`...$args`) are something that can
5155 * be actually unpacked. *)
5158 let env, _, ety
= expr
env e in
5160 | _, Ttuple
_ -> env (* tuples are always fine *)
5163 let ty = Env.fresh_type
() in
5164 let unpack_r = Reason.Runpack_param
pos in
5165 let unpack_ty = unpack_r, Tclass
((pos, SN.Collections.cTraversable
), [ty]) in
5166 Type.sub_type
pos Reason.URparam
env ety
unpack_ty
5171 Errors.bad_call
p (Typing_print.error ty)
5173 and unop ~is_func_arg ~
forbid_uref p env uop
te ty =
5174 let check_dynamic env ty ~
f =
5175 if TUtils.is_dynamic
env ty then
5176 env, (fst
ty, Tdynamic
)
5179 let make_result env te result_ty =
5180 env, T.make_typed_expr
p result_ty (T.Unop
(uop
, te)), result_ty in
5183 Async.enforce_nullable_or_not_awaitable
env p ty;
5184 (* !$x (logical not) works with any type, so we just return Tbool *)
5185 make_result env te (Reason.Rlogic_ret
p, Tprim Tbool
)
5187 (* ~$x (bitwise not) only works with int *)
5189 check_dynamic env ty ~
f:begin fun () ->
5190 let int_ty = (Reason.Rarith
p, Tprim Tint
) in
5191 Type.sub_type
p Reason.URnone
env ty int_ty, int_ty
5194 make_result env te ty
5201 (* math operators work with int or floats, so we call sub_type *)
5202 let env, ty = check_dynamic env ty ~
f:begin fun () ->
5203 Type.sub_type
p Reason.URnone
env ty (Reason.Rarith
p, Tprim Tnum
), ty
5206 make_result env te ty
5208 (* We basically just ignore references in non-strict files *)
5209 if forbid_uref && TypecheckerOptions.experimental_feature_enabled
5210 (Env.get_options
env)
5211 TypecheckerOptions.experimental_disallow_refs_in_array
5212 then Errors.binding_ref_in_array
p
5213 else if Env.is_strict
env && not is_func_arg
5214 then Errors.reference_expr
p;
5215 make_result env te ty
5217 (* Silencing does not change the type *)
5218 make_result env te ty
5220 and binop in_cond
p env bop
p1 te1 ty1 p2 te2
ty2 =
5222 match Env.expand_type
env ty with
5223 | (_, (_, (Tany
| Terr
))) -> true
5224 | (_, (_, Tunresolved
tyl)) -> List.for_all
tyl is_any
5226 (* Test if `ty` is *not* the any type (or a variant thereof) and
5227 * is a subtype of the primitive type `prim`. *)
5228 let is_sub_prim env ty prim
=
5229 let ty_prim = (Reason.Rarith
p, Tprim prim
) in
5230 if not
(is_any ty) && SubType.is_sub_type
env ty ty_prim
5231 then Some
(fst
ty) else None
in
5232 (* Test if `ty` is *not* the any type (or a variant thereof) and
5233 * is a subtype of `num` but is not a subtype of `int` *)
5234 let is_sub_num_not_sub_int env ty =
5235 let ty_num = (Reason.Rarith
p, Tprim Tnum
) in
5236 let ty_int = (Reason.Rarith
p, Tprim Tint
) in
5237 if not
(is_any ty) && SubType.is_sub_type
env ty ty_num
5238 && not
(SubType.is_sub_type
env ty ty_int)
5239 then Some
(fst
ty) else None
in
5240 (* Force ty1 to be a subtype of ty2 (unless it is any) *)
5241 let enforce_sub_ty env ty1 ty2 =
5242 let env = Type.sub_type
p Reason.URnone
env ty1 ty2 in
5243 Env.expand_type
env ty1 in
5244 let make_result env te1 te2
ty =
5245 env, T.make_typed_expr
p ty (T.Binop
(bop
, te1, te2
)), ty in
5246 let check_dynamic f =
5247 if TUtils.is_dynamic
env ty1 then
5249 match is_sub_prim env ty2 Tfloat
with
5251 (* dynamic op float = float *)
5254 (* dynamic op _ = num *)
5255 (fst
ty2, Tprim Tnum
)
5257 make_result env te1 te2
result_prim
5258 else if TUtils.is_dynamic
env ty2 then
5260 match is_sub_prim env ty1 Tfloat
with
5262 (* dynamic op float = float *)
5265 (* dynamic op _ = num *)
5266 (fst
ty1, Tprim Tnum
)
5268 make_result env te1 te2
result_prim
5273 let env, ty1 = TUtils.fold_unresolved
env ty1 in
5274 let env, ty2 = TUtils.fold_unresolved
env ty2 in
5275 let env, ety1
= Env.expand_type
env ty1 in
5276 let env, ety2
= Env.expand_type
env ty2 in
5277 (match ety1
, ety2
with
5278 (* For array<V1>+array<V2> and array<K1,V1>+array<K2,V2>, allow
5279 * the addition to produce a supertype. (We could also handle
5280 * when they have mismatching annotations, but we get better error
5281 * messages if we just let those get unified in the next case. *)
5282 (* The general types are:
5283 * function<Tk,Tv>(array<Tk,Tv>, array<Tk,Tv>): array<Tk,Tv>
5284 * function<T>(array<T>, array<T>): array<T>
5285 * and subtyping on the arguments deals with everything
5287 | (_, Tarraykind
(AKmap
_ as ak
)), (_, Tarraykind
(AKmap
_))
5288 | (_, Tarraykind
(AKvec
_ as ak
)), (_, Tarraykind
(AKvec
_)) ->
5289 let env, a_sup
= Env.fresh_unresolved_type
env in
5290 let env, b_sup
= Env.fresh_unresolved_type
env in
5291 let res_ty = Reason.Rarray_plus_ret
p, Tarraykind
(
5293 | AKvec
_ -> AKvec a_sup
5294 | AKmap
_ -> AKmap
(a_sup
, b_sup
)
5297 let env = Type.sub_type
p1 Reason.URnone
env ety1
res_ty in
5298 let env = Type.sub_type
p2 Reason.URnone
env ety2
res_ty in
5299 make_result env te1 te2
res_ty
5300 | (_, Tarraykind
_), (_, Tarraykind
(AKshape
_)) ->
5301 let env, ty2 = Typing_arrays.downcast_aktypes
env ty2 in
5302 binop in_cond
p env bop
p1 te1 ty1 p2 te2
ty2
5303 | (_, Tarraykind
(AKshape
_)), (_, Tarraykind
_) ->
5304 let env, ty1 = Typing_arrays.downcast_aktypes
env ty1 in
5305 binop in_cond
p env bop
p1 te1 ty1 p2 te2
ty2
5306 | (_, Tarraykind
_), (_, Tarraykind
_)
5307 | (_, (Tany
| Terr
)), (_, Tarraykind
_)
5308 | (_, Tarraykind
_), (_, Tany
) ->
5309 let env, ty = Type.unify
p Reason.URnone
env ty1 ty2 in
5310 make_result env te1 te2
ty
5311 | (_, Tdynamic
), (_, Tdynamic
) ->
5312 make_result env te1 te2
(Reason.Rarith
p, Tdynamic
)
5313 | (_, (Tany
| Terr
| Tmixed
| Tnonnull
| Tarraykind
_ | Toption
_ | Tdynamic
5314 | Tprim
_ | Tvar
_ | Tfun
_ | Tabstract
(_, _) | Tclass
(_, _)
5315 | Ttuple
_ | Tanon
(_, _) | Tunresolved
_ | Tobject
| Tshape
_
5318 let env, texpr
, ty =
5319 binop in_cond
p env Ast.Minus
p1 te1 ty1 p2 te2
ty2 in
5320 match snd texpr
with
5321 | T.Binop
(_, te1, te2
) -> make_result env te1 te2
ty
5324 | Ast.Minus
| Ast.Star
-> check_dynamic begin fun () ->
5325 let env, ty1 = enforce_sub_ty env ty1 (Reason.Rarith
p1, Tprim Tnum
) in
5326 let env, ty2 = enforce_sub_ty env ty2 (Reason.Rarith
p2, Tprim Tnum
) in
5327 (* If either side is a float then float: 1.0 - 1 -> float *)
5329 * function(float, num): float
5330 * function(num, float): float
5332 match is_sub_prim env ty1 Tfloat
, is_sub_prim env ty2 Tfloat
with
5333 | (Some
r, _) | (_, Some
r) ->
5334 make_result env te1 te2
(r, Tprim Tfloat
)
5336 (* Both sides are integers, then integer: 1 - 1 -> int *)
5338 * function(int, int): int
5340 match is_sub_prim env ty1 Tint
, is_sub_prim env ty2 Tint
with
5341 | (Some
_, Some
_) ->
5342 make_result env te1 te2
(Reason.Rarith_ret
p, Tprim Tint
)
5344 (* Either side is a non-int num then num *)
5346 * function(num, num): num
5348 match is_sub_num_not_sub_int env ty1,
5349 is_sub_num_not_sub_int env ty2 with
5350 | (Some
r, _) | (_, Some
r) ->
5351 make_result env te1 te2
(r, Tprim Tnum
)
5353 | _, _ -> make_result env te1 te2
ty1
5355 | Ast.Slash
| Ast.Starstar
-> check_dynamic begin fun () ->
5356 let env, ty1 = enforce_sub_ty env ty1 (Reason.Rarith
p1, Tprim Tnum
) in
5357 let env, ty2 = enforce_sub_ty env ty2 (Reason.Rarith
p2, Tprim Tnum
) in
5358 (* If either side is a float then float *)
5360 * function(float, num) : float
5361 * function(num, float) : float
5362 * [Actually, for division result can be false if second arg is zero]
5364 match is_sub_prim env ty1 Tfloat
, is_sub_prim env ty2 Tfloat
with
5365 | (Some
r, _) | (_, Some
r) ->
5366 make_result env te1 te2
(r, Tprim Tfloat
)
5367 (* Otherwise it has type
5368 * function(num, num) : num
5369 * [Actually, for division result can be false if second arg is zero]
5372 let r = match bop
with
5373 | Ast.Slash
-> Reason.Rret_div
p
5374 | _ -> Reason.Rarith_ret
p in
5375 make_result env te1 te2
(r, Tprim Tnum
)
5377 | Ast.Percent
-> check_dynamic begin fun () ->
5378 (* Integer remainder function has type
5379 * function(int, int) : int
5380 * [Actually, result can be false if second arg is zero]
5382 let env, _ = enforce_sub_ty env ty1 (Reason.Rarith
p1, Tprim Tint
) in
5383 let env, _ = enforce_sub_ty env ty2 (Reason.Rarith
p1, Tprim Tint
) in
5384 make_result env te1 te2
(Reason.Rarith_ret
p, Tprim Tint
)
5387 if TUtils.is_dynamic
env ty1 && TUtils.is_dynamic
env ty2 then
5388 make_result env te1 te2
(Reason.Rbitwise
p, Tdynamic
) else
5389 begin match is_sub_prim env ty1 Tbool
, is_sub_prim env ty2 Tbool
with
5393 * function(bool, bool) : bool
5395 let env, _ = if TUtils.is_dynamic
env ty1 then env, ty1 else
5396 enforce_sub_ty env ty1 (Reason.Rlogic_ret
p1, Tprim Tbool
) in
5397 let env, _ = if TUtils.is_dynamic
env ty2 then env, ty2 else
5398 enforce_sub_ty env ty2 (Reason.Rlogic_ret
p1, Tprim Tbool
) in
5399 make_result env te1 te2
(Reason.Rlogic_ret
p, Tprim Tbool
)
5402 * function(int, int) : int
5404 let env, _ = if TUtils.is_dynamic
env ty1 then env, ty1 else
5405 enforce_sub_ty env ty1 (Reason.Rarith
p1, Tprim Tint
) in
5406 let env, _ = if TUtils.is_dynamic
env ty2 then env, ty2 else
5407 enforce_sub_ty env ty2 (Reason.Rarith
p1, Tprim Tint
) in
5408 make_result env te1 te2
(Reason.Rarith_ret
p, Tprim Tint
)
5410 (* Equality and disequality:
5411 * function<T>(T, T): bool
5413 | Ast.Eqeq
| Ast.Diff
->
5414 make_result env te1 te2
(Reason.Rcomp
p, Tprim Tbool
)
5415 | Ast.EQeqeq
| Ast.Diff2
->
5417 then Typing_equality_check.assert_nontrivial
p bop
env ty1 ty2;
5418 make_result env te1 te2
(Reason.Rcomp
p, Tprim Tbool
)
5419 | Ast.Lt
| Ast.Lte
| Ast.Gt
| Ast.Gte
| Ast.Cmp
->
5420 let ty_result = match bop
with Ast.Cmp
-> Tprim Tint
| _ -> Tprim Tbool
in
5421 let ty_num = (Reason.Rcomp
p, Tprim
Nast.Tnum
) in
5422 let ty_string = (Reason.Rcomp
p, Tprim
Nast.Tstring
) in
5424 (Reason.Rcomp
p, Tclass
((p, SN.Classes.cDateTime
), [])) in
5426 SubType.is_sub_type
env ty1 ty && SubType.is_sub_type
env ty2 ty in
5427 (* So we have three different types here:
5428 * function(num, num): bool
5429 * function(string, string): bool
5430 * function(DateTime, DateTime): bool
5432 if both_sub ty_num || both_sub ty_string || both_sub ty_datetime
5433 then make_result env te1 te2
(Reason.Rcomp
p, ty_result)
5435 (* TODO this is questionable; PHP's semantics for conversions with "<"
5436 * are pretty crazy and we may want to just disallow this? *)
5437 (* This is universal:
5438 * function<T>(T, T): bool
5441 if TUtils.is_dynamic
env ty1 || TUtils.is_dynamic
env ty2
5442 then env, ty1 else Type.unify
p Reason.URnone
env ty1 ty2 in
5443 make_result env te1 te2
(Reason.Rcomp
p, ty_result)
5445 (* A bit weird, this one:
5446 * function(Stringish | string, Stringish | string) : string)
5448 let env = SubType.sub_string
p1 env ty1 in
5449 let env = SubType.sub_string
p2 env ty2 in
5450 make_result env te1 te2
(Reason.Rconcat_ret
p, Tprim Tstring
)
5454 make_result env te1 te2
(Reason.Rlogic_ret
p, Tprim Tbool
)
5455 | Ast.Amp
| Ast.Bar
| Ast.Ltlt
| Ast.Gtgt
->
5456 (* If both are dynamic, we can only return dynamic *)
5457 if TUtils.is_dynamic
env ty1 && TUtils.is_dynamic
env ty2 then
5458 make_result env te1 te2
(Reason.Rbitwise_ret
p, Tdynamic
) else
5459 (* Otherwise at least one of these is an int, so the result is an int *)
5460 let env, _ = if TUtils.is_dynamic
env ty1
5462 else enforce_sub_ty env ty1 (Reason.Rbitwise
p1, Tprim Tint
) in
5463 let env, _ = if TUtils.is_dynamic
env ty2
5465 enforce_sub_ty env ty2 (Reason.Rbitwise
p2, Tprim Tint
) in
5466 make_result env te1 te2
(Reason.Rbitwise_ret
p, Tprim Tint
)
5470 and condition_var_non_null
env = function
5472 | _, Dollardollar
x ->
5473 let x_ty = Env.get_local
env (snd
x) in
5474 let env, x_ty = TUtils.non_null
env x_ty in
5475 set_local
env x x_ty
5476 | p, Class_get
(((), cname
), (_, member_name
)) as e ->
5477 let env, _te
, ty = expr
env e in
5478 let env, local = Env.FakeMembers.make_static
p env cname member_name
in
5479 let lvar = (p, local) in
5480 let env = set_local
env lvar ty in
5481 let local = p, Lvar
lvar in
5482 condition_var_non_null
env local
5483 (* TODO TAST: generate an assignment to the fake local in the TAST *)
5484 | p, Obj_get
((_, This
| _, Lvar
_ as obj
),
5485 (_, Id
(_, member_name
)),
5487 let env, _te
, ty = expr
env e in
5488 let env, local = Env.FakeMembers.make
p env obj member_name
in
5489 let lvar = (p, local) in
5490 let env = set_local
env lvar ty in
5491 let local = p, Lvar
lvar in
5492 condition_var_non_null
env local
5495 and condition_isset
env = function
5496 | _, Array_get
(x, _) -> condition_isset
env x
5497 | v
-> condition_var_non_null
env v
5500 * Build an environment for the true or false branch of
5501 * conditional statements.
5503 and condition ?lhs_of_null_coalesce
env tparamet
=
5505 let env, _te
, ty = raw_expr ?lhs_of_null_coalesce ~in_cond
:true env x in
5506 Async.enforce_nullable_or_not_awaitable
env (fst
x) ty;
5507 check_valid_rvalue
(fst
x) env ty
5508 in let condition = condition ?lhs_of_null_coalesce
5510 | _, Expr_list
[] -> env
5511 | _, Expr_list
[x] ->
5512 let env, _ = expr env x in
5513 condition env tparamet
x
5514 | r, Expr_list
(x::xs
) ->
5515 let env, _ = expr env x in
5516 condition env tparamet
(r, Expr_list xs
)
5517 | _, Call
(Cnormal
, (_, Id
(_, func
)), _, [param], [])
5518 when SN.PseudoFunctions.isset
= func
&& tparamet
&&
5519 not
(Env.is_strict
env) ->
5520 condition_isset
env param
5521 | _, Call
(Cnormal
, (_, Id
(_, func
)), _, [e], [])
5522 when not tparamet
&& SN.StdlibFunctions.is_null
= func
->
5523 condition_var_non_null
env e
5524 | r, Binop
((Ast.Eqeq
| Ast.EQeqeq
as bop
),
5526 | r, Binop
((Ast.Eqeq
| Ast.EQeqeq
as bop
),
5527 e, (_, Null
)) when not tparamet
->
5528 let env, x_ty = expr env e in
5530 if bop
== Ast.Eqeq
then check_null_wtf
env r x_ty else env in
5531 condition_var_non_null
env e
5532 | (p, (Lvar
_ | Obj_get
_ | Class_get
_) as e) ->
5533 let env, ty = expr env e in
5534 let env, ety
= Env.expand_type
env ty in
5536 | _, Tarraykind
(AKany
| AKempty
)
5537 | _, Tprim Tbool
-> env
5538 | _, (Terr
| Tany
| Tmixed
| Tnonnull
| Tarraykind
_ | Toption
_ | Tdynamic
5539 | Tprim
_ | Tvar
_ | Tfun
_ | Tabstract
(_, _) | Tclass
(_, _)
5540 | Ttuple
_ | Tanon
(_, _) | Tunresolved
_ | Tobject
| Tshape
_
5542 condition env (not tparamet
) (p, Binop
(Ast.Eqeq
, e, (p, Null
))))
5543 | r, Binop
(Ast.Eq None
, var
, e) when tparamet
->
5544 let env, e_ty
= expr env e in
5545 let env = check_null_wtf
env r e_ty
in
5546 condition_var_non_null
env var
5547 | p1, Binop
(Ast.Eq None
, (_, (Lvar
_ | Obj_get
_) as lv
), (p2, _)) ->
5548 let env, _ = expr env (p1, Binop
(Ast.Eq None
, lv
, (p2, Null
))) in
5549 condition env tparamet lv
5550 | p, Binop
((Ast.Diff
| Ast.Diff2
as op
), e1
, e2
) ->
5551 let op = if op = Ast.Diff
then Ast.Eqeq
else Ast.EQeqeq
in
5552 condition env (not tparamet
) (p, Binop
(op, e1
, e2
))
5553 | _, Id
(_, s
) when s
= SN.HH.rx_is_enabled
->
5554 (* when Rx\IS_ENABLED is false - switch env to non-reactive *)
5556 then Env.set_env_reactive
env Nonreactive
5558 | _, Binop
(Ast.AMpamp
, e1
, e2
) when tparamet
->
5559 let env = condition env true e1
in
5560 let env = condition env true e2
in
5562 | _, Binop
(Ast.BArbar
, e1
, e2
) when not tparamet
->
5563 let env = condition env false e1
in
5564 let env = condition env false e2
in
5566 | _, Call
(Cnormal
, (p, Id
(_, f)), _, [lv
], [])
5567 when tparamet
&& f = SN.StdlibFunctions.is_array
->
5568 is_array
env `PHPArray
p f lv
5569 | _, Call
(Cnormal
, (p, Id
(_, f)), _, [lv
], [])
5570 when tparamet
&& f = SN.StdlibFunctions.is_vec ->
5571 is_array
env `HackVec
p f lv
5572 | _, Call
(Cnormal
, (p, Id
(_, f)), _, [lv
], [])
5573 when tparamet
&& f = SN.StdlibFunctions.is_dict
->
5574 is_array
env `HackDict
p f lv
5575 | _, Call
(Cnormal
, (p, Id
(_, f)), _, [lv
], [])
5576 when tparamet
&& f = SN.StdlibFunctions.is_keyset
->
5577 is_array
env `HackKeyset
p f lv
5578 | _, Call
(Cnormal
, (p, Id
(_, f)), _, [lv
], [])
5579 when tparamet
&& f = SN.StdlibFunctions.is_int
->
5580 is_type
env lv Tint
(Reason.Rpredicated
(p, f))
5581 | _, Call
(Cnormal
, (p, Id
(_, f)), _, [lv
], [])
5582 when tparamet
&& f = SN.StdlibFunctions.is_bool
->
5583 is_type
env lv Tbool
(Reason.Rpredicated
(p, f))
5584 | _, Call
(Cnormal
, (p, Id
(_, f)), _, [lv
], [])
5585 when tparamet
&& f = SN.StdlibFunctions.is_float
->
5586 is_type
env lv Tfloat
(Reason.Rpredicated
(p, f))
5587 | _, Call
(Cnormal
, (p, Id
(_, f)), _, [lv
], [])
5588 when tparamet
&& f = SN.StdlibFunctions.is_string
->
5589 is_type
env lv Tstring
(Reason.Rpredicated
(p, f))
5590 | _, Call
(Cnormal
, (p, Id
(_, f)), _, [lv
], [])
5591 when tparamet
&& f = SN.StdlibFunctions.is_resource
->
5592 is_type
env lv Tresource
(Reason.Rpredicated
(p, f))
5593 | _, Unop
(Ast.Unot
, e) ->
5594 condition env (not tparamet
) e
5595 | p, InstanceOf
(ivar
, ((), cid)) when tparamet
&& is_instance_var ivar
->
5596 (* Check the expession and determine its static type *)
5597 let env, _te
, x_ty = raw_expr ~in_cond
:false env ivar
in
5599 (* What is the local variable bound to the expression? *)
5600 let env, ((ivar_pos
, _) as ivar
) = get_instance_var
env ivar
in
5602 (* The position p here is not really correct... it's the position
5603 * of the instanceof expression, not the class id. But we don't store
5604 * position data for the latter. *)
5605 let env, _te
, obj_ty = static_class_id
p env cid in
5607 if SubType.is_sub_type
env obj_ty (
5608 Reason.none
, Tclass
((Pos.none
, SN.Classes.cAwaitable
), [Reason.none
, Typing_utils.tany env])
5609 ) then () else Async.enforce_nullable_or_not_awaitable
env (fst ivar
) x_ty;
5611 let safe_instanceof_enabled =
5612 TypecheckerOptions.experimental_feature_enabled
5613 (Env.get_options
env) TypecheckerOptions.experimental_instanceof
in
5614 let rec resolve_obj env obj_ty =
5615 (* Expand so that we don't modify x *)
5616 let env, obj_ty = Env.expand_type
env obj_ty in
5618 (* If it's a generic that's expression dependent, we need to
5619 look at all of its upper bounds and create an unresolved type to
5620 represent all of the possible types.
5622 | r, Tabstract
(AKgeneric s
, _) when AbstractKind.is_generic_dep_ty s
->
5623 let upper_bounds = TySet.elements
(Env.get_upper_bounds
env s
) in
5624 let env, tyl = List.map_env
env upper_bounds resolve_obj in
5625 env, (r, Tunresolved
tyl)
5626 | _, Tabstract
(AKgeneric
name, _) ->
5627 if safe_instanceof_enabled
5628 then Errors.instanceof_generic_classname
p name;
5630 | _, Tabstract
(AKdependent
(`this
, []), Some
(_, Tclass
_)) ->
5632 (* Technically instanceof static is not strong enough to prove
5633 * that a type is exactly the same as the late bound type.
5634 * For now we allow this lie to exist. To solve
5635 * this we either need to create a new type that means
5636 * subtype of static or provide a way of specifying exactly
5637 * the late bound type i.e. $x::class === static::class
5639 if cid = CIstatic
then
5640 ExprDepTy.make
env CIstatic
obj_ty
5644 | _, Tabstract
((AKdependent
_ | AKnewtype
_), Some
ty) ->
5646 | _, Tclass
((_, cid as _c
), tyl) ->
5647 begin match Env.get_class
env cid with
5648 (* Why would this happen? *)
5650 env, (Reason.Rwitness ivar_pos
, Tobject
)
5652 | Some
class_info ->
5653 if SubType.is_sub_type
env x_ty obj_ty
5655 (* If the right side of the `instanceof` object is
5656 * a super type of what we already knew. In this case,
5657 * since we already have a more specialized object, we
5658 * don't touch the original object. Check out the unit
5659 * test srecko.php if this is unclear.
5661 * Note that if x_ty is Typing_utils.tany env, no amount of subtype
5662 * checking will be able to specify it
5663 * further. This is arguably desirable to maintain
5664 * the invariant that removing annotations gets rid
5665 * of typing errors in partial mode (See also
5669 (* We only implement the safe instanceof in strict mode *)
5670 (* Also: for generic types we implememt it only with
5671 * experimental feature enabled *)
5672 if Env.is_strict
env && (tyl = [] || safe_instanceof_enabled)
5673 then safe_instanceof
env p ivar
x_ty obj_ty _c
class_info
5676 | r, Tunresolved
tyl ->
5677 let env, tyl = List.map_env
env tyl resolve_obj in
5678 env, (r, Tunresolved
tyl)
5679 | _, (Terr
| Tany
| Tmixed
| Tnonnull
| Tarraykind
_ | Tprim
_ | Tvar
_
5680 | Tfun
_ | Tabstract
((AKenum
_ | AKnewtype
_ | AKdependent
_), _)
5681 | Ttuple
_ | Tanon
(_, _) | Toption
_ | Tobject
| Tshape
_
5683 env, (Reason.Rwitness ivar_pos
, Tobject
)
5685 let env, x_ty = resolve_obj env obj_ty in
5686 set_local
env ivar
x_ty
5687 | _p
, Is
(ivar
, h) ->
5688 (* What is the local variable bound to the expression? *)
5689 let env, ivar
= get_instance_var
env ivar
in
5690 (* Resolve the typehint to a type *)
5691 let env, hint_ty = Phase.hint_locl
env h in
5692 begin match (IsAsExprHint.validate
hint_ty) with
5693 | IsAsExprHint.Valid
5694 | IsAsExprHint.Partial
_ ->
5695 set_local
env ivar
hint_ty
5696 | IsAsExprHint.Invalid
_ -> env
5698 | _, Binop
((Ast.Eqeq
| Ast.EQeqeq
), e, (_, Null
))
5699 | _, Binop
((Ast.Eqeq
| Ast.EQeqeq
), (_, Null
), e) ->
5700 let env, _ = expr env e in
5703 let env, _ = expr env e in
5706 and safe_instanceof
env p ivar ivar_ty
obj_ty _c
class_info =
5707 let ivar_pos = fst ivar
in
5708 (* Generate fresh names consisting of formal type parameter name
5709 * with unique suffix *)
5710 let env, tparams_with_new_names
=
5711 List.map_env
env class_info.tc_tparams
5712 (fun env ((_, (_,name), _) as tp
) ->
5713 let env, name = Env.add_fresh_generic_parameter
env name in
5717 String.concat
"," (List.map tparams_with_new_names ~
f:snd
)
5719 let reason = Reason.Rinstanceof
(ivar_pos, s) in
5720 let tyl_fresh = List.map
5721 ~
f:(fun (_, newname
) -> (reason, Tabstract
(AKgeneric newname
, None
)))
5722 tparams_with_new_names
in
5724 (* Type of variable in block will be class name
5725 * with fresh type parameters *)
5726 let obj_ty = (fst
obj_ty, Tclass
(_c
, tyl_fresh)) in
5728 (* Add in constraints as assumptions on those type parameters *)
5730 type_expansions
= [];
5731 substs
= Subst.make
class_info.tc_tparams
tyl_fresh;
5732 this_ty = obj_ty; (* In case `this` appears in constraints *)
5735 let add_bounds env ((_, _, cstr_list
), ty_fresh
) =
5736 List.fold_left cstr_list ~init
:env ~
f:begin fun env (ck
, ty) ->
5737 (* Substitute fresh type parameters for
5738 * original formals in constraint *)
5739 let env, ty = Phase.localize ~
ety_env env ty in
5740 SubType.add_constraint
p env ck ty_fresh
ty end in
5742 List.fold_left
(List.zip_exn
class_info.tc_tparams
tyl_fresh)
5743 ~
f:add_bounds ~init
:env in
5745 (* Finally, if we have a class-test on something with static class type,
5746 * then we can chase the hierarchy and decompose the types to deduce
5747 * further assumptions on type parameters. For example, we might have
5748 * class B<Tb> { ... }
5749 * class C extends B<int>
5750 * and have obj_ty = C and x_ty = B<T> for a generic parameter T.
5751 * Then SubType.add_constraint will deduce that T=int and add int as
5752 * both lower and upper bound on T in env.lenv.tpenv
5754 let env = SubType.add_constraint
p env Ast.Constraint_as
obj_ty ivar_ty
in
5756 (* It's often the case that the fresh name isn't necessary. For
5757 * example, if C<T> extends B<T>, and we have $x:B<t> for some type t
5758 * then $x instanceof B should refine to $x:C<t>.
5759 * We take a simple approach:
5760 * For a fresh type parameter T#1, if
5761 * 1) There is an eqality constraint T#1 = t then replace T#1 with t.
5762 * 2) T#1 is covariant, and T#1 <: t and occurs nowhere else in the constraints
5763 * 3) T#1 is contravariant, and t <: T#1 and occurs nowhere else in the constraints
5765 let tparams_in_constraints = Env.get_tpenv_tparams
env in
5766 let tyl_fresh_simplified =
5767 List.map tparams_with_new_names
5768 ~
f:begin fun ((variance
, _, _), newname
) ->
5770 TySet.elements
(Env.get_lower_bounds
env newname
),
5771 TySet.elements
(Env.get_equal_bounds
env newname
),
5772 TySet.elements
(Env.get_upper_bounds
env newname
) with
5773 | _, _, [ty], _ -> ty
5774 | Ast.Covariant
, _, _, [ty]
5775 | Ast.Contravariant
, [ty], _, _
5776 when not
(SSet.mem newname
tparams_in_constraints) -> ty
5777 | _, _, _, _ -> (reason, Tabstract
(AKgeneric newname
, None
)) end in
5778 let obj_ty_simplified = (fst
obj_ty, Tclass
(_c
, tyl_fresh_simplified)) in
5779 env, obj_ty_simplified
5781 and is_instance_var
= function
5782 | _, (Lvar
_ | This
) -> true
5783 | _, Obj_get
((_, This
), (_, Id
_), _) -> true
5784 | _, Obj_get
((_, Lvar
_), (_, Id
_), _) -> true
5785 | _, Class_get
(_, _) -> true
5788 and get_instance_var
env = function
5789 | p, Class_get
(((), cname
), (_, member_name
)) ->
5790 let env, local = Env.FakeMembers.make_static
p env cname member_name
in
5792 | p, Obj_get
((_, This
| _, Lvar
_ as obj
), (_, Id
(_, member_name
)), _) ->
5793 let env, local = Env.FakeMembers.make
p env obj member_name
in
5795 | _, Lvar
(p, x) -> env, (p, x)
5796 | p, This
-> env, (p, this
)
5797 | _ -> failwith
"Should only be called when is_instance_var is true"
5799 and check_null_wtf
env p ty =
5800 if not
(Env.is_strict
env) then env else
5801 let env, ty = TUtils.fold_unresolved
env ty in
5802 let env, ety
= Env.expand_type
env ty in
5805 (* Find sketchy nulls hidden under singleton Tunresolved *)
5806 let env, ty = TUtils.fold_unresolved
env ty in
5807 let env, ety
= Env.expand_type
env ty in
5809 | _, (Tmixed
| Tnonnull
) ->
5810 Errors.sketchy_null_check
p
5812 Errors.sketchy_null_check_primitive
p
5813 | _, (Terr
| Tany
| Tarraykind
_ | Toption
_ | Tvar
_ | Tfun
_
5814 | Tabstract
(_, _) | Tclass
(_, _) | Ttuple
_ | Tanon
(_, _)
5815 | Tunresolved
_ | Tobject
| Tshape
_ | Tdynamic
) -> ());
5817 | _, (Terr
| Tany
| Tmixed
| Tnonnull
| Tarraykind
_ | Tprim
_ | Tvar
_ | Tdynamic
5818 | Tfun
_ | Tabstract
(_, _) | Tclass
(_, _) | Ttuple
_ | Tanon
(_, _)
5819 | Tunresolved
_ | Tobject
| Tshape
_ ) -> env
5821 and is_type
env e tprim
r =
5823 | p, Class_get
(((), cname
), (_, member_name
)) ->
5824 let env, local = Env.FakeMembers.make_static
p env cname member_name
in
5825 set_local
env (p, local) (r, Tprim tprim
)
5826 | p, Obj_get
((_, This
| _, Lvar
_ as obj
), (_, Id
(_, member_name
)), _) ->
5827 let env, local = Env.FakeMembers.make
p env obj member_name
in
5828 set_local
env (p, local) (r, Tprim tprim
)
5830 set_local
env lvar (r, Tprim tprim
)
5833 (* Refine type for is_array, is_vec, is_keyset and is_dict tests
5834 * `pred_name` is the function name itself (e.g. 'is_vec')
5835 * `p` is position of the function name in the source
5836 * `arg_expr` is the argument to the function
5838 and is_array
env ty p pred_name arg_expr
=
5839 let env, _te
, arg_ty
= expr env arg_expr
in
5840 let r = Reason.Rpredicated
(p, pred_name
) in
5841 let env, tarrkey_name
= Env.add_fresh_generic_parameter
env "Tk" in
5842 let tarrkey = (r, Tabstract
(AKgeneric tarrkey_name
, None
)) in
5843 let env = SubType.add_constraint
p env Ast.Constraint_as
5844 tarrkey (r, Tprim Tarraykey
) in
5845 let env, tfresh_name
= Env.add_fresh_generic_parameter
env "T" in
5846 let tfresh = (r, Tabstract
(AKgeneric tfresh_name
, None
)) in
5847 (* This is the refined type of e inside the branch *)
5851 Tclass
((Pos.none
, SN.Collections.cDict
), [tarrkey; tfresh])
5853 Tclass
((Pos.none
, SN.Collections.cVec
), [tfresh])
5855 Tclass
((Pos.none
, SN.Collections.cKeyset
), [tarrkey])
5857 let safe_isarray_enabled =
5858 TypecheckerOptions.experimental_feature_enabled
5859 (Env.get_options
env) TypecheckerOptions.experimental_isarray
in
5860 if safe_isarray_enabled
5861 then Tarraykind
(AKvarray_or_darray
tfresh)
5862 else Tarraykind AKany
)) in
5863 (* Add constraints on generic parameters that must
5864 * hold for refined_ty <:arg_ty. For example, if arg_ty is Traversable<T>
5865 * and refined_ty is keyset<T#1> then we know T#1 <: T *)
5866 let env = SubType.add_constraint
p env Ast.Constraint_as
refined_ty arg_ty
in
5868 | (_, Class_get
(((), cname
), (_, member_name
))) ->
5869 let env, local = Env.FakeMembers.make_static
p env cname member_name
in
5870 set_local
env (p, local) refined_ty
5871 | (_, Obj_get
((_, This
| _, Lvar
_ as obj
), (_, Id
(_, member_name
)), _)) ->
5872 let env, local = Env.FakeMembers.make
p env obj member_name
in
5873 set_local
env (p, local) refined_ty
5875 set_local
env lvar refined_ty
5878 and string2
env idl
=
5880 List.fold_left idl ~init
:(env,[]) ~
f:begin fun (env,tel) x ->
5881 let env, te, ty = expr env x in
5883 let env = SubType.sub_string
p env ty in
5888 (* If the current class inherits from classes that take type arguments, we need
5889 * to check that the arguments provided are consistent with the constraints on
5890 * the type parameters. *)
5891 and check_implements_tparaml
(env: Env.env) ht
=
5892 let _r, (p, c), paraml = TUtils.unwrap_class_type ht
in
5893 let class_ = Decl_env.get_class_dep
env.Env.decl_env
c in
5896 (* The class lives in PHP land *)
5899 let size1 = List.length
class_.dc_tparams
in
5900 let size2 = List.length
paraml in
5901 if size1 <> size2 then Errors.class_arity
p class_.dc_pos
c size1;
5902 let subst = Inst.make_subst
class_.dc_tparams
paraml in
5903 iter2_shortest
begin fun (_, (p, _), cstrl
) ty ->
5904 List.iter cstrl
begin fun (ck
, cstr) ->
5905 (* Constraint might contain uses of generic type parameters *)
5906 let cstr = Inst.instantiate
subst cstr in
5908 | Ast.Constraint_as
->
5909 Type.sub_type_decl
p Reason.URnone
env ty cstr
5910 | Ast.Constraint_eq
->
5911 (* This code could well be unreachable, because we don't allow
5912 * equality constraints on class generics. *)
5913 Type.sub_type_decl
p Reason.URnone
env ty cstr;
5914 Type.sub_type_decl
p Reason.URnone
env cstr ty
5915 | Ast.Constraint_super
->
5916 Type.sub_type_decl
p Reason.URnone
env cstr ty
5918 end class_.dc_tparams
paraml
5920 (* In order to type-check a class, we need to know what "parent"
5921 * refers to. Sometimes people write "parent::", when that happens,
5922 * we need to know the type of parent.
5924 and class_def_parent
env class_def class_type
=
5925 match class_def
.c_extends
with
5926 | (_, Happly
((_, x), _) as parent_ty
) :: _ ->
5927 let parent_type = Decl_env.get_class_dep
env.Env.decl_env
x in
5928 (match parent_type with
5929 | Some
parent_type -> check_parent class_def class_type
parent_type
5931 let parent_ty = Decl_hint.hint
env.Env.decl_env
parent_ty in
5932 env, Some
x, parent_ty
5933 (* The only case where we have more than one parent class is when
5934 * dealing with interfaces and interfaces cannot use parent.
5937 | _ -> env, None
, (Reason.Rnone
, Typing_utils.tany env)
5939 and check_parent class_def class_type
parent_type =
5940 let position = fst class_def
.c_name
in
5941 if class_type
.tc_const
&& not
parent_type.dc_const
5942 then Errors.self_const_parent_not
position;
5943 if parent_type.dc_const
&& not class_type
.tc_const
5944 then Errors.parent_const_self_not
position;
5945 (* Are all the parents in Hack? Do we know all their methods?
5946 * If so, let's check that the abstract methods have been implemented.
5948 if class_type
.tc_members_fully_known
5949 then check_parent_abstract
position parent_type class_type
;
5950 if parent_type.dc_final
5951 then Errors.extend_final
position parent_type.dc_pos
parent_type.dc_name
5954 and check_parent_abstract
position parent_type class_type
=
5955 let is_final = class_type
.tc_final
in
5956 if parent_type.dc_kind
= Ast.Cabstract
&&
5957 (class_type
.tc_kind
<> Ast.Cabstract
|| is_final)
5959 check_extend_abstract_meth ~
is_final position class_type
.tc_methods
;
5960 check_extend_abstract_meth ~
is_final position class_type
.tc_smethods
;
5961 check_extend_abstract_const ~
is_final position class_type
.tc_consts
;
5962 check_extend_abstract_typeconst
5963 ~
is_final position class_type
.tc_typeconsts
;
5966 and class_def tcopt
c =
5967 let env = EnvFromDef.class_env tcopt
c in
5968 let tc = Env.get_class
env (snd
c.c_name
) in
5969 add_decl_errors
(Option.(map
tc (fun tc -> value_exn
tc.tc_decl_errors
)));
5970 let c = TNBody.class_meth_bodies tcopt
c in
5971 if not
!auto_complete
then begin
5972 NastCheck.class_ env c;
5973 NastInitCheck.class_ env c;
5977 (* This can happen if there was an error during the declaration
5981 Typing_requirements.check_class
env tc;
5982 Some
(class_def_
env c tc)
5984 and class_def_
env c tc =
5985 Typing_hooks.dispatch_enter_class_def_hook
c tc;
5986 let env = Env.set_mode
env c.c_mode
in
5987 let pc, _ = c.c_name
in
5989 (c.c_extends
@ c.c_implements
@ c.c_uses
)
5990 (Decl_hint.hint
env.Env.decl_env
) in
5991 TI.check_tparams_instantiable
env (fst
c.c_tparams
);
5992 let env, constraints
=
5993 Phase.localize_generic_parameters_with_bounds
env (fst
c.c_tparams
)
5994 ~
ety_env:(Phase.env_with_self
env) in
5995 let env = add_constraints
(fst
c.c_name
) env constraints
in
5996 Typing_variance.class_ (Env.get_options
env) (snd
c.c_name
) tc impl;
5997 List.iter impl (check_implements_tparaml
env);
5999 let env, parent_id
, parent = class_def_parent
env c tc in
6000 let is_final = tc.tc_final
in
6001 if (tc.tc_kind
= Ast.Cnormal
|| is_final) && tc.tc_members_fully_known
6003 check_extend_abstract_meth ~
is_final pc tc.tc_methods
;
6004 check_extend_abstract_meth ~
is_final pc tc.tc_smethods
;
6005 check_extend_abstract_const ~
is_final pc tc.tc_consts
;
6006 check_extend_abstract_typeconst ~
is_final pc tc.tc_typeconsts
;
6008 let env = Env.set_parent
env parent in
6009 let env = match parent_id
with
6011 | Some parent_id
-> Env.set_parent_id
env parent_id
in
6012 if tc.tc_final
then begin
6014 | Ast.Cinterface
-> Errors.interface_final
(fst
c.c_name
)
6015 | Ast.Cabstract
-> ()
6016 | Ast.Ctrait
-> Errors.trait_final
(fst
c.c_name
)
6018 Errors.internal_error
pc "The parser should not parse final on enums"
6021 SMap.iter (check_static_method
tc.tc_methods
) tc.tc_smethods
;
6022 List.iter impl (class_implements_type
env c);
6023 if tc.tc_is_disposable
6024 then List.iter (c.c_extends
@ c.c_uses
) (Typing_disposable.enforce_is_disposable
env);
6025 let typed_vars = List.map
c.c_vars
(class_var_def
env ~is_static
:false c) in
6026 let typed_methods = List.map
c.c_methods
(method_def
env) in
6027 let typed_typeconsts = List.map
c.c_typeconsts
(typeconst_def
env) in
6028 let typed_consts, const_types
=
6029 List.unzip
(List.map
c.c_consts
(class_const_def
env)) in
6030 let env = Typing_enum.enum_class_check
env tc c.c_consts const_types
in
6031 let typed_constructor = class_constr_def
env c in
6032 let env = Env.set_static
env in
6033 let typed_static_vars =
6034 List.map
c.c_static_vars
(class_var_def
env ~is_static
:true c) in
6035 let typed_static_methods = List.map
c.c_static_methods
(method_def
env) in
6036 Typing_hooks.dispatch_exit_class_def_hook
c tc;
6038 T.c_annotation
= save_env env;
6039 T.c_mode
= c.c_mode
;
6040 T.c_final
= c.c_final
;
6041 T.c_is_xhp
= c.c_is_xhp
;
6042 T.c_kind
= c.c_kind
;
6043 T.c_name
= c.c_name
;
6044 T.c_tparams
= c.c_tparams
;
6045 T.c_extends
= c.c_extends
;
6046 T.c_uses
= c.c_uses
;
6047 T.c_xhp_attr_uses
= c.c_xhp_attr_uses
;
6048 T.c_xhp_category
= c.c_xhp_category
;
6049 T.c_req_extends
= c.c_req_extends
;
6050 T.c_req_implements
= c.c_req_implements
;
6051 T.c_implements
= c.c_implements
;
6052 T.c_consts
= typed_consts;
6053 T.c_typeconsts
= typed_typeconsts;
6054 T.c_static_vars
= typed_static_vars;
6055 T.c_vars
= typed_vars;
6056 T.c_constructor
= typed_constructor;
6057 T.c_static_methods
= typed_static_methods;
6058 T.c_methods
= typed_methods;
6059 T.c_user_attributes
= List.map
c.c_user_attributes
(user_attribute
env);
6060 T.c_enum
= c.c_enum
;
6063 and check_static_method obj method_name static_method
=
6064 if SMap.mem method_name obj
6066 let lazy (static_method_reason
, _) = static_method
.ce_type
in
6067 let dyn_method = SMap.find_unsafe method_name obj
in
6068 let lazy (dyn_method_reason
, _) = dyn_method.ce_type
in
6069 Errors.static_dynamic
6070 (Reason.to_pos static_method_reason
)
6071 (Reason.to_pos dyn_method_reason
)
6076 and check_extend_abstract_meth ~
is_final p smap
=
6077 SMap.iter begin fun x ce
->
6078 match ce
.ce_type
with
6079 | lazy (r, Tfun
{ ft_abstract
= true; _ }) ->
6080 Errors.implement_abstract ~
is_final p (Reason.to_pos
r) "method" x
6084 (* Type constants must be bound to a concrete type for non-abstract classes.
6086 and check_extend_abstract_typeconst ~
is_final p smap
=
6087 SMap.iter begin fun x tc ->
6088 if tc.ttc_type
= None
then
6089 Errors.implement_abstract ~
is_final p (fst
tc.ttc_name
) "type constant" x
6092 and check_extend_abstract_const ~
is_final p smap
=
6093 SMap.iter begin fun x cc
->
6094 match cc
.cc_type
with
6095 | r, _ when cc
.cc_abstract
&& not cc
.cc_synthesized
->
6096 Errors.implement_abstract ~
is_final p (Reason.to_pos
r) "constant" x
6107 | Tvarray_or_darray
_
6120 and typeconst_def
env {
6121 c_tconst_name
= (pos, _) as id;
6122 c_tconst_constraint
;
6125 let env, cstr = opt
Phase.hint_locl
env c_tconst_constraint
in
6126 let env, ty = opt
Phase.hint_locl
env c_tconst_type
in
6128 Option.map2
ty cstr ~
f:(Type.sub_type
pos Reason.URtypeconst_cstr
env)
6131 T.c_tconst_name
= id;
6132 T.c_tconst_constraint
= c_tconst_constraint
;
6133 T.c_tconst_type
= c_tconst_type
;
6136 and class_const_def
env (h, id, e) =
6137 let env, ty, opt_expected
=
6139 | None
-> env, Env.fresh_type
(), None
6141 let env, ty = Phase.hint_locl
env h in
6142 env, ty, Some
(fst
id, Reason.URhint
, ty)
6146 let env, te, ty'
= expr ?
expected:opt_expected
env e in
6147 ignore
(Type.sub_type
(fst
id) Reason.URhint
env ty'
ty);
6148 (h, id, Some
te), ty'
6152 and class_constr_def
env c =
6153 let env = { env with Env.inside_constructor
= true } in
6154 Option.map
c.c_constructor
(method_def
env)
6156 and class_implements_type
env c1 ctype2
=
6158 List.map
(fst c1
.c_tparams
) begin fun (_, (p, s), _) ->
6159 (Reason.Rwitness
p, Tgeneric
s)
6161 let r = Reason.Rwitness
(fst c1
.c_name
) in
6162 let ctype1 = r, Tapply
(c1
.c_name
, params) in
6163 Typing_extends.check_implements
env ctype2
ctype1;
6166 (* Type-check a property declaration, with optional initializer *)
6167 and class_var_def
env ~is_static
c cv
=
6168 (* First pick up and localize the hint if it exists *)
6170 match cv
.cv_type
with
6173 | Some
(p, _ as cty
) ->
6175 (* If this is an XHP attribute and we're in strict mode,
6176 relax to partial mode to allow the use of the "array"
6177 annotation without specifying type parameters. Until
6178 recently HHVM did not allow "array" with type parameters
6179 in XHP attribute declarations, so this is a temporary
6180 hack to support existing code for now. *)
6181 (* Task #5815945: Get rid of this Hack *)
6182 if cv
.cv_is_xhp
&& (Env.is_strict
env)
6183 then Env.set_mode
env FileInfo.Mpartial
6185 let cty = TI.instantiable_hint
env cty in
6186 let env, cty = Phase.localize_with_self
env cty in
6187 env, Some
(p, Reason.URhint
, cty) in
6188 (* Next check the expression, passing in expected type if present *)
6189 let env, typed_cv_expr
, ty =
6190 match cv
.cv_expr
with
6191 | None
-> env, None
, Env.fresh_type
()
6193 let env, te, ty = expr ?
expected env e in
6194 (* Check that the inferred type is a subtype of the expected type.
6195 * Eventually this will be the responsibility of `expr`
6200 | Some
(p, ur
, cty) -> Type.sub_type
p ur
env ty cty in
6203 if Option.is_none cv
.cv_type
6205 if Env.is_strict
env
6206 then Errors.add_a_typehint
(fst cv
.cv_id
)
6208 let pos, name = cv
.cv_id
in
6209 let name = if is_static
then "$" ^
name else name in
6210 let var_type = Reason.Rwitness
pos, Typing_utils.tany env in
6211 if Option.is_none cv
.cv_expr
6212 then Typing_suggest.uninitialized_member
(snd
c.c_name
) name env var_type ty
6213 else Typing_suggest.save_member
name env var_type ty
6216 T.cv_final
= cv
.cv_final
;
6217 T.cv_is_xhp
= cv
.cv_is_xhp
;
6218 T.cv_visibility
= cv
.cv_visibility
;
6219 T.cv_type
= cv
.cv_type
;
6221 T.cv_expr
= typed_cv_expr
;
6222 T.cv_user_attributes
= List.map cv
.cv_user_attributes
(user_attribute
env);
6226 and localize_where_constraints
6227 ~
ety_env (env:Env.env) (where_constraints
:Nast.where_constraint list
) =
6228 let add_constraint env (h1
, ck
, h2
) =
6230 Phase.localize
env (Decl_hint.hint
env.Env.decl_env h1
) ~
ety_env in
6232 Phase.localize
env (Decl_hint.hint
env.Env.decl_env h2
) ~
ety_env in
6233 SubType.add_constraint (fst h1
) env ck
ty1 ty2
6235 List.fold_left where_constraints ~
f:add_constraint ~init
:env
6237 and add_constraints
p env constraints
=
6238 let add_constraint env (ty1, ck
, ty2) =
6239 SubType.add_constraint p env ck
ty1 ty2 in
6240 List.fold_left constraints ~
f:add_constraint ~init
: env
6242 and user_attribute
env ua
=
6243 let typed_ua_params =
6244 List.map ua
.ua_params
(fun e -> let _env, te, _ty
= expr env e in te) in
6246 T.ua_name
= ua
.ua_name
;
6247 T.ua_params
= typed_ua_params;
6250 and method_def
env m
=
6251 (* reset the expression dependent display ids for each method body *)
6252 Reason.expr_display_id_map
:= IMap.empty
;
6253 Typing_hooks.dispatch_enter_method_def_hook m
;
6254 let pos = fst m
.m_name
in
6256 Env.env_with_locals
env Typing_continuations.Map.empty
Local_id.Map.empty
6258 let env = Env.set_env_function_pos
env pos in
6259 let reactive = Decl.fun_reactivity
env.Env.decl_env m
.m_user_attributes
in
6260 let mut = TUtils.fun_mutable m
.m_user_attributes
in
6261 let env = Env.set_env_reactive
env reactive in
6262 let env = Env.set_fun_mutable
env mut in
6264 { (Phase.env_with_self
env) with from_class
= Some CIstatic
; } in
6265 let env, constraints
=
6266 Phase.localize_generic_parameters_with_bounds
env m
.m_tparams
6268 TI.check_tparams_instantiable
env m
.m_tparams
;
6269 let env = add_constraints
pos env constraints
in
6271 localize_where_constraints ~
ety_env env m
.m_where_constraints
in
6273 if Env.is_static
env then env
6274 else Env.set_local
env this
(Env.get_self
env) in
6276 match Env.get_class
env (Env.get_self_id
env) with
6279 (* Mark $this as a using variable if it has a disposable type *)
6280 if c.tc_is_disposable
6281 then Env.set_using_var
env this
6283 let env = Env.clear_params
env in
6284 let env, ty = match m
.m_ret
with
6286 env, Typing_return.make_default_return
env m
.m_name
6288 let ret = TI.instantiable_hint
env ret in
6289 (* If a 'this' type appears it needs to be compatible with the
6293 { (Phase.env_with_self
env) with
6294 from_class
= Some CIstatic
} in
6295 Phase.localize ~
ety_env env ret in
6296 let return = Typing_return.make_info m
.m_fun_kind m
.m_user_attributes
env
6297 ~is_explicit
:(Option.is_some m
.m_ret
) ~is_by_ref
:m
.m_ret_by_ref
ty in
6298 TI.check_params_instantiable
env m
.m_params
;
6299 let env, param_tys
= List.map_env
env m
.m_params
make_param_local_ty in
6300 if Env.is_strict
env then begin
6301 List.iter2_exn ~
f:(check_param
env) m
.m_params param_tys
;
6303 Typing_memoize.check_method
env m
;
6304 let env, typed_params
=
6305 List.map_env
env (List.zip_exn param_tys m
.m_params
) bind_param in
6306 let env, t_variadic
= match m
.m_variadic
with
6307 | FVvariadicArg vparam
->
6308 TI.check_param_instantiable
env vparam
;
6309 let env, ty = make_param_local_ty env vparam
in
6310 if Env.is_strict
env then
6311 check_param
env vparam
ty;
6312 let env, t_variadic
= bind_param env (ty, vparam
) in
6313 env, (T.FVvariadicArg t_variadic
)
6314 | FVellipsis
-> env, T.FVellipsis
6315 | FVnonVariadic
-> env, T.FVnonVariadic
in
6316 let nb = Nast.assert_named_body m
.m_body
in
6318 fun_ ~abstract
:m
.m_abstract
env return pos nb m
.m_fun_kind
in
6320 Env.check_todo
env in
6324 snd m
.m_name
= SN.Members.__destruct
6325 || snd m
.m_name
= SN.Members.__construct
->
6326 Some
(pos, Happly
((pos, "void"), []))
6327 | None
when Env.is_strict
env ->
6328 Typing_return.suggest_return
env pos return.Typing_env_return_info.return_type
; None
6329 | None
-> let (pos, id) = m
.m_name
in
6330 let id = (Env.get_self_id
env) ^
"::" ^
id in
6331 Typing_suggest.save_fun_or_method
(pos, id);
6334 Typing_return.async_suggest_return
(m
.m_fun_kind
) hint
(fst m
.m_name
); m
.m_ret in
6335 Env.log_anonymous
env;
6336 let m = { m with m_ret = m_ret; } in
6337 Typing_hooks.dispatch_exit_method_def_hook
m;
6339 T.m_annotation
= save_env env;
6340 T.m_final
= m.m_final
;
6341 T.m_abstract
= m.m_abstract
;
6342 T.m_visibility
= m.m_visibility
;
6343 T.m_name
= m.m_name
;
6344 T.m_tparams
= m.m_tparams
;
6345 T.m_where_constraints
= m.m_where_constraints
;
6346 T.m_variadic
= t_variadic
;
6347 T.m_params
= typed_params
;
6348 T.m_fun_kind
= m.m_fun_kind
;
6349 T.m_user_attributes
= List.map
m.m_user_attributes
(user_attribute
env);
6351 T.m_body
= T.NamedBody
{
6353 T.fnb_unsafe
= nb.fnb_unsafe
;
6355 T.m_ret_by_ref
= m.m_ret_by_ref
;
6358 and typedef_def tcopt typedef
=
6359 let env = EnvFromDef.typedef_env tcopt typedef
in
6360 let tdecl = Env.get_typedef
env (snd typedef
.t_name
) in
6361 add_decl_errors
(Option.(map
tdecl (fun tdecl -> value_exn
tdecl.td_decl_errors
)));
6362 let env = Typing_env.set_mode
env typedef
.t_mode
in
6363 let env, constraints
=
6364 Phase.localize_generic_parameters_with_bounds
env typedef
.t_tparams
6365 ~
ety_env:(Phase.env_with_self
env) in
6366 let env = add_constraints
(fst typedef
.t_name
) env constraints
in
6367 NastCheck.typedef
env typedef
;
6372 t_constraint
= tcstr
;
6374 t_user_attributes
= _;
6378 let ty = TI.instantiable_hint
env hint
in
6379 let env, ty = Phase.localize_with_self
env ty in
6380 let env = begin match tcstr
with
6382 let cstr = TI.instantiable_hint
env tcstr
in
6383 let env, cstr = Phase.localize_with_self
env cstr in
6384 Typing_ops.sub_type t_pos
Reason.URnewtype_cstr
env ty cstr
6387 let env = begin match hint
with
6388 | pos, Hshape
{ nsi_allows_unknown_fields
=_; nsi_field_map
} ->
6389 check_shape_keys_validity
env pos (ShapeMap.keys nsi_field_map
)
6393 T.t_annotation
= save_env env;
6394 T.t_name
= typedef
.t_name
;
6395 T.t_mode
= typedef
.t_mode
;
6396 T.t_vis
= typedef
.t_vis
;
6397 T.t_user_attributes
= List.map typedef
.t_user_attributes
(user_attribute
env);
6398 T.t_constraint
= typedef
.t_constraint
;
6399 T.t_kind
= typedef
.t_kind
;
6400 T.t_tparams
= typedef
.t_tparams
;
6403 and gconst_def tcopt cst
=
6404 Typing_hooks.dispatch_global_const_hook cst
.cst_name
;
6405 let env = EnvFromDef.gconst_env tcopt cst
in
6406 let env = Typing_env.set_mode
env cst
.cst_mode
in
6407 add_decl_errors
(Option.map
(Env.get_gconst
env (snd cst
.cst_name
)) ~
f:snd
);
6409 let typed_cst_value, env =
6410 match cst
.cst_value
with
6413 match cst
.cst_type
with
6415 let ty = TI.instantiable_hint
env hint
in
6416 let env, dty
= Phase.localize_with_self
env ty in
6417 let env, te, value_type
=
6418 expr ~
expected:(fst hint
, Reason.URhint
, dty
) env value in
6419 let env = Typing_utils.sub_type
env value_type dty
in
6422 let env, te, _value_type
= expr env value in
6425 { T.cst_annotation
= save_env env;
6426 T.cst_mode
= cst
.cst_mode
;
6427 T.cst_name
= cst
.cst_name
;
6428 T.cst_type
= cst
.cst_type
;
6429 T.cst_value
= typed_cst_value;
6430 T.cst_is_define
= cst
.cst_is_define
;
6433 (* Calls the method of a class, but allows the f callback to override the
6434 * return value type *)
6435 and overload_function
p env ((), class_id
) method_id
el uel
f =
6436 let env, _ce
, ty = static_class_id
p env class_id
in
6437 let env, _tel
, _ = exprs ~is_func_arg
:true env el in
6439 class_get ~
is_method:true ~is_const
:false env ty method_id class_id
in
6440 (* call the function as declared to validate arity and input types,
6441 but ignore the result and overwrite with custom one *)
6442 let (env, res
), has_error
= Errors.try_with_error
6443 (* TODO: Should we be passing hints here *)
6444 (fun () -> (let env, _, _, ty =
6445 call ~
expected:None
p env fty el uel
in env, ty), false)
6446 (fun () -> (env, (Reason.Rwitness
p, Typing_utils.tany env)), true) in
6447 (* if there are errors already stop here - going forward would
6448 * report them twice *)
6449 if has_error
then env, T.make_typed_expr
p res
T.Any
, res
6450 else let env, ty = f env fty res
el in
6451 (* TODO TAST: do this right *)
6452 env, T.make_typed_expr
p ty T.Any
, ty
6454 and update_array_type ?lhs_of_null_coalesce
p env e1 e2
valkind =
6455 let access_type = Typing_arrays.static_array_access
env e2
in
6457 Typing_arrays.update_array_type
p access_type in
6459 | `lvalue
| `lvalue_subexpr
->
6461 raw_expr ~
valkind:`lvalue_subexpr ~in_cond
:false env e1
in
6462 let env, ty1 = type_mapper env ty1 in
6464 | (_, Lvar
(_, x)) ->
6465 (* type_mapper has updated the type in ty1 typevars, but we
6466 need to update the local variable type too *)
6467 let env, ty1 = set_valid_rvalue
p env x ty1 in
6469 | _ -> env, te1, ty1
6472 raw_expr ~in_cond
:false ?lhs_of_null_coalesce
env e1
6474 let nast_to_tast opts nast
=
6475 let convert_def = function
6476 | Nast.Fun
f -> T.Fun
(fun_def opts
f)
6477 | Nast.Constant gc
-> T.Constant
(gconst_def opts gc
)
6478 | Nast.Typedef td
-> T.Typedef
(typedef_def opts td
)
6480 match class_def opts
c with
6481 | Some
c -> T.Class
c
6482 | None
-> failwith
@@ Printf.sprintf
6483 "Error in declaration of class: %s" (snd
c.c_name
)
6485 List.map nast
convert_def