2 * Copyright (c) 2015, Facebook, Inc.
5 * This source code is licensed under the MIT license found in the
6 * LICENSE file in the "hack" directory of this source tree.
10 (* This module implements the typing.
12 * Given an Nast.program, it infers the type of all the local
13 * variables, and checks that all the types are correct (aka
22 module TFTerm
= Typing_func_terminality
23 module TUtils
= Typing_utils
24 module Reason
= Typing_reason
25 module Inst
= Decl_instantiate
26 module Type
= Typing_ops
27 module Env
= Typing_env
28 module LEnv
= Typing_lenv
29 module 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 C
= Typing_continuations
48 module Try
= Typing_try
49 module TR
= Typing_reactivity
51 (*****************************************************************************)
53 (*****************************************************************************)
55 (* A guess as to the last position we were typechecking, for use in debugging,
56 * such as figuring out what a runaway hh_server thread is doing. Updated
57 * only best-effort -- it's an approximation to point debugging in the right
58 * direction, nothing more. *)
59 let debug_last_pos = ref Pos.none
60 let debug_print_last_pos _
= print_endline
(Pos.string (Pos.to_absolute
63 (****************************************************************************)
65 (****************************************************************************)
67 let expr_hook = ref None
69 let with_expr_hook hook f
= with_context
70 ~enter
: (fun () -> expr_hook := Some hook
)
71 ~exit
: (fun () -> expr_hook := None
)
74 (*****************************************************************************)
76 (*****************************************************************************)
78 let suggest env p ty
=
79 let ty = Typing_expand.fully_expand env
ty in
80 (match Typing_print.suggest ty with
81 | "..." -> Errors.expecting_type_hint p
82 | ty -> Errors.expecting_type_hint_suggest p
ty
85 let err_witness env p
= Reason.Rwitness p
, Typing_utils.terr env
87 (* When typing an expression, we optionally pass in the type
88 * that we *expect* the expression to have.
91 Pos.t
* Reason.ureason
* locl
ty
93 let expr_error env p r
=
94 let ty = (r
, Typing_utils.terr env
) in
95 env
, T.make_typed_expr p
ty T.Any
, ty
97 let expr_any env p r
=
98 let ty = (r
, Typing_utils.tany env
) in
99 env
, T.make_typed_expr p
ty T.Any
, ty
101 let compare_field_kinds x y
=
103 | Nast.AFvalue
(p1
, _
), Nast.AFkvalue
((p2
, _
), _
)
104 | Nast.AFkvalue
((p2
, _
), _
), Nast.AFvalue
(p1
, _
) ->
105 Errors.field_kinds p1 p2
;
110 let check_consistent_fields x l
=
111 List.for_all l
(compare_field_kinds x
)
113 let unbound_name env
(pos
, name
) =
114 match Env.get_mode env
with
115 | FileInfo.Mstrict
->
116 (Errors.unbound_name_typing pos name
;
117 expr_error env pos
(Reason.Rwitness pos
))
119 | FileInfo.Mdecl
| FileInfo.Mpartial
| FileInfo.Mphp
->
120 expr_any env pos
(Reason.Rwitness pos
)
122 (* Is this type Traversable<vty> or Container<vty> for some vty? *)
123 let get_value_collection_inst ty =
125 | (_
, Tclass
((_
, c
), [vty
])) when
126 c
= SN.Collections.cTraversable
||
127 c
= SN.Collections.cContainer
->
132 (* Is this type KeyedTraversable<kty,vty>
133 * or KeyedContainer<kty,vty>
134 * or Indexish<kty,vty>
137 let get_key_value_collection_inst ty =
139 | (_
, Tclass
((_
, c
), [kty
; vty
])) when
140 c
= SN.Collections.cKeyedTraversable
||
141 c
= SN.Collections.cKeyedContainer
||
142 c
= SN.Collections.cIndexish
->
147 (* Is this type varray<vty> or a supertype for some vty? *)
148 let get_varray_inst ty =
150 (* It's varray<vty> *)
151 | (_
, Tarraykind
(AKvarray vty
)) -> Some vty
152 | _
-> get_value_collection_inst ty
154 (* Is this type one of the value collection types with element type vty? *)
155 let get_vc_inst vc_kind
ty =
157 | (_
, Tclass
((_
, c
), [vty
]))
158 when c
= vc_kind_to_name vc_kind
-> Some vty
159 | _
-> get_value_collection_inst ty
161 (* Is this type array<vty> or a supertype for some vty? *)
162 let get_akvec_inst ty =
164 | (_
, Tarraykind
(AKvec vty
)) -> Some vty
165 | _
-> get_value_collection_inst ty
167 (* Is this type array<kty,vty> or a supertype for some kty and vty? *)
168 let get_akmap_inst ty =
170 | (_
, Tarraykind
(AKmap
(kty
, vty
))) -> Some
(kty
, vty
)
171 | _
-> get_key_value_collection_inst ty
173 (* Is this type one of the three key-value collection types
174 * e.g. dict<kty,vty> or a supertype for some kty and vty? *)
175 let get_kvc_inst kvc_kind
ty =
177 | (_
, Tclass
((_
, c
), [kty
; vty
]))
178 when c
= kvc_kind_to_name kvc_kind
-> Some
(kty
, vty
)
179 | _
-> get_key_value_collection_inst ty
181 (* Is this type darray<kty, vty> or a supertype for some kty and vty? *)
182 let get_darray_inst ty =
184 (* It's darray<kty, vty> *)
185 | (_
, Tarraykind
(AKdarray
(kty
, vty
))) -> Some
(kty
, vty
)
186 | _
-> get_key_value_collection_inst ty
189 (* Try running function on each concrete supertype in turn. Return all
192 let try_over_concrete_supertypes env
ty f
=
193 let env, tyl
= TUtils.get_concrete_supertypes
env ty in
194 (* If there is just a single result then don't swallow errors *)
199 let rec iter_over_types env resl tyl
=
204 (fun () -> iter_over_types env (f
env ty::resl
) tyl
)
205 (fun _
-> iter_over_types env resl tyl
) in
206 iter_over_types env [] tyl
209 (*****************************************************************************)
210 (* Handling function/method arguments *)
211 (*****************************************************************************)
212 let param_has_attribute param attr
=
213 List.exists param
.param_user_attributes
214 (fun { ua_name
; _
} -> attr
= snd ua_name
)
216 let has_accept_disposable_attribute param
=
217 param_has_attribute param
SN.UserAttributes.uaAcceptDisposable
219 let get_param_mutability param
=
220 if param_has_attribute param
SN.UserAttributes.uaMutable
221 then Some Param_mutable
222 else if param_has_attribute param
SN.UserAttributes.uaMaybeMutable
223 then Some Param_maybe_mutable
226 (* Check whether this is a function type that (a) either returns a disposable
227 * or (b) has the <<__ReturnDisposable>> attribute
229 let is_return_disposable_fun_type env ty =
230 match Env.expand_type
env ty with
231 | _env
, (_
, Tfun ft
) ->
232 ft
.ft_return_disposable
|| Option.is_some
(Typing_disposable.is_disposable_type
env ft
.ft_ret
)
235 let enforce_param_not_disposable env param
ty =
236 if has_accept_disposable_attribute param
then ()
238 let p = param
.param_pos
in
239 match Typing_disposable.is_disposable_type
env ty with
241 Errors.invalid_disposable_hint
p (strip_ns class_name
)
245 let fun_reactivity env attrs
=
246 let r = Decl.fun_reactivity env attrs
in
247 if Attributes.mem
Naming_special_names.UserAttributes.uaOnlyRxIfArgs attrs
251 (* This function is used to determine the type of an argument.
252 * When we want to type-check the body of a function, we need to
253 * introduce the type of the arguments of the function in the environment
254 * Let's take an example, we want to check the code of foo:
256 * function foo(int $x): int {
257 * // CALL TO make_param_type on (int $x)
258 * // Now we know that the type of $x is int
260 * return $x; // in the environment $x is an int, the code is correct
263 * When we localize, we want to resolve to "static" or "$this" depending on
264 * the context. Even though we are passing in CIstatic, resolve_with_class_id
265 * is smart enough to know what to do. Why do this? Consider the following
268 * abstract const type T;
270 * private this::T $val;
272 * final public function __construct(this::T $x) {
276 * public static function create(this::T $x): this {
277 * return new static($x);
281 * class D extends C { const type T = int; }
283 * In __construct() we want to be able to assign $x to $this->val. The type of
284 * $this->val will expand to '$this::T', so we need $x to also be '$this::T'.
285 * We can do this soundly because when we construct a new class such as,
286 * 'new D(0)' we can determine the late static bound type (D) and resolve
287 * 'this::T' to 'D::T' which is int.
289 * A similar line of reasoning is applied for the static method create.
291 let make_param_local_ty attrs
env param
=
293 { (Phase.env_with_self
env) with from_class
= Some CIstatic
; } in
295 match param
.param_hint
with
296 | None
when param
.param_expr
= None
->
297 let r = Reason.Rwitness param
.param_pos
in
298 env, (r, TUtils.tany
env)
300 (* if the type is missing, use an unbound type variable *)
301 let _r, ty = Env.fresh_type
() in
302 let r = Reason.Rwitness param
.param_pos
in
305 let ty = Decl_hint.hint
env.Env.decl_env x
in
307 Decl.condition_type_from_attributes
env.Env.decl_env param
.param_user_attributes
in
308 begin match condition_type with
309 | Some
condition_type ->
310 let env, ty = Phase.localize ~
ety_env env ty in
311 begin match TR.try_substitute_type_with_condition
env condition_type ty with
315 | _
when Attributes.mem
SN.UserAttributes.uaOnlyRxIfArgs attrs
->
316 let env, ty = Phase.localize ~
ety_env env ty in
317 (* expand type to track aliased function types *)
318 let env, expanded_ty
= Env.expand_type
env ty in
320 make_function_type_mayberx
(Env.env_reactivity
env) expanded_ty
in
321 env, if adjusted_ty == expanded_ty
then ty else adjusted_ty
323 Phase.localize ~
ety_env env ty
326 let ty = match ty with
327 | _
, t
when param
.param_is_variadic
->
328 (* when checking the body of a function with a variadic
329 * argument, "f(C ...$args)", $args is a varray<C> *)
330 let r = Reason.Rvar_param param
.param_pos
in
331 let arr_values = r, t
in
332 r, Tarraykind
(AKvarray
arr_values)
335 Typing_reactivity.disallow_onlyrx_if_rxfunc_on_non_functions
env param
ty;
338 (* Given a localized parameter type and parameter information, infer
339 * a type for the parameter default expression (if present) and check that
340 * it is a subtype of the parameter type. Set the type of the parameter in
341 * the locals environment *)
342 let rec bind_param env (ty1
, param
) =
343 let env, param_te
, ty2
=
344 match param
.param_expr
with
346 (* XXX: We don't want to replace this Tany with Tdynamic, since it
347 represents the lack of a default parameter, which is valid
348 We really want a bottom type here rather than Tany, but this is fine
350 env, None
, (Reason.none
, Tany
)
352 let env, te
, ty = expr ~expected
:(param
.param_pos
, Reason.URparam
, ty1
) env e
in
353 Typing_sequencing.sequence_check_expr e
;
356 Typing_suggest.save_param
(param
.param_name
) env ty1 ty2
;
357 let env = Type.sub_type param
.param_pos
Reason.URhint
env ty2 ty1
in
359 T.param_annotation
= T.make_expr_annotation param
.param_pos ty1
;
360 T.param_hint
= param
.param_hint
;
361 T.param_is_reference
= param
.param_is_reference
;
362 T.param_is_variadic
= param
.param_is_variadic
;
363 T.param_pos
= param
.param_pos
;
364 T.param_name
= param
.param_name
;
365 T.param_expr
= param_te
;
366 T.param_callconv
= param
.param_callconv
;
367 T.param_user_attributes
= List.map param
.param_user_attributes
(user_attribute
env);
369 let mode = get_param_mode param
.param_is_reference param
.param_callconv
in
370 let id = Local_id.get param
.param_name
in
371 let env = Env.set_local
env id ty1
in
372 let env = Env.set_param
env id (ty1
, mode) in
373 let env = if has_accept_disposable_attribute param
374 then Env.set_using_var
env id else env in
376 match get_param_mutability param
with
377 | Some Param_mutable
->
378 Env.add_mutable_var
env id (param
.param_pos
, Typing_mutability_env.Borrowed
)
379 | Some Param_maybe_mutable
->
380 Env.add_mutable_var
env id (param
.param_pos
, Typing_mutability_env.MaybeMutable
)
384 (* In strict mode, we force you to give a type declaration on a parameter *)
385 (* But the type checker is nice: it makes a suggestion :-) *)
386 and check_param
env param
ty =
387 let env = Typing_attributes.check_def
env new_object
388 SN.AttributeKinds.parameter param
.param_user_attributes
in
389 match param
.param_hint
with
390 | None
-> suggest env param
.param_pos
ty
392 (* We do not permit hints to implement IDisposable or IAsyncDisposable *)
393 enforce_param_not_disposable env param
ty
395 and check_inout_return
env =
396 let params = Local_id.Map.elements
(Env.get_params
env) in
397 List.fold
params ~init
:env ~f
:begin fun env (id, ((r, ty), mode)) ->
400 (* Whenever the function exits normally, we require that each local
401 * corresponding to an inout parameter be compatible with the original
402 * type for the parameter (under subtyping rules). *)
403 let local_ty = Env.get_local
env id in
404 let env, ety
= Env.expand_type
env local_ty in
405 let pos = Reason.to_pos
(fst ety
) in
406 let param_ty = Reason.Rinout_param
(Reason.to_pos
r), ty in
407 Type.sub_type
pos Reason.URassign_inout
env ety
param_ty
411 and add_decl_errors
= function
413 | Some errors
-> Errors.merge_into_current errors
415 (*****************************************************************************)
416 (* Now we are actually checking stuff! *)
417 (*****************************************************************************)
418 and fun_def tcopt f
=
419 (* reset the expression dependent display ids for each function body *)
420 Reason.expr_display_id_map
:= IMap.empty
;
421 Typing_hooks.dispatch_enter_fun_def_hook f
;
422 let pos = fst f
.f_name
in
423 let nb = TNBody.func_body tcopt f
in
424 let env = EnvFromDef.fun_env tcopt f
in
425 add_decl_errors
(Option.map
426 (Env.get_fun
env (snd f
.f_name
))
427 ~f
:(fun x
-> Option.value_exn x
.ft_decl_errors
)
429 let env = Env.set_env_function_pos
env pos in
430 let env = Typing_attributes.check_def
env new_object
SN.AttributeKinds.fn f
.f_user_attributes
in
431 let reactive = fun_reactivity env.Env.decl_env f
.f_user_attributes
in
432 let mut = TUtils.fun_mutable f
.f_user_attributes
in
433 let env = Env.set_env_reactive
env reactive in
434 let env = Env.set_fun_mutable
env mut in
435 NastCheck.fun_
env f
nb;
436 (* Fresh type environment is actually unnecessary, but I prefer to
437 * have a guarantee that we are using a clean typing environment. *)
438 let tfun_def = Env.fresh_tenv
env (
440 let env, constraints
=
441 Phase.localize_generic_parameters_with_bounds
env f
.f_tparams
442 ~
ety_env:(Phase.env_with_self
env) in
443 let env = add_constraints
pos env constraints
in
445 localize_where_constraints
446 ~
ety_env:(Phase.env_with_self
env) env f
.f_where_constraints
in
450 env, (Reason.Rwitness
pos, Typing_utils.tany
env)
452 let ty = TI.instantiable_hint
env ret
in
453 Phase.localize_with_self
env ty in
454 let return = Typing_return.make_info f
.f_fun_kind f
.f_user_attributes
env
455 ~is_explicit
:(Option.is_some f
.f_ret
) ~is_by_ref
:f
.f_ret_by_ref
ty in
456 TI.check_params_instantiable
env f
.f_params
;
457 TI.check_tparams_instantiable
env f
.f_tparams
;
459 List.map_env
env f
.f_params
(make_param_local_ty f
.f_user_attributes
) in
460 if Env.is_strict
env then
461 List.iter2_exn ~f
:(check_param
env) f
.f_params param_tys
;
462 Typing_memoize.check_function
env f
;
463 let env, typed_params
= List.map_env
env (List.zip_exn param_tys f
.f_params
)
465 let env, t_variadic
= match f
.f_variadic
with
466 | FVvariadicArg vparam
->
467 TI.check_param_instantiable
env vparam
;
468 let env, ty = make_param_local_ty f
.f_user_attributes
env vparam
in
469 if Env.is_strict
env then
470 check_param
env vparam
ty;
471 let env, t_vparam
= bind_param env (ty, vparam
) in
472 env, T.FVvariadicArg t_vparam
474 if Env.is_strict
env then
475 Errors.ellipsis_strict_mode ~require
:`Type_and_param_name
pos;
477 | FVnonVariadic
-> env, T.FVnonVariadic
in
478 let local_tpenv = env.Env.lenv
.Env.tpenv
in
479 let env, tb
= fun_
env return pos nb f
.f_fun_kind
in
480 let env = Env.check_todo
env in
481 if Env.is_strict
env then Env.log_anonymous
env;
482 begin match f
.f_ret
with
483 | None
when Env.is_strict
env ->
484 Typing_return.suggest_return
env pos return.Typing_env_return_info.return_type
485 | None
-> Typing_suggest.save_fun_or_method f
.f_name
487 Typing_return.async_suggest_return
(f
.f_fun_kind
) hint
pos
490 T.f_annotation
= Env.save
local_tpenv env;
494 T.f_tparams
= f
.f_tparams
;
495 T.f_where_constraints
= f
.f_where_constraints
;
496 T.f_variadic
= t_variadic
;
497 T.f_params
= typed_params
;
498 T.f_fun_kind
= f
.f_fun_kind
;
499 T.f_user_attributes
= List.map f
.f_user_attributes
(user_attribute
env);
500 T.f_body
= T.NamedBody
{
502 T.fnb_unsafe
= nb.fnb_unsafe
;
504 T.f_ret_by_ref
= f
.f_ret_by_ref
;
507 Typing_hooks.dispatch_exit_fun_def_hook f
;
510 (*****************************************************************************)
511 (* function used to type closures, functions and methods *)
512 (*****************************************************************************)
514 and fun_ ?
(abstract
=false) env return pos named_body f_kind
=
515 Env.with_env
env begin fun env ->
516 debug_last_pos := pos;
517 let env = Env.set_return
env return in
518 let env = Env.set_fn_kind
env f_kind
in
519 let env, tb
= block
env named_body
.fnb_nast
in
520 Typing_sequencing.sequence_check_block named_body
.fnb_nast
;
521 let { Typing_env_return_info.return_type
= ret
; _
} = Env.get_return
env in
523 if Nast_terminality.Terminal.block
env named_body
.fnb_nast
||
525 named_body
.fnb_unsafe
||
528 else fun_implicit_return
env pos ret f_kind
in
529 debug_last_pos := Pos.none
;
533 and fun_implicit_return
env pos ret
= function
534 | Ast.FGenerator
| Ast.FAsyncGenerator
-> env
537 (* A function without a terminal block has an implicit return; the
539 let env = check_inout_return
env in
540 let rty = Reason.Rno_return
pos, Tprim
Nast.Tvoid
in
541 Typing_suggest.save_return
env ret
rty;
542 Type.sub_type
pos Reason.URreturn
env rty ret
544 (* An async function without a terminal block has an implicit return;
545 * the Awaitable<void> type *)
546 let r = Reason.Rno_return_async
pos in
547 let rty = r, Tclass
((pos, SN.Classes.cAwaitable
), [r, Tprim
Nast.Tvoid
]) in
548 Typing_suggest.save_return
env ret
rty;
549 Type.sub_type
pos Reason.URreturn
env rty ret
551 (* Perform provided typing function only if the Next continuation is present.
552 * If the Next continuation is absent, it means that we are typechecking
553 * unreachable code. *)
554 and if_next tyf
env node
=
555 match LEnv.get_cont_option
env C.Next
with
558 let env, tn
= tyf
env node
in
561 and block
env stl
= List.filter_map_env
env stl ~f
:(if_next stmt
)
563 (* Set a local; must not be already assigned if it is a using variable *)
564 and set_local ?
(is_using_clause
= false) env (pos,x
) ty =
565 if Env.is_using_var
env x
568 then Errors.duplicate_using_var
pos
569 else Errors.illegal_disposable
pos "assigned";
570 let env = Env.set_local
env x
ty in
571 if is_using_clause
then Env.set_using_var
env x
else env
573 (* Check an individual component in the expression `e` in the
574 * `using (e) { ... }` statement.
575 * This consists of either
576 * a simple assignment `$x = e`, in which `$x` is the using variable, or
577 * an arbitrary expression `e`, in which case a temporary is the using
578 * variable, inaccessible in the source.
579 * Return the typed expression and its type, and any variables that must
580 * be designated as "using variables" for avoiding escapes.
582 and check_using_expr has_await
env ((pos, content
) as using_clause
) =
584 (* Simple assignment to local of form `$lvar = e` *)
585 | Binop
(Ast.Eq None
, (lvar_pos
, Lvar lvar
), e
) ->
586 let env, te
, ty = expr ~is_using_clause
:true env e
in
587 let env = Typing_disposable.enforce_is_disposable_type
env has_await
(fst e
) ty in
588 let env = set_local ~is_using_clause
:true env lvar
ty in
589 (* We are assigning a new value to the local variable, so we need to
590 * generate a new expression id
592 let env = Env.set_local_expr_id
env (snd lvar
) (Ident.tmp
()) in
593 env, (T.make_typed_expr
pos ty (T.Binop
(Ast.Eq None
,
594 T.make_typed_expr lvar_pos
ty (T.Lvar lvar
), te
)), [snd lvar
])
596 (* Arbitrary expression. This will be assigned to a temporary *)
598 let env, typed_using_clause
, ty = expr ~is_using_clause
:true env using_clause
in
599 let env = Typing_disposable.enforce_is_disposable_type
env has_await
pos ty in
600 env, (typed_using_clause
, [])
602 (* Check the using clause e in
603 * `using (e) { ... }` statement (`has_await = false`) or
604 * `await using (e) { ... }` statement (`has_await = true`).
605 * The expression consists of a comma-separated list of expressions (Expr_list)
606 * or a single expression.
607 * Return the typed expression, and any variables that must
608 * be designated as "using variables" for avoiding escapes.
610 and check_using_clause
env has_await
((pos, content
) as using_clause
) =
612 | Expr_list using_clauses
->
613 let env, pairs
= List.map_env
env using_clauses
(check_using_expr has_await
) in
614 let typed_using_clauses, vars_list
= List.unzip pairs
in
615 let ty_ = Ttuple
(List.map
typed_using_clauses T.get_type
) in
616 let ty = (Reason.Rnone
, ty_) in
617 env, T.make_typed_expr
pos ty (T.Expr_list
typed_using_clauses),
618 List.concat vars_list
620 let env, (typed_using_clause
, vars
) = check_using_expr has_await
env using_clause
in
621 env, typed_using_clause
, vars
623 (* Require a new construct with disposable *)
624 and enforce_return_disposable _env e
=
628 | _
, Await
(_
, Call _
) -> ()
630 Errors.invalid_return_disposable
p
632 (* Wrappers around the function with the same name in Typing_lenv, which only
633 * performs the move/save and merge operation if we are in a try block or in a
634 * function with return type 'noreturn'.
635 * This enables significant perf improvement, because this is called at every
636 * function of method call, when most calls are outside of a try block. *)
637 and move_and_merge_next_in_catch
env =
638 if env.Env.in_try
|| (TFTerm.is_noreturn
env)
639 then LEnv.move_and_merge_next_in_cont
env C.Catch
640 else LEnv.drop_cont
env C.Next
642 and save_and_merge_next_in_catch
env =
643 if env.Env.in_try
|| (TFTerm.is_noreturn
env)
644 then LEnv.save_and_merge_next_in_cont
env C.Catch
647 and stmt
env = function
649 let env = if env.Env.in_case
650 then LEnv.move_and_merge_next_in_cont
env C.Fallthrough
655 let env = move_and_merge_next_in_catch
env in
661 let env, te
, ty = expr ~is_expr_statement
:true env e
in
662 let env = if TFTerm.expression_exits te
ty
663 then LEnv.move_and_merge_next_in_cont
env C.Exit
665 (* NB: this check does belong here and not in expr, even though it only
666 * applies to expressions -- we actually want to perform the check on
667 * statements that are expressions, e.g., "foo();" we want to check, but
668 * "return foo();" we do not even though the expression "foo()" is a
669 * subexpression of the statement "return foo();". *)
671 | Nast.Binop
(Ast.Eq _
, _
, _
) -> ()
672 | _
-> Async.enforce_not_awaitable
env (fst e
) ty);
675 let env, te
, _
= expr
env e
in
677 (* We stash away the locals environment because condition updates it
678 * locally for checking b1. For example, we might have condition
679 * $x === null, or $x instanceof C, which changes the type of $x in
681 let parent_lenv = env.Env.lenv
in
683 let env = condition
env true e
in
684 let env, tb1
= block
env b1
in
685 let lenv1 = env.Env.lenv
in
687 let env = { env with Env.lenv
= parent_lenv } in
688 let env = condition
env false e
in
689 let env, tb2
= block
env b2
in
690 let lenv2 = env.Env.lenv
in
692 let env = LEnv.union_lenvs
env parent_lenv lenv1 lenv2 in
693 (* TODO TAST: annotate with joined types *)
694 env, T.If
(te
, tb1
, tb2
)
695 | Return
(p, None
) ->
696 let env = check_inout_return
env in
697 let rty = Typing_return.wrap_awaitable
env p (Reason.Rwitness
p, Tprim Tvoid
) in
698 let { Typing_env_return_info.return_type
= expected_return
; _
} = Env.get_return
env in
699 Typing_suggest.save_return
env expected_return
rty;
700 let env = Type.sub_type
p Reason.URreturn
env rty expected_return
in
701 let env = LEnv.move_and_merge_next_in_cont
env C.Exit
in
702 env, T.Return
(p, None
)
703 | Return
(p, Some e
) ->
704 let env = check_inout_return
env in
706 let Typing_env_return_info.{
707 return_type
; return_disposable
; return_mutable
; return_explicit
; return_by_ref
;
708 return_void_to_rx
} = Env.get_return
env in
711 then Some
(pos, Reason.URreturn
,
712 Typing_return.strip_awaitable
(Env.get_fn_kind
env) env return_type
)
713 else Some
(pos, Reason.URreturn
, (Reason.Rwitness
p, Typing_utils.tany
env)) in
714 if return_disposable
then enforce_return_disposable
env e
;
715 let env, te
, rty = expr ~is_using_clause
:return_disposable ?
expected:expected env e
in
716 if Env.env_reactivity
env <> Nonreactive
718 Typing_mutability.check_function_return_value
719 ~function_returns_mutable
:return_mutable
720 ~function_returns_void_for_rx
: return_void_to_rx
726 then begin match snd e
with
727 | Array_get _
-> Errors.return_ref_in_array
p
730 let rty = Typing_return.wrap_awaitable
env p rty in
731 let env, rty = (match snd
(Env.expand_type
env return_type
) with
732 | r, Tprim Tvoid
when not
(TUtils.is_void_type_of_null
env) ->
733 (* Yell about returning a value from a void function. This catches
734 * more issues than just unifying with void would do -- in particular
735 * just unifying allows you to return a Typing_utils.tany env from a void function,
736 * which is clearly wrong. Note this check is best-effort; if the
737 * function returns a generic type which later ends up being Tvoid
738 * then there's not much we can do here. *)
739 Errors.return_in_void
p (Reason.to_pos
r);
740 env, T.Return
(p, Some te
)
741 | _
, Tunresolved _
->
742 (* we allow return types to grow for anonymous functions *)
743 let env, rty = TUtils.unresolved
env rty in
744 let env = Type.sub_type
pos Reason.URreturn
env rty return_type
in
745 env, T.Return
(p, Some te
)
746 | _
, (Terr
| Tany
| Tmixed
| Tnonnull
| Tarraykind _
| Toption _
| Tprim _
747 | Tvar _
| Tfun _
| Tabstract
(_
, _
) | Tclass
(_
, _
) | Ttuple _
748 | Tanon
(_
, _
) | Tobject
| Tshape _
| Tdynamic
) ->
749 Typing_suggest.save_return
env return_type
rty;
750 let env = Type.sub_type
pos Reason.URreturn
env rty return_type
in
751 env, T.Return
(p, Some te
)
753 let env = LEnv.move_and_merge_next_in_cont
env C.Exit
in
756 (* NOTE: leaks scope as currently implemented; this matches
757 the behavior in naming (cf. `do_stmt` in naming/naming.ml).
759 let env, (tb
, te
) = LEnv.stash_and_do
env [C.Continue
; C.Break
] (fun env ->
760 let env, _
= block
env b
in
761 let env, te
, _
= if_next_expr
env e
in
762 (* saving the locals in continue here even if there is no continue
763 * statement because they must be merged at the end of the loop, in
764 * case there is no iteration *)
765 let env = LEnv.save_and_merge_next_in_cont
env C.Continue
in
767 if env.Env.in_loop
then 1 else Typing_alias.get_depth st
in
768 let env, tb
= Env.in_loop
env begin
769 iter_n_acc
alias_depth begin fun env ->
770 let env = LEnv.update_next_from_conts
env [C.Continue
; C.Next
] in
771 (* Reset let variables *)
772 let env = if_next_condition
env true e
in
773 let env, tb
= block
env b
in
776 let env = LEnv.update_next_from_conts
env [C.Continue
; C.Next
] in
777 let env = if_next_condition
env false e
in
778 let env = LEnv.update_next_from_conts
env [C.Break
; C.Next
] in
781 | While
(e
, b
) as st
->
782 let env, te
, _
= expr
env e
in
783 let env, (te
, tb
) = LEnv.stash_and_do
env [C.Continue
; C.Break
] (fun env ->
784 let env = LEnv.save_and_merge_next_in_cont
env C.Continue
in
786 if env.Env.in_loop
then 1 else Typing_alias.get_depth st
in
787 let env, tb
= Env.in_loop
env begin
788 iter_n_acc
alias_depth begin fun env ->
789 let env = LEnv.update_next_from_conts
env [C.Continue
; C.Next
] in
790 (* Reset let variables *)
791 let env = if_next_condition
env true e
in
792 (* TODO TAST: avoid repeated generation of block *)
793 let env, tb
= block
env b
in
797 let env = LEnv.update_next_from_conts
env [C.Continue
; C.Next
] in
798 let env = if_next_condition
env false e
in
799 let env = LEnv.update_next_from_conts
env [C.Break
; C.Next
] in
801 env, T.While
(te
, tb
)
802 | Using
(has_await
, using_clause
, using_block
) ->
803 let env, typed_using_clause
, using_vars
= check_using_clause
env has_await using_clause
in
804 let env, typed_using_block
= block
env using_block
in
805 (* Remove any using variables from the environment, as they should not
806 * be in scope outside the block *)
807 let env = List.fold_left using_vars ~init
:env ~f
:Env.unset_local
in
808 env, T.Using
(has_await
, typed_using_clause
, typed_using_block
)
809 | For
(e1
, e2
, e3
, b
) as st
->
810 (* For loops leak their initalizer, but nothing that's defined in the
813 let (env, te1
, _
) = expr
env e1
in (* initializer *)
814 let (env, te2
, _
) = expr
env e2
in
815 let env, (te1
, te2
, te3
, tb
) = LEnv.stash_and_do
env [C.Continue
; C.Break
]
817 let env = LEnv.save_and_merge_next_in_cont
env C.Continue
in
819 if env.Env.in_loop
then 1 else Typing_alias.get_depth st
in
820 let env, (tb
, te3
) = Env.in_loop
env begin
821 iter_n_acc
alias_depth begin fun env ->
822 (* Reset let variables *)
823 let env = if_next_condition
env true e2
in
824 let env, tb
= block
env b
in
825 let env = LEnv.update_next_from_conts
env [C.Continue
; C.Next
] in
826 let (env, te3
, _
) = if_next_expr
env e3
in
830 let env = LEnv.update_next_from_conts
env [C.Continue
; C.Next
] in
831 let env = if_next_condition
env false e2
in
832 let env = LEnv.update_next_from_conts
env [C.Break
; C.Next
] in
833 env, (te1
, te2
, te3
, tb
)) in
834 env, T.For
(te1
, te2
, te3
, tb
)
835 | Switch
((pos, _
) as e
, cl
) ->
836 let env, te
, ty = expr
env e
in
837 Async.enforce_not_awaitable
env (fst e
) ty;
838 let env = check_exhaustiveness
env (fst e
) ty cl
in
839 (* NB: A 'continue' inside a 'switch' block is equivalent to a 'break'.
841 * http://php.net/manual/en/control-structures.continue.php *)
842 let env, (te
, tcl
) = LEnv.stash_and_do
env [C.Continue
; C.Break
]
844 let parent_locals = LEnv.get_all_locals
env in
845 let case_list env = case_list parent_locals ty env pos cl
in
846 let env, tcl
= Env.in_case
env case_list in
847 let env = LEnv.update_next_from_conts
env
848 [C.Continue
; C.Break
; C.Next
] in
850 env, T.Switch
(te
, tcl
)
851 | Foreach
(e1
, e2
, b
) as st
->
852 let check_dynamic env ty ~f
=
853 if TUtils.is_dynamic
env ty then
856 (* It's safe to do foreach over a disposable, as no leaking is possible *)
857 let env, te1
, ty1
= expr ~accept_using_var
:true env e1
in
858 TR.check_foreach_collection
env (fst e1
) ty1
;
859 let env, (te1
, te2
, tb
) = LEnv.stash_and_do
env [C.Continue
; C.Break
]
861 let env = LEnv.save_and_merge_next_in_cont
env C.Continue
in
862 let env, ty2
= as_expr
env (fst e1
) e2
in
864 check_dynamic env ty1 ~f
:begin fun () ->
865 Type.sub_type
(fst e1
) Reason.URforeach
env ty1 ty2
868 if env.Env.in_loop
then 1 else Typing_alias.get_depth st
in
869 let env, (te2
, tb
) = Env.in_loop
env begin
870 iter_n_acc
alias_depth begin fun env ->
871 let env = LEnv.update_next_from_conts
env [C.Continue
; C.Next
] in
872 let env, te2
= bind_as_expr
env ty1 ty2 e2
in
873 let env, tb
= block
env b
in
877 let env = LEnv.update_next_from_conts
env
878 [C.Continue
; C.Break
; C.Next
] in
879 env, (te1
, te2
, tb
)) in
880 env, T.Foreach
(te1
, te2
, tb
)
881 | Try
(tb
, cl
, fb
) ->
882 let env, ttb
, tcl
, tfb
= try_catch
env tb cl fb
in
883 env, T.Try
(ttb
, tcl
, tfb
)
885 Typing_reactivity.disallow_static_or_global_in_reactive_context
env el
887 let env = List.fold_left el ~f
:begin fun env e
->
889 | _
, Binop
(Ast.Eq _
, (_
, Lvar
(p, x
)), _
) ->
890 Env.add_todo
env (TGen.no_generic
p x
)
893 let env, tel
, _
= exprs
env el
in
894 env, T.Static_var tel
896 Typing_reactivity.disallow_static_or_global_in_reactive_context
env el
898 let env = List.fold_left el ~f
:begin fun env e
->
900 | _
, Binop
(Ast.Eq _
, (_
, Lvar
(p, x
)), _
) ->
901 Env.add_todo
env (TGen.no_generic
p x
)
904 let env, tel
, _
= exprs
env el
in
905 env, T.Global_var tel
906 | Throw
(is_terminal
, e
) ->
908 let env, te
, ty = expr
env e
in
909 let env = exception_ty
p env ty in
910 let env = move_and_merge_next_in_catch
env in
911 env, T.Throw
(is_terminal
, te
)
913 let env = LEnv.move_and_merge_next_in_cont
env C.Continue
in
916 let env = LEnv.move_and_merge_next_in_cont
env C.Break
in
918 | Let
((p, x
) as id, h
, rhs
) ->
919 let env, hint_ty
, expected = match h
with
922 { (Phase.env_with_self
env) with from_class
= Some CIstatic
; } in
923 let hint_ty = Decl_hint.hint
env.Env.decl_env
(p, h
) in
924 let env, hint_ty = Phase.localize ~
ety_env env hint_ty in
925 env, Some
hint_ty, Some
(p, Reason.URhint
, hint_ty)
926 | None
-> env, None
, None
928 let env, t_rhs
, rhs_ty
= expr
env rhs
in
929 let env, _
= match hint_ty with
931 let env = check_expected_ty
"Let" env rhs_ty
expected in
932 set_valid_rvalue
p env x
ty
933 | None
-> set_valid_rvalue
p env x rhs_ty
935 (* Transfer expression ID with RHS to let varible if RHS is another variable *)
936 let env = match rhs
with
937 | _
, ImmutableVar
(_
, x_rhs
) | _
, Lvar
(_
, x_rhs
) ->
938 let eid_rhs = Env.get_local_expr_id
env x_rhs
in
941 ~f
:(Env.set_local_expr_id
env x
)
944 env, T.Let
(id, h
, t_rhs
)
946 and check_exhaustiveness
env pos ty caselist
=
947 check_exhaustiveness_
env pos ty caselist
false
949 and check_exhaustiveness_
env pos ty caselist enum_coming_from_unresolved
=
950 (* Right now we only do exhaustiveness checking for enums. *)
951 (* This function has a built in hack where if Tunresolved has an enum
952 inside then it tells the enum exhaustiveness checker to
953 not punish for extra default *)
954 let env, (_
, ty) = Env.expand_type
env ty in
957 let new_enum = enum_coming_from_unresolved
||
958 (List.length tyl
> 1 && List.exists tyl ~f
:begin fun cur_ty
->
959 let _, (_, cur_ty
) = Env.expand_type
env cur_ty
in
961 | Tabstract
(AKenum
_, _) -> true
964 List.fold_left tyl ~init
:env ~f
:begin fun env ty ->
965 check_exhaustiveness_
env pos ty caselist
new_enum
967 | Tabstract
(AKenum
id, _) ->
968 let dep = Dep.AllMembers
id in
969 Option.iter
env.Env.decl_env
.Decl_env.droot
970 (fun root
-> Typing_deps.add_idep root
dep);
971 let tc = unsafe_opt
@@ Env.get_enum
env id in
972 Typing_enum.check_enum_exhaustiveness
pos tc
973 caselist enum_coming_from_unresolved
;
975 | Terr
| Tany
| Tmixed
| Tnonnull
| Tarraykind
_ | Tclass
_ | Toption
_
976 | Tprim
_ | Tvar
_ | Tfun
_ | Tabstract
(_, _) | Ttuple
_ | Tanon
(_, _)
977 | Tobject
| Tshape
_ | Tdynamic
-> env
979 and finally_cont fb
env ctx
=
980 let env = LEnv.replace_cont
env C.Next
(Some ctx
) in
981 let env, _tfb
= block
env fb
in
982 env, LEnv.get_all_locals
env
987 let env = LEnv.update_next_from_conts
env [C.Next
; C.Finally
] in
990 let parent_locals = LEnv.get_all_locals
env in
991 (* First typecheck the finally block against all continuations merged
993 * During this phase, record errors found in the finally block, but discard
994 * the resulting environment. *)
995 let env'
= LEnv.update_next_from_conts
env C.all
in
996 let _, tfb
= block
env' fb
in
997 (* Second, typecheck the finally block once against each continuation. This
998 * helps be more clever about what each continuation will be after the
1000 * We don't want to record errors during this phase, because certain types
1001 * of errors will fire wrongly. For example, if $x is nullable in some
1002 * continuations but not in others, then we must use `?->` on $x, but an
1003 * error will fire when typechecking the finally block againts continuations
1004 * where $x is non-null. *)
1005 let finally_cont env _key
= finally_cont fb
env in
1006 let env, locals_map
= Errors.ignore_
(fun () ->
1007 CMap.map_env
finally_cont env parent_locals) in
1008 let env, locals
= Try.finally_merge
env locals_map
in
1009 (Env.env_with_locals
env locals
), tfb
1011 and try_catch
env tb cl fb
=
1012 let parent_locals = LEnv.get_all_locals
env in
1013 let env = LEnv.drop_conts
env
1014 [C.Break
; C.Continue
; C.Exit
; C.Catch
; C.Finally
] in
1015 let env, (ttb
, tcb
) = Env.in_try
env (fun env ->
1016 let env, ttb
= block
env tb
in
1017 let env = LEnv.move_and_merge_next_in_cont
env C.Finally
in
1018 (* If there is no catch continuation, this means the try block has not
1019 * thrown, so the catch blocks are not reached, so we don't typecheck them. *)
1020 let env, tcb
= match LEnv.get_cont_option
env C.Catch
with
1023 let env, lenvtcblist
= List.map_env
env ~f
:(catch catchctx
) cl
in
1024 let lenvl, tcb
= List.unzip lenvtcblist
in
1025 let env = LEnv.union_lenv_list
env env.Env.lenv
lenvl in
1026 let env = LEnv.move_and_merge_next_in_cont
env C.Finally
in
1029 let env, tfb
= finally
env fb
in
1030 let env = LEnv.drop_cont
env C.Finally
in
1031 let env = LEnv.restore_and_merge_conts_from
1032 env parent_locals [C.Break
; C.Continue
; C.Exit
; C.Catch
; C.Finally
] in
1035 and case_list parent_locals ty env switch_pos cl
=
1036 let initialize_next_cont env =
1037 let env = LEnv.restore_conts_from
env parent_locals [C.Next
] in
1038 let env = LEnv.update_next_from_conts
env [C.Next
; C.Fallthrough
] in
1039 LEnv.drop_cont
env C.Fallthrough
in
1041 let check_fallthrough env switch_pos case_pos block rest_of_list ~is_default
=
1042 if not
@@ List.is_empty block
then
1043 begin match rest_of_list
with
1046 begin match LEnv.get_cont_option
env C.Next
with
1048 if is_default
then Errors.default_fallthrough switch_pos
1049 else Errors.case_fallthrough switch_pos case_pos
1057 | Default b
:: rl
->
1058 let env = initialize_next_cont env in
1059 (* TODO this is wrong, should continue on to the other cases, but it
1060 * doesn't matter in practice since our parser won't parse default
1061 * anywhere but in the last position :) Should fix all of this as well
1062 * as totality detection for switch. *)
1063 let env, tb
= block
env b
in
1064 check_fallthrough env switch_pos
Pos.none b rl ~is_default
:true;
1066 | (Case
((pos, _) as e
, b
)) :: rl
->
1067 (* TODO - we should consider handling the comparisons the same
1068 * way as Binop Ast.EqEq, since case statements work using ==
1069 * comparison rules *)
1071 let env = initialize_next_cont env in
1072 let ty_num = (Reason.Rnone
, Tprim
Nast.Tnum
) in
1073 let ty_arraykey = (Reason.Rnone
, Tprim
Nast.Tarraykey
) in
1074 let both_are_sub_types env tprim ty1 ty2
=
1075 (SubType.is_sub_type
env ty1 tprim
) &&
1076 (SubType.is_sub_type
env ty2 tprim
) in
1077 let env, te
, ty2
= expr
env e
in
1078 let env, _ = if (both_are_sub_types env ty_num ty ty2
) ||
1079 (both_are_sub_types env ty_arraykey ty ty2
)
1081 else Type.unify
(fst e
) Reason.URnone
env ty ty2
in
1082 let env, tb
= block
env b
in
1083 check_fallthrough env switch_pos
pos b rl ~is_default
:false;
1084 let env, tcl
= case_list parent_locals ty env switch_pos rl
in
1085 env, T.Case
(te
, tb
)::tcl
1087 and catch catchctx
env (sid
, exn
, b
) =
1088 let env = LEnv.replace_cont
env C.Next
(Some catchctx
) in
1089 let cid = CI
(sid
, []) in
1090 let ety_p = (fst sid
) in
1091 let env, _, _ = instantiable_cid
ety_p env cid in
1092 let env, _te
, ety
= static_class_id ~check_constraints
:false ety_p env cid in
1093 let env = exception_ty
ety_p env ety
in
1094 let env = set_local
env exn ety
in
1095 let env, tb
= block
env b
in
1096 env, (env.Env.lenv
, (sid
, exn
, tb
))
1098 and as_expr
env pe
= function
1100 let env, ty = Env.fresh_unresolved_type
env in
1101 let tvector = Tclass
((pe
, SN.Collections.cTraversable
), [ty]) in
1102 env, (Reason.Rforeach pe
, tvector)
1104 let env, ty1
= Env.fresh_unresolved_type
env in
1105 let env, ty2
= Env.fresh_unresolved_type
env in
1106 let tmap = Tclass
((pe
, SN.Collections.cKeyedTraversable
), [ty1
; ty2
]) in
1107 env, (Reason.Rforeach pe
, tmap)
1109 let env, ty = Env.fresh_unresolved_type
env in
1110 let tvector = Tclass
((pe
, SN.Classes.cAsyncIterator
), [ty]) in
1111 env, (Reason.Rasyncforeach pe
, tvector)
1113 let env, ty1
= Env.fresh_unresolved_type
env in
1114 let env, ty2
= Env.fresh_unresolved_type
env in
1115 let tmap = Tclass
((pe
, SN.Classes.cAsyncKeyedIterator
), [ty1
; ty2
]) in
1116 env, (Reason.Rasyncforeach pe
, tmap)
1118 and bind_as_expr
env loop_ty
ty aexpr
=
1119 let env, ety
= Env.expand_type
env ty in
1122 | _, Tclass
((p, _), [ty2
]) ->
1123 (p, (Reason.Rnone
, TUtils.desugar_mixed
Reason.Rnone
), ty2
)
1124 | _, Tclass
((p, _), [ty1
; ty2
]) -> (p, ty1
, ty2
)
1125 | _ -> assert false in
1126 (* Set id as dynamic if the foreach loop was dynamic *)
1127 let env, eloop_ty
= Env.expand_type
env loop_ty
in
1128 let ty1, ty2
= if TUtils.is_dynamic
env eloop_ty
then
1129 (fst
ty1, Tdynamic
), (fst ty2
, Tdynamic
) else ty1, ty2
in
1132 let env, te
, _ = assign
p env ev ty2
in
1134 | Await_as_v
(p, ev
) ->
1135 let env, te
, _ = assign
p env ev ty2
in
1136 env, T.Await_as_v
(p, te
)
1137 | As_kv
((p, ImmutableVar
((_, k
) as id)), ev
)
1138 | As_kv
((p, Lvar
((_, k
) as id)), ev
) ->
1139 let env, ty1'
= set_valid_rvalue
p env k
ty1 in
1140 let env, te
, _ = assign
p env ev ty2
in
1141 env, T.As_kv
(T.make_typed_expr
p ty1'
(T.Lvar
id), te
)
1142 | Await_as_kv
(p, (p1
, ImmutableVar
((_, k
) as id)), ev
)
1143 | Await_as_kv
(p, (p1
, Lvar
((_, k
) as id)), ev
) ->
1144 let env, ty1'
= set_valid_rvalue
p env k
ty1 in
1145 let env, te
, _ = assign
p env ev ty2
in
1146 env, T.Await_as_kv
(p, T.make_typed_expr p1
ty1'
(T.Lvar
id), te
)
1147 | _ -> (* TODO Probably impossible, should check that *)
1150 and if_next_expr
env (p, _ as e
) =
1151 match LEnv.get_cont_option
env C.Next
with
1152 | None
-> expr_error env p (Reason.Rwitness
p)
1153 | Some
_ -> expr
env e
1157 ?
(accept_using_var
= false)
1158 ?
(is_using_clause
= false)
1159 ?
(is_expr_statement
= false)
1160 ?
(allow_non_awaited_awaitable_in_rx
=false)
1164 begin match expected with
1166 | Some
(_, r, ty) ->
1167 Typing_log.log_types
1 (fst e
) env
1168 [Typing_log.Log_sub
("Typing.expr " ^
Typing_reason.string_of_ureason
r,
1169 [Typing_log.Log_type
("expected_ty", ty)])] end;
1170 raw_expr ~accept_using_var ~is_using_clause ~is_expr_statement
1171 ~allow_non_awaited_awaitable_in_rx
1172 ?is_func_arg ?forbid_uref ?
expected ~in_cond
:false env e
1176 ?
(accept_using_var
= false)
1177 ?
(is_using_clause
= false)
1178 ?
(is_expr_statement
= false)
1179 ?
(allow_non_awaited_awaitable_in_rx
=false)
1181 ?lhs_of_null_coalesce
1184 ?valkind
:(valkind
=`other
)
1186 debug_last_pos := fst e
;
1188 expr_ ~in_cond ~accept_using_var ~is_using_clause ~is_expr_statement ?
expected
1189 ?lhs_of_null_coalesce ?is_func_arg ?forbid_uref
1191 let () = match !expr_hook with
1192 | Some f
-> f e
(Typing_expand.fully_expand
env ty)
1194 if Env.env_local_reactive
env
1195 && not allow_non_awaited_awaitable_in_rx
1196 && not
(TypecheckerOptions.unsafe_rx
(Env.get_options
env))
1197 then begin match ty with
1198 | _, Tclass
((_, cls
), _) when cls
= SN.Classes.cAwaitable
->
1199 Errors.non_awaited_awaitable_in_rx
(fst e
);
1205 let valkind = `lvalue
in
1206 expr_ ~in_cond
:false ~
valkind env e
1208 and is_pseudo_function s
=
1209 s
= SN.PseudoFunctions.hh_show
||
1210 s
= SN.PseudoFunctions.hh_show_env
||
1211 s
= SN.PseudoFunctions.hh_log_level
||
1212 s
= SN.PseudoFunctions.hh_loop_forever
1214 and loop_forever
env =
1215 (* forever = up to 10 minutes, to avoid accidentally stuck processes *)
1217 (* Look up things in shared memory occasionally to have a chance to be
1219 match Env.get_class
env "FOR_TEST_ONLY" with
1220 | None
-> Unix.sleep
1;
1223 Utils.assert_false_log_backtrace
1224 (Some
"hh_loop_forever was looping for more than 10 minutes")
1226 (* $x ?? 0 is handled similarly to $x ?: 0, except that the latter will also
1227 * look for sketchy null checks in the condition. *)
1228 (* TODO TAST: type refinement should be made explicit in the typed AST *)
1229 and eif
env ~
expected ~coalesce ~in_cond
p c e1 e2
=
1230 let condition = condition ~lhs_of_null_coalesce
:coalesce
in
1231 let env, tc, tyc
= raw_expr ~in_cond ~lhs_of_null_coalesce
:coalesce
env c
in
1232 let parent_lenv = env.Env.lenv
in
1233 let c = if coalesce
then (p, Binop
(Ast.Diff2
, c, (p, Null
))) else c in
1234 let env = condition env true c in
1235 let env, te1
, ty1 = match e1
with
1237 let env, ty = TUtils.non_null
env tyc
in
1240 let env, te1
, ty1 = expr ?
expected ~allow_non_awaited_awaitable_in_rx
:true env e1
in
1243 let lenv1 = env.Env.lenv
in
1244 let env = { env with Env.lenv
= parent_lenv } in
1245 let env = condition env false c in
1246 let env, te2
, ty2
= expr ?
expected ~allow_non_awaited_awaitable_in_rx
:true env e2
in
1247 let lenv2 = env.Env.lenv
in
1248 let fake_members = LEnv.intersect_fake
lenv1 lenv2 in
1249 (* we restore the locals to their parent state so as not to leak the
1250 * effects of the `condition` calls above *)
1251 let env = { env with Env.lenv
=
1252 { parent_lenv with Env.fake_members = fake_members } } in
1253 (* This is a shortened form of what we do in Typing_lenv.union_lenvs. The
1254 * latter takes local environments as arguments, but our types here
1255 * aren't assigned to local variables in an environment *)
1256 (* TODO: Omit if expected type is present and checked in calls to expr *)
1258 if Typing_defs.ty_equal
ty1 ty2
1261 let env, ty1 = TUtils.unresolved
env ty1 in
1262 let env, ty2
= TUtils.unresolved
env ty2
in
1263 Type.union
env.Env.pos Reason.URnone
env ty1 ty2
in
1264 let te = if coalesce
then T.Binop
(Ast.QuestionQuestion
, tc, te2
) else T.Eif
(tc, te1
, te2
) in
1265 env, T.make_typed_expr
p ty te, ty
1267 and check_escaping_var
env (pos, x
) =
1268 if Env.is_using_var
env x
1271 then Errors.escaping_this
pos
1273 if Option.is_some
(Local_id.Map.get x
(Env.get_params
env))
1274 then Errors.escaping_disposable_parameter
pos
1275 else Errors.escaping_disposable
pos
1278 and exprs ?
(accept_using_var
= false) ?
(allow_non_awaited_awaitable_in_rx
=false)
1279 ?is_func_arg ?
expected env el
=
1285 let env, te, ty = expr ~accept_using_var ~allow_non_awaited_awaitable_in_rx
1286 ?is_func_arg ?
expected env e
in
1287 let env, tel
, tyl
= exprs ~accept_using_var ~allow_non_awaited_awaitable_in_rx
1288 ?is_func_arg ?
expected env el
in
1289 env, te::tel
, ty::tyl
1291 and exprs_expected
(pos, ur
, expected_tyl
) env el
=
1292 match el
, expected_tyl
with
1295 | e
::el
, expected_ty
::expected_tyl
->
1296 let env, te, ty = expr ~
expected:(pos, ur
, expected_ty
) env e
in
1297 let env, tel
, tyl
= exprs_expected
(pos, ur
, expected_tyl
) env el
in
1298 env, te::tel
, ty::tyl
1305 ?
(accept_using_var
= false)
1306 ?
(is_using_clause
= false)
1307 ?
(is_expr_statement
= false)
1308 ?lhs_of_null_coalesce
1309 ?
(is_func_arg
=false)
1310 ?
(forbid_uref
=false)
1311 ~
(valkind: [> `lvalue
| `lvalue_subexpr
| `other
])
1313 let make_result env te ty =
1314 env, T.make_typed_expr
p ty te, ty in
1317 * Given a list of types, computes their supertype. If any of the types are
1318 * unknown (e.g., comes from PHP), the supertype will be Typing_utils.tany env.
1320 let compute_supertype ~
expected env tys
=
1321 let env, supertype
=
1323 | None
-> Env.fresh_unresolved_type
env
1324 | Some
(_, _, ty) -> env, ty in
1325 let subtype_value env ty =
1326 Type.sub_type
p Reason.URarray_value
env ty supertype
in
1327 let env = List.fold_left tys ~init
:env ~f
:subtype_value in
1328 if List.exists tys
(fun (_, ty) -> ty = Typing_utils.tany
env) then
1329 (* If one of the values comes from PHP land, we have to be conservative
1330 * and consider that we don't know what the type of the values are. *)
1331 env, (Reason.Rwitness
p, Typing_utils.tany
env)
1336 * Given a 'a list and a method to extract an expr and its ty from a 'a, this
1337 * function extracts a list of exprs from the list, and computes the supertype
1338 * of all of the expressions' tys.
1340 let compute_exprs_and_supertype ~
expected env l extract_expr_and_ty
=
1341 let env, exprs_and_tys
= List.map_env
env l
(extract_expr_and_ty ~
expected) in
1342 let exprs, tys
= List.unzip exprs_and_tys
in
1343 let env, supertype
= compute_supertype ~
expected env tys
in
1344 env, exprs, supertype
in
1346 let shape_and_tuple_arrays_enabled =
1348 TypecheckerOptions.experimental_feature_enabled
1349 (Env.get_options
env)
1350 TypecheckerOptions.experimental_disable_shape_and_tuple_arrays
in
1352 let null_coalesce_assignment_enabled =
1353 TypecheckerOptions.experimental_feature_enabled
1354 (Env.get_options
env)
1355 TypecheckerOptions.experimental_null_coalesce_assignment
in
1357 let subtype_arraykey ~class_name ~key_pos
env key_ty
=
1358 let ty_arraykey = Reason.Ridx_dict key_pos
, Tprim Tarraykey
in
1359 Type.sub_type
p (Reason.index_class class_name
) env key_ty
ty_arraykey in
1361 let check_call ~is_using_clause ~
expected ~is_expr_statement
env p call_type e hl el uel ~in_suspend
=
1362 let env, te, result
=
1363 dispatch_call ~is_using_clause ~
expected ~is_expr_statement
p env call_type e hl el uel ~in_suspend
in
1364 let env = Env.forget_members
env p in
1368 | Any
-> expr_error env p (Reason.Rwitness
p)
1370 (* TODO: use expected type to determine expected element type *)
1371 make_result env (T.Array
[]) (Reason.Rwitness
p, Tarraykind AKempty
)
1373 (* TODO: use expected type to determine expected element type *)
1374 when Typing_arrays.is_shape_like_array
env l
&&
1375 shape_and_tuple_arrays_enabled ->
1376 let env, (tafl
, fdm
) = List.fold_left_env
env l
1377 ~init
:([], ShapeMap.empty
)
1378 ~f
:begin fun env (tafl
,fdm
) x
->
1379 let env, taf
, (key
, value) = akshape_field
env x
in
1380 env, (taf
::tafl
, Nast.ShapeMap.add key
value fdm
)
1382 make_result env (T.Array
(List.rev tafl
))
1383 (Reason.Rwitness
p, Tarraykind
(AKshape fdm
))
1385 | Array
(x
:: rl
as l
) ->
1386 (* True if all fields are values, or all fields are key => value *)
1387 let fields_consistent = check_consistent_fields x rl
in
1388 let is_vec = match x
with
1389 | Nast.AFvalue
_ -> true
1390 | Nast.AFkvalue
_ -> false in
1391 if fields_consistent && is_vec then
1392 (* Use expected type to determine expected element type *)
1393 let env, elem_expected
=
1394 match expand_expected
env expected with
1395 | env, Some
(pos, ur
, ety
) ->
1396 begin match get_akvec_inst ety
with
1397 | Some vty
-> env, Some
(pos, ur
, vty
)
1402 let env, tel
, arraykind
=
1403 if shape_and_tuple_arrays_enabled then
1404 let env, tel
, fields
=
1405 List.foldi l ~f
:begin fun index
(env, tel
, acc
) e
->
1406 let env, te, ty = aktuple_field
env e
in
1407 env, te::tel
, IMap.add index
ty acc
1408 end ~init
:(env, [], IMap.empty
) in
1409 env, tel
, AKtuple fields
1411 let env, tel
, value_ty
=
1412 compute_exprs_and_supertype ~
expected:elem_expected
env l array_field_value
in
1413 env, tel
, AKvec value_ty
in
1415 (T.Array
(List.map tel
(fun e
-> T.AFvalue e
)))
1416 (Reason.Rwitness
p, Tarraykind arraykind
)
1419 (* TODO TAST: produce a typed expression here *)
1422 (* Use expected type to determine expected element type *)
1423 let env, vexpected
=
1424 match expand_expected
env expected with
1425 | env, Some
(pos, ur
, ety
) ->
1426 begin match get_akvec_inst ety
with
1427 | Some vty
-> env, Some
(pos, ur
, vty
)
1432 let env, _value_exprs
, value_ty
=
1433 compute_exprs_and_supertype ~
expected:vexpected
env l array_field_value
in
1434 make_result env T.Any
1435 (Reason.Rwitness
p, Tarraykind
(AKvec value_ty
))
1437 (* Use expected type to determine expected element type *)
1438 let env, kexpected
, vexpected
=
1439 match expand_expected
env expected with
1440 | env, Some
(pos, ur
, ety
) ->
1441 begin match get_akmap_inst ety
with
1442 | Some
(kty
, vty
) -> env, Some
(pos, ur
, kty
), Some
(pos, ur
, vty
)
1443 | None
-> env, None
, None
1447 let env, key_exprs
, key_ty
=
1448 compute_exprs_and_supertype ~
expected:kexpected
env l array_field_key
in
1449 let env, value_exprs
, value_ty
=
1450 compute_exprs_and_supertype ~
expected:vexpected
env l array_field_value
in
1452 (T.Array
(List.map
(List.zip_exn key_exprs value_exprs
)
1453 (fun (tek
, tev
) -> T.AFkvalue
(tek
, tev
))))
1454 (Reason.Rwitness
p, Tarraykind
(AKmap
(key_ty
, value_ty
)))
1457 (* Use expected type to determine expected key and value types *)
1458 let env, kexpected
, vexpected
=
1459 match expand_expected
env expected with
1460 | env, Some
(pos, ur
, ety
) ->
1461 begin match get_darray_inst ety
with
1462 | Some
(kty
, vty
) ->
1463 env, Some
(pos, ur
, kty
), Some
(pos, ur
, vty
)
1469 let keys, values
= List.unzip l
in
1471 let env, value_exprs
, value_ty
=
1472 compute_exprs_and_supertype ~
expected:vexpected
env values array_value
in
1473 let env, key_exprs
, key_ty
=
1474 compute_exprs_and_supertype ~
expected:kexpected
env keys array_value
in
1476 List.fold_left key_exprs ~init
:env ~f
:begin
1477 fun env ((key_pos
, key_ty
), _) ->
1478 subtype_arraykey ~class_name
:"darray" ~key_pos
env key_ty
1480 let field_exprs = List.zip_exn key_exprs value_exprs
in
1482 (T.Darray
field_exprs)
1483 (Reason.Rwitness
p, Tarraykind
(AKdarray
(key_ty
, value_ty
)))
1486 (* Use expected type to determine expected element type *)
1487 let env, elem_expected
=
1488 match expand_expected
env expected with
1489 | env, Some
(pos, ur
, ety
) ->
1490 begin match get_varray_inst ety
with
1492 env, Some
(pos, ur
, vty
)
1499 let env, value_exprs
, value_ty
=
1500 compute_exprs_and_supertype ~
expected:elem_expected
env values array_value
in
1502 (T.Varray value_exprs
)
1503 (Reason.Rwitness
p, Tarraykind
(AKvarray value_ty
))
1505 | ValCollection
(kind
, el
) ->
1506 (* Use expected type to determine expected element type *)
1507 let env, elem_expected
=
1508 match expand_expected
env expected with
1509 | env, Some
(pos, ur
, ety
) ->
1510 begin match get_vc_inst kind ety
with
1512 env, Some
(pos, ur
, vty
)
1517 let env, tel
, tyl
= exprs ?
expected:elem_expected
env el
in
1518 let env, tyl
= List.map_env
env tyl
Typing_env.unbind
in
1520 match elem_expected
with
1521 | Some
(_, _, ty) -> env, ty
1522 | None
-> Env.fresh_unresolved_type
env in
1523 let class_name = vc_kind_to_name kind
in
1524 let subtype_val env ((pos, _), ty) =
1525 let env = Type.sub_type
p Reason.URvector
env ty elem_ty
in
1526 begin match kind
with
1527 | `Set
| `ImmSet
| `Keyset
->
1528 subtype_arraykey ~
class_name ~key_pos
:pos env ty
1529 | `Vector
| `ImmVector
| `Vec
| `Pair
->
1533 List.fold_left
(List.zip_exn el tyl
) ~init
:env ~f
:subtype_val in
1534 let tvector = Tclass
((p, class_name), [elem_ty
]) in
1535 let ty = Reason.Rwitness
p, tvector in
1536 make_result env (T.ValCollection
(kind
, tel
)) ty
1537 | KeyValCollection
(kind
, l
) ->
1538 (* Use expected type to determine expected key and value types *)
1539 let env, kexpected
, vexpected
=
1540 match expand_expected
env expected with
1541 | env, Some
(pos, ur
, ety
) ->
1542 begin match get_kvc_inst kind ety
with
1543 | Some
(kty
, vty
) ->
1544 env, Some
(pos, ur
, kty
), Some
(pos, ur
, vty
)
1548 | _ -> env, None
, None
in
1549 let kl, vl
= List.unzip l
in
1550 let env, tkl
, kl = exprs ?
expected:kexpected
env kl in
1551 let env, tvl
, vl
= exprs ?
expected:vexpected
env vl
in
1552 let env, kl = List.map_env
env kl Typing_env.unbind
in
1554 match kexpected
with
1555 | Some
(_, _, k
) -> env, k
1556 | None
-> Env.fresh_unresolved_type
env in
1557 let env, vl
= List.map_env
env vl
Typing_env.unbind
in
1559 match vexpected
with
1560 | Some
(_, _, v
) -> env, v
1561 | None
-> Env.fresh_unresolved_type
env in
1562 let class_name = kvc_kind_to_name kind
in
1563 let subtype_key env (((key_pos
, _), _), ty) =
1564 let env = Type.sub_type
p Reason.URkey
env ty k
in
1565 subtype_arraykey ~
class_name ~key_pos
env ty in
1567 List.fold_left
(List.zip_exn tkl
kl) ~init
:env ~f
:subtype_key in
1568 let subtype_val env ty = Type.sub_type
p Reason.URvalue
env ty v
in
1570 List.fold_left vl ~init
:env ~f
:subtype_val in
1571 let ty = Tclass
((p, class_name), [k
; v
])
1573 make_result env (T.KeyValCollection
(kind
, List.zip_exn tkl tvl
))
1574 (Reason.Rwitness
p, ty)
1576 let env, te, ty = expr
env e
in
1577 (* Clone only works on objects; anything else fatals at runtime *)
1578 let tobj = (Reason.Rwitness
p, Tobject
) in
1579 let env = Type.sub_type
p Reason.URclone
env ty tobj in
1580 make_result env (T.Clone
te) ty
1581 | This
when Env.is_static
env ->
1582 Errors.this_in_static
p;
1583 expr_error env p (Reason.Rwitness
p)
1584 | This
when valkind = `lvalue
->
1585 Errors.this_lvalue
p;
1586 expr_error env p (Reason.Rwitness
p)
1588 let r, _ = Env.get_self
env in
1590 then Errors.this_var_outside_class
p;
1591 if not accept_using_var
1592 then check_escaping_var
env (p,this
);
1593 let (_, ty) = Env.get_local
env this
in
1594 let r = Reason.Rwitness
p in
1596 let ty = r, TUtils.this_of
ty in
1597 (* '$this' always refers to the late bound static type *)
1598 let env, new_ty
= ExprDepTy.make
env CIstatic
ty in
1599 make_result env T.This
(new_ty
)
1600 | Assert
(AE_assert e
) ->
1601 let env, te, _ = expr
env e
in
1602 let env = LEnv.save_and_merge_next_in_cont
env C.Exit
in
1603 let env = condition env true e
in
1604 make_result env (T.Assert
(T.AE_assert
te))
1605 (Reason.Rwitness
p, Tprim Tvoid
)
1607 make_result env T.True
(Reason.Rwitness
p, Tprim Tbool
)
1609 make_result env T.False
(Reason.Rwitness
p, Tprim Tbool
)
1610 (* TODO TAST: consider checking that the integer is in range. Right now
1611 * it's possible for HHVM to fail on well-typed Hack code
1614 make_result env (T.Int s
) (Reason.Rwitness
p, Tprim Tint
)
1616 make_result env (T.Float s
) (Reason.Rwitness
p, Tprim Tfloat
)
1617 (* TODO TAST: consider introducing a "null" type, and defining ?t to
1622 if TUtils.is_void_type_of_null
env
1624 else Toption
(Env.fresh_type
()) in
1625 make_result env T.Null
(Reason.Rwitness
p, ty)
1627 make_result env (T.String s
) (Reason.Rwitness
p, Tprim Tstring
)
1629 let env, tel
= string2
env idl
in
1630 make_result env (T.String2 tel
) (Reason.Rwitness
p, Tprim Tstring
)
1631 | PrefixedString
_ ->
1632 Errors.experimental_feature
p "prefixed strings";
1633 expr_error env p (Reason.Rnone
)
1635 Typing_hooks.dispatch_id_hook x
env;
1636 let env, fty
= fun_type_of_id
env x
[] in
1637 begin match fty
with
1638 | _, Tfun fty
-> check_deprecated
(fst x
) fty
;
1641 make_result env (T.Fun_id x
) fty
1642 | Id
((cst_pos
, cst_name
) as id) ->
1643 Typing_hooks.dispatch_id_hook
id env;
1644 (match Env.get_gconst
env cst_name
with
1645 | None
when Env.is_strict
env ->
1646 Errors.unbound_global cst_pos
;
1647 let ty = (Reason.Rwitness cst_pos
, Typing_utils.terr
env) in
1648 let te = T.make_typed_expr cst_pos
ty (T.Id
id) in
1651 make_result env (T.Id
id) (Reason.Rwitness cst_pos
, Typing_utils.tany
env)
1653 if cst_name
= SN.Rx.is_enabled
1654 && Env.env_reactivity
env = Nonreactive
1655 && not
(TypecheckerOptions.unsafe_rx
(Env.get_options
env))
1656 then Errors.rx_enabled_in_non_rx_context cst_pos
;
1658 Phase.localize_with_self
env ty in
1659 make_result env (T.Id
id) ty
1661 | Method_id
(instance
, meth
) ->
1662 (* Method_id is used when creating a "method pointer" using the magic
1663 * inst_meth function.
1665 * Typing this is pretty simple, we just need to check that instance->meth
1666 * is public+not static and then return its type.
1668 Typing_hooks.dispatch_fun_id_hook
(p, "\\"^
SN.SpecialFunctions.inst_meth
);
1669 let env, te, ty1 = expr
env instance
in
1670 let env, result
, vis
=
1671 obj_get_with_visibility ~is_method
:true ~nullsafe
:None ~
valkind:`other ~pos_params
:None
env
1672 ty1 (CIexpr instance
) meth
(fun x
-> x
) in
1673 let has_lost_info = Env.FakeMembers.is_invalid
env instance
(snd meth
) in
1676 let name = "the method "^snd meth
in
1677 let env, result
= Env.lost_info
name env result
in
1678 make_result env (T.Method_id
(te, meth
)) result
1682 | _, Tfun fty
-> check_deprecated
p fty
1685 | Some
(method_pos
, Vprivate
_) ->
1686 Errors.private_inst_meth method_pos
p
1687 | Some
(method_pos
, Vprotected
_) ->
1688 Errors.protected_inst_meth method_pos
p
1691 make_result env (T.Method_id
(te, meth
)) result
1693 | Method_caller
((pos, class_name) as pos_cname
, meth_name
) ->
1694 (* meth_caller('X', 'foo') desugars to:
1697 Typing_hooks.dispatch_fun_id_hook
(p, "\\"^
SN.SpecialFunctions.meth_caller
);
1698 let class_ = Env.get_class
env class_name in
1700 | None
-> unbound_name env pos_cname
1702 (* Create a class type for the given object instantiated with unresolved
1703 * types for its type parameters.
1706 List.map_env
env class_.tc_tparams
TUtils.unresolved_tparam
in
1707 let params = List.map
class_.tc_tparams
begin fun (_, (p, n
), _) ->
1708 Reason.Rwitness
p, Tgeneric n
1710 let obj_type = Reason.Rwitness
p, Tapply
(pos_cname
, params) in
1712 (Phase.env_with_self
env) with
1713 substs
= Subst.make
class_.tc_tparams tvarl
;
1715 let env, local_obj_ty
= Phase.localize ~
ety_env env obj_type in
1717 obj_get ~is_method
:true ~nullsafe
:None
env local_obj_ty
1718 (CI
((pos, class_name), [])) meth_name
(fun x
-> x
) in
1720 | reason
, Tfun fty
->
1721 check_deprecated
p fty
;
1722 (* We are creating a fake closure:
1723 * function(Class $x, arg_types_of(Class::meth_name))
1724 : return_type_of(Class::meth_name)
1727 ety_env with substs
= Subst.make
class_.tc_tparams tvarl
1730 Phase.check_tparams_constraints ~use_pos
:p ~
ety_env env class_.tc_tparams
in
1731 let env, local_obj_ty
= Phase.localize ~
ety_env env obj_type in
1732 let local_obj_fp = TUtils.default_fun_param local_obj_ty
in
1733 let fty = { fty with
1734 ft_params
= local_obj_fp :: fty.ft_params
} in
1735 let fun_arity = match fty.ft_arity
with
1736 | Fstandard
(min
, max
) -> Fstandard
(min
+ 1, max
+ 1)
1737 | Fvariadic
(min
, x
) -> Fvariadic
(min
+ 1, x
)
1738 | Fellipsis
(min
, p) -> Fellipsis
(min
+ 1, p) in
1741 ft_deprecated
= None
;
1742 ft_abstract
= false;
1743 (* propagate 'is_coroutine' from the method being called*)
1744 ft_is_coroutine
= fty.ft_is_coroutine
;
1745 ft_arity
= fun_arity;
1746 ft_tparams
= fty.ft_tparams
;
1747 ft_where_constraints
= fty.ft_where_constraints
;
1748 ft_params
= fty.ft_params
;
1749 ft_ret
= fty.ft_ret
;
1750 ft_ret_by_ref
= fty.ft_ret_by_ref
;
1751 ft_reactive
= fty.ft_reactive
;
1752 ft_mutability
= fty.ft_mutability
;
1753 ft_returns_mutable
= fty.ft_returns_mutable
;
1754 ft_return_disposable
= fty.ft_return_disposable
;
1755 ft_decl_errors
= None
;
1756 ft_returns_void_to_rx
= fty.ft_returns_void_to_rx
;
1758 make_result env (T.Method_caller
(pos_cname
, meth_name
))
1759 (reason
, Tfun
caller)
1761 (* This can happen if the method lives in PHP *)
1762 make_result env (T.Method_caller
(pos_cname
, meth_name
))
1763 (Reason.Rwitness
pos, Typing_utils.tany
env)
1766 | Smethod_id
(c, meth
) ->
1767 (* Smethod_id is used when creating a "method pointer" using the magic
1768 * class_meth function.
1770 * Typing this is pretty simple, we just need to check that c::meth is
1771 * public+static and then return its type.
1773 Typing_hooks.dispatch_fun_id_hook
(p, "\\"^
SN.SpecialFunctions.class_meth
);
1774 let class_ = Env.get_class
env (snd
c) in
1777 (* The class given as a static string was not found. *)
1780 let smethod = Env.get_static_member
true env class_ (snd meth
) in
1782 | None
-> (* The static method wasn't found. *)
1783 smember_not_found
p ~is_const
:false ~is_method
:true class_ (snd meth
);
1784 expr_error env p Reason.Rnone
1785 | Some
{ ce_type
= lazy ty; ce_visibility
; _ } ->
1786 let cid = CI
(c, []) in
1787 let env, _te
, cid_ty
= static_class_id ~check_constraints
:true (fst
c) env cid in
1790 | (_, Tclass
(_, tyargs)) -> tyargs
1793 type_expansions
= [];
1794 substs
= Subst.make
class_.tc_tparams
tyargs;
1796 from_class
= Some
cid;
1797 validate_dty
= None
;
1802 let env, ft
= Phase.localize_ft ~use_pos
:p ~
ety_env env ft
in
1803 let ty = r, Tfun ft
in
1804 check_deprecated
p ft
;
1805 match ce_visibility
with
1807 make_result env (T.Smethod_id
(c, meth
)) ty
1809 Errors.private_class_meth
(Reason.to_pos
r) p;
1812 Errors.protected_class_meth
(Reason.to_pos
r) p;
1816 Errors.internal_error
p "We have a method which isn't callable";
1821 let r = Reason.Rplaceholder
p in
1822 let ty = r, Tprim Tvoid
in
1823 make_result env (T.Lplaceholder
p) ty
1824 | Dollardollar
_ when valkind = `lvalue
->
1825 Errors.dollardollar_lvalue
p;
1826 expr_error env p (Reason.Rwitness
p)
1827 | Dollardollar
((_, x
) as id) ->
1828 let ty = Env.get_local
env x
in
1829 let env = save_and_merge_next_in_catch
env in
1830 make_result env (T.Dollardollar
id) ty
1831 | Lvar
((_, x
) as id) ->
1832 let local_id = Local_id.to_string x
in
1833 if SN.Superglobals.is_superglobal
local_id
1834 then Env.error_if_reactive_context
env @@ begin fun () ->
1835 Errors.superglobal_in_reactive_context
p local_id;
1837 if not accept_using_var
1838 then check_escaping_var
env id;
1839 let ty = Env.get_local
env x
in
1840 make_result env (T.Lvar
id) ty
1841 | ImmutableVar
((_, x
) as id) ->
1842 let ty = Env.get_local
env x
in
1843 make_result env (T.ImmutableVar
id) ty
1845 let env, te, _ty
= expr
env e
in
1846 (** Can't easily track any typing information for variable variable. *)
1847 make_result env (T.Dollar
te) (Reason.Rwitness
p, Typing_utils.tany
env)
1849 let env, expected = expand_expected
env expected in
1852 | Some
(pos, ur
, (_, Ttuple expected_tyl
)) ->
1853 exprs_expected
(pos, ur
, expected_tyl
) env el
1857 (* TODO TAST: figure out role of unbind here *)
1858 let env, tyl
= List.map_env
env tyl
Typing_env.unbind
in
1859 let env, tyl
= List.map_env
env tyl
TUtils.unresolved
in
1860 let ty = Reason.Rwitness
p, Ttuple tyl
in
1861 make_result env (T.List tel
) ty
1863 (* Use expected type to determine expected element types *)
1864 let env, expected1
, expected2
=
1865 match expand_expected
env expected with
1866 | env, Some
(pos, ur
, (_, Tclass
((_, k
), [ty1; ty2
]))) when k
= SN.Collections.cPair
->
1867 env, Some
(pos, ur
, ty1), Some
(pos, ur
, ty2
)
1868 | _ -> env, None
, None
in
1869 let env, te1
, ty1 = expr ?
expected:expected1
env e1
in
1870 let env, ty1 = Typing_env.unbind
env ty1 in
1871 let env, ty1 = TUtils.unresolved
env ty1 in
1872 let env, te2
, ty2
= expr ?
expected:expected2
env e2
in
1873 let env, ty2
= Typing_env.unbind
env ty2
in
1874 let env, ty2
= TUtils.unresolved
env ty2
in
1876 Reason.Rwitness
p, Tclass
((p, SN.Collections.cPair
), [ty1; ty2
]) in
1877 make_result env (T.Pair
(te1
, te2
)) ty
1879 (* TODO: use expected type to determine tuple component types *)
1880 let env, tel
, tyl
= exprs env el
in
1881 let ty = Reason.Rwitness
p, Ttuple tyl
in
1882 make_result env (T.Expr_list tel
) ty
1883 | Array_get
(e
, None
) ->
1884 let env, te1
, ty1 = update_array_type
p env e None
valkind in
1885 let env = save_and_merge_next_in_catch
env in
1886 let env, ty = array_append
p env ty1 in
1887 make_result env (T.Array_get
(te1
, None
)) ty
1888 | Array_get
(e1
, Some e2
) ->
1890 update_array_type ?lhs_of_null_coalesce
p env e1
(Some e2
) valkind in
1891 let env, ty1 = TUtils.fold_unresolved
env ty1 in
1892 let env, te2
, ty2
= expr
env e2
in
1893 let env = save_and_merge_next_in_catch
env in
1894 let is_lvalue = (valkind == `lvalue
) in
1896 array_get ?lhs_of_null_coalesce
is_lvalue p env ty1 e2 ty2
in
1897 make_result env (T.Array_get
(te1
, Some te2
)) ty
1898 | Call
(Cnormal
, (pos_id
, Id
((_, s
) as id)), hl
, el
, [])
1899 when is_pseudo_function s
->
1900 let env, tel
, tys
= exprs ~accept_using_var
:true env el
in
1901 if s
= SN.PseudoFunctions.hh_show
1902 then List.iter tys
(Typing_log.hh_show
p env)
1904 if s
= SN.PseudoFunctions.hh_show_env
1905 then Typing_log.hh_show_env
p env
1907 if s
= SN.PseudoFunctions.hh_log_level
1909 | [(_, Int level_str
)] ->
1910 Typing_log.hh_log_level
(int_of_string level_str
)
1913 if s
= SN.PseudoFunctions.hh_loop_forever
then loop_forever
env
1918 T.make_typed_expr pos_id
(Reason.Rnone
, TUtils.tany
env) (T.Id
id),
1921 [])) (Env.fresh_type
())
1922 | Call
(call_type
, e
, hl
, el
, uel
) ->
1923 let env = save_and_merge_next_in_catch
env in
1924 let env, te, ty = check_call ~is_using_clause ~
expected ~is_expr_statement
1925 env p call_type e hl el uel ~in_suspend
:false in
1926 Typing_mutability.enforce_mutable_call
env te;
1928 | Binop
(Ast.QuestionQuestion
, e1
, e2
) ->
1929 eif
env ~
expected ~coalesce
:true ~in_cond
p e1 None e2
1930 (* For example, e1 += e2. This is typed and translated as if
1931 * written e1 = e1 + e2.
1932 * TODO TAST: is this right? e1 will get evaluated more than once
1934 | Binop
(Ast.Eq
(Some op
), e1
, e2
) ->
1935 if op
= Ast.QuestionQuestion
&& not
null_coalesce_assignment_enabled
1937 Errors.experimental_feature
p "null coalesce assignment operator";
1938 expr_error env p (Reason.Rnone
)
1940 let e_fake = (p, Binop
(Ast.Eq None
, e1
, (p, Binop
(op
, e1
, e2
)))) in
1941 let env, te_fake
, ty = raw_expr in_cond
env e_fake in
1942 begin match snd te_fake
with
1943 | T.Binop
(_, te1
, (_, T.Binop
(_, _, te2
))) ->
1944 let te = T.Binop
(Ast.Eq
(Some op
), te1
, te2
) in
1945 make_result env te ty
1948 | Binop
(Ast.Eq None
, e1
, e2
) ->
1949 let forbid_uref = match e1
, e2
with
1950 | (_, Array_get
_), (_, Unop
(Ast.Uref
, _))
1951 | _, (_, Unop
(Ast.Uref
, (_, Array_get
_))) -> true
1954 | _, ImmutableVar
(p, x
) ->
1955 Errors.let_var_immutability_violation
p (Local_id.get_name x
)
1958 let env, te2
, ty2
= raw_expr ~in_cond ~
forbid_uref env e2
in
1959 let env, te1
, ty = assign
p env e1 ty2
in
1961 if Env.env_local_reactive
env then
1962 Typing_mutability.handle_assignment_mutability
env te1 te2
1965 (* If we are assigning a local variable to another local variable then
1966 * the expression ID associated with e2 is transferred to e1
1969 | (_, Lvar
(_, x1
)), (_, ImmutableVar
(_, x2
))
1970 | (_, Lvar
(_, x1
)), (_, Lvar
(_, x2
)) ->
1971 let eid2 = Env.get_local_expr_id
env x2
in
1975 ~f
:(Env.set_local_expr_id
env x1
) in
1976 make_result env (T.Binop
(Ast.Eq None
, te1
, te2
)) ty
1978 make_result env (T.Binop
(Ast.Eq None
, te1
, te2
)) ty
1980 | Binop
((Ast.AMpamp
| Ast.BArbar
as bop
), e1
, e2
) ->
1981 let c = bop
= Ast.AMpamp
in
1982 let lenv = env.Env.lenv in
1983 let env, te1
, _ = expr
env e1
in
1984 let env = condition env c e1
in
1985 let env, te2
, _ = raw_expr in_cond
env e2
in
1986 let env = { env with Env.lenv = lenv } in
1987 make_result env (T.Binop
(bop
, te1
, te2
))
1988 (Reason.Rlogic_ret
p, Tprim Tbool
)
1989 | Binop
(bop
, e1
, e2
) when Env.is_strict
env
1990 && (snd e1
= Nast.Null
|| snd e2
= Nast.Null
)
1991 && (bop
= Ast.EQeqeq
|| bop
= Ast.Diff2
) ->
1992 let e, ne
= if snd e2
= Nast.Null
then e1
, e2
else e2
, e1
in
1993 let _, te, ty = raw_expr in_cond
env e in
1994 let tne = T.make_typed_expr
(fst ne
) ty T.Null
in
1995 let te1, te2
= if snd e2
= Nast.Null
then te, tne else tne, te in
1996 make_result env (T.Binop
(bop
, te1, te2
))
1997 (Reason.Rcomp
p, Tprim Tbool
)
1998 | Binop
(bop
, e1
, e2
) ->
1999 let env, te1, ty1 = raw_expr in_cond
env e1
in
2000 let env, te2
, ty2
= raw_expr in_cond
env e2
in
2001 let env = save_and_merge_next_in_catch
env in
2003 binop in_cond
p env bop
(fst e1
) te1 ty1 (fst e2
) te2 ty2
in
2005 | Pipe
(e0
, e1
, e2
) ->
2006 let env, te1, ty = expr
env e1
in
2007 (** id is the ID of the $$ that is implicitly declared by the pipe.
2008 * Set the local type for the $$ in the RHS. *)
2009 let env = set_local
env e0
ty in
2010 (* do not error on awaitable being returned from RHS *)
2012 expr
env ~allow_non_awaited_awaitable_in_rx
:true e2
in
2014 * Return ty2 since the type of the pipe expression is the type of the
2017 * Note: env does have the type of this Pipe's $$, but it doesn't
2018 * override the outer one since they have different ID's.
2021 * a() |> ( inner1($$) |> inner2($$) ) + $$
2023 * The rightmost $$ refers to the result of a()
2025 make_result env (T.Pipe
(e0
, te1, te2
)) ty2
2027 let env, te, ty = raw_expr in_cond
env e in
2028 let env = save_and_merge_next_in_catch
env in
2029 unop ~is_func_arg ~
forbid_uref p env uop
te ty
2030 | Eif
(c, e1
, e2
) -> eif
env ~
expected ~coalesce
:false ~in_cond
p c e1 e2
2032 begin match Env.get_typedef
env (snd sid
) with
2033 | Some
{td_tparams
= tparaml
; _} ->
2034 (* Typedef type parameters cannot have constraints *)
2035 let params = List.map ~f
:begin fun (_, (p, x
), _) ->
2036 Reason.Rwitness
p, Tgeneric x
2038 let tdef = Reason.Rwitness
(fst sid
), Tapply
(sid
, params) in
2040 Reason.Rwitness
p, Tapply
((p, SN.Classes.cTypename
), [tdef]) in
2041 let env, tparams
= List.map_env
env tparaml
begin fun env _ ->
2042 Env.fresh_unresolved_type
env
2044 let ety_env = { (Phase.env_with_self
env) with
2045 substs
= Subst.make tparaml tparams
} in
2046 let env = Phase.check_tparams_constraints ~use_pos
:p ~
ety_env env tparaml
in
2047 let env, ty = Phase.localize ~
ety_env env typename in
2048 make_result env (T.Typename sid
) ty
2050 (* Should never hit this case since we only construct this AST node
2051 * if in the expression Foo::class, Foo is a type def.
2053 expr_error env p (Reason.Rwitness
p)
2055 | Class_const
(cid, mid
) -> class_const
env p (cid, mid
)
2056 | Class_get
(((), x
), (py
, y
))
2057 when Env.FakeMembers.get_static
env x y
<> None
->
2058 Env.error_if_reactive_context
env @@ begin fun () ->
2059 Errors.static_property_in_reactive_context
p
2061 let env, local
= Env.FakeMembers.make_static
p env x y
in
2062 let local = p, Lvar
(p, local) in
2063 let env, _, ty = expr
env local in
2064 let env, te, _ = static_class_id ~check_constraints
:false p env x
in
2065 make_result env (T.Class_get
(te, (py
, y
))) ty
2066 | Class_get
(((), cid), mid
) ->
2067 Env.error_if_reactive_context
env @@ begin fun () ->
2068 Errors.static_property_in_reactive_context
p
2070 let env, te, cty
= static_class_id ~check_constraints
:false p env cid in
2071 let env = save_and_merge_next_in_catch
env in
2073 class_get ~is_method
:false ~is_const
:false env cty mid
cid in
2074 if Env.FakeMembers.is_static_invalid
env cid (snd mid
)
2076 let fake_name = Env.FakeMembers.make_static_id
cid (snd mid
) in
2077 let env, ty = Env.lost_info
fake_name env ty in
2078 make_result env (T.Class_get
(te, mid
)) ty
2080 make_result env (T.Class_get
(te, mid
)) ty
2081 (* Fake member property access. For example:
2082 * if ($x->f !== null) { ...$x->f... }
2084 | Obj_get
(e, (pid
, Id
(py
, y
)), nf
)
2085 when Env.FakeMembers.get
env e y
<> None
->
2086 let env = save_and_merge_next_in_catch
env in
2087 let env, local = Env.FakeMembers.make
p env e y
in
2088 let local = p, Lvar
(p, local) in
2089 let env, _, ty = expr
env local in
2090 let env, t_lhs
, _ = expr ~accept_using_var
:true env e in
2091 let t_rhs = T.make_typed_expr pid
ty (T.Id
(py
, y
)) in
2092 make_result env (T.Obj_get
(t_lhs
, t_rhs, nf
)) ty
2093 (* Statically-known instance property access e.g. $x->f *)
2094 | Obj_get
(e1
, (pm
, Id m
), nullflavor
) ->
2096 (match nullflavor
with
2097 | OG_nullthrows
-> None
2098 | OG_nullsafe
-> Some
p
2100 let env, te1, ty1 = expr ~accept_using_var
:true env e1
in
2101 let env = save_and_merge_next_in_catch
env in
2103 obj_get ~is_method
:false ~
nullsafe ~
valkind env ty1 (CIexpr e1
) m
(fun x
-> x
) in
2104 let has_lost_info = Env.FakeMembers.is_invalid
env e1
(snd m
) in
2108 let name = "the member " ^ snd m
in
2109 Env.lost_info
name env result
2113 make_result env (T.Obj_get
(te1,
2114 T.make_typed_expr pm result
(T.Id m
), nullflavor
)) result
2115 (* Dynamic instance property access e.g. $x->$f *)
2116 | Obj_get
(e1
, e2
, nullflavor
) ->
2117 let env, te1, ty1 = expr ~accept_using_var
:true env e1
in
2118 let env, te2
, _ = expr
env e2
in
2119 let ty = if TUtils.is_dynamic
env ty1 then
2120 (Reason.Rwitness
p, Tdynamic
) else
2122 if Env.is_strict
env then
2124 Errors.dynamic_method_call
(fst e2
);
2125 (Reason.Rwitness
p, Typing_utils.terr
env)
2128 (Reason.Rwitness
p, Typing_utils.tany
env)
2130 let (pos, _), te2
= te2
in
2131 let env = save_and_merge_next_in_catch
env in
2132 let te2 = T.make_typed_expr
pos ty te2 in
2133 make_result env (T.Obj_get
(te1, te2, nullflavor
)) ty
2135 make_result env T.Yield_break
(Reason.Rwitness
p, Typing_utils.tany
env)
2137 let env, (taf
, opt_key
, value) = array_field
env af
in
2138 let send = Env.fresh_type
() in
2139 let env, key
= match af
, opt_key
with
2140 | Nast.AFvalue
(p, _), None
->
2142 match Env.get_fn_kind
env with
2146 Errors.internal_error
p "yield found in non-generator";
2147 Reason.Rwitness
p, Typing_utils.tany
env
2149 (Reason.Rwitness
p, Tprim Tint
)
2150 | Ast.FAsyncGenerator
->
2151 (Reason.Ryield_asyncnull
p,
2152 Toption
(Env.fresh_type
()))
2157 | _, _ -> assert false in
2158 let rty = match Env.get_fn_kind
env with
2160 (* yield in coroutine is already reported as error in NastCheck *)
2161 let _, _, ty = expr_error env p (Reason.Rwitness
p) in
2164 Reason.Ryield_gen
p,
2165 Tclass
((p, SN.Classes.cGenerator
), [key
; value; send])
2166 | Ast.FAsyncGenerator
->
2167 Reason.Ryield_asyncgen
p,
2168 Tclass
((p, SN.Classes.cAsyncGenerator
), [key
; value; send])
2169 | Ast.FSync
| Ast.FAsync
->
2170 failwith
"Parsing should never allow this" in
2171 let Typing_env_return_info.{ return_type
= expected_return
; _ } = Env.get_return
env in
2173 Type.sub_type
p (Reason.URyield
) env rty expected_return
in
2174 let env = Env.forget_members
env p in
2175 let env = LEnv.save_and_merge_next_in_cont
env C.Exit
in
2176 make_result env (T.Yield taf
) (Reason.Ryield_send
p, Toption
send)
2178 let key = Env.fresh_type
() in
2179 let value = Env.fresh_type
() in
2180 (* Expected type of `e` in `yield from e` is KeyedTraversable<Tk,Tv> *)
2181 let expected_yield_from_ty =
2182 (Reason.Ryield_gen
p,
2183 Tclass
((p, SN.Collections.cKeyedTraversable
), [key; value])) in
2184 let env, te, yield_from_ty
=
2185 expr ~is_using_clause ~is_expr_statement
env e in
2187 Type.sub_type
p Reason.URyield_from
env yield_from_ty
expected_yield_from_ty in
2188 let rty = match Env.get_fn_kind
env with
2190 (* yield in coroutine is already reported as error in NastCheck *)
2191 let _, _, ty = expr_error env p (Reason.Rwitness
p) in
2194 Reason.Ryield_gen
p,
2195 Tclass
((p, SN.Classes.cGenerator
), [key; value; (Reason.Rwitness
p, Tprim Tvoid
)])
2196 | Ast.FSync
| Ast.FAsync
| Ast.FAsyncGenerator
->
2197 failwith
"Parsing should never allow this" in
2198 let Typing_env_return_info.{ return_type
= expected_return
; _ } = Env.get_return
env in
2200 Type.sub_type
p (Reason.URyield_from
) env rty expected_return
in
2201 let env = Env.forget_members
env p in
2202 make_result env (T.Yield_from
te) (Reason.Rwitness
p, Tprim Tvoid
)
2204 (* Await is permitted in a using clause e.g. using (await make_handle()) *)
2206 expr ~is_using_clause ~is_expr_statement
2207 ~allow_non_awaited_awaitable_in_rx
:true env e in
2208 let env, ty = Async.overload_extract_from_awaitable
env p rty in
2209 make_result env (T.Await
te) ty
2213 | _, Call
(call_type
, e, hl
, el
, uel
) ->
2214 check_call ~is_using_clause ~
expected ~is_expr_statement
2215 env p call_type
e hl el uel ~in_suspend
:true
2217 let env, te, (r, ty) = expr
env e in
2218 (* not a call - report an error *)
2219 Errors.non_call_argument_in_suspend
2221 (Reason.to_string
("This is " ^
Typing_print.error
ty) r);
2223 make_result env (T.Suspend
te) ty
2225 | Special_func func
-> special_func
env p func
2226 | New
(((), c), el
, uel
) ->
2227 Typing_hooks.dispatch_new_id_hook
c env p;
2228 let env = save_and_merge_next_in_catch
env in
2229 let env, tc, tel
, tuel
, ty, _ =
2230 new_object ~
expected ~is_using_clause ~check_parent
:false ~check_not_abstract
:true
2232 let env = Env.forget_members
env p in
2233 make_result env (T.New
(tc, tel
, tuel
)) ty
2234 | Cast
((_, Harray
(None
, None
)), _)
2235 when Env.is_strict
env
2236 || TCO.migration_flag_enabled
(Env.get_tcopt
env) "array_cast" ->
2237 Errors.array_cast
p;
2238 expr_error env p (Reason.Rwitness
p)
2240 let env, te, ty2
= expr
env e in
2241 let env = save_and_merge_next_in_catch
env in
2242 Async.enforce_not_awaitable
env (fst
e) ty2
;
2243 if (TypecheckerOptions.experimental_feature_enabled
2244 (Env.get_options
env)
2245 TypecheckerOptions.experimental_forbid_nullable_cast
)
2246 && TUtils.is_option_non_mixed
env ty2
2248 let (r, ty2
) = ty2
in
2249 Errors.nullable_cast
p (Typing_print.error ty2
) (Reason.to_pos
r)
2251 let env, ty = Phase.hint_locl
env hint
in
2252 make_result env (T.Cast
(hint
, te)) ty
2253 | InstanceOf
(e, ((), cid)) ->
2254 let env, te, _ = expr
env e in
2255 let env, te2, _class
= instantiable_cid
p env cid in
2256 make_result env (T.InstanceOf
(te, te2)) (Reason.Rwitness
p, Tprim Tbool
)
2258 let env, te, _ = expr
env e in
2259 make_result env (T.Is
(te, hint
)) (Reason.Rwitness
p, Tprim Tbool
)
2260 | As
(e, hint
, is_nullable
) ->
2261 let env, te, expr_ty
= expr
env e in
2262 let env, hint_ty = Phase.hint_locl
env hint
in
2264 if not is_nullable
then hint_ty else
2266 (* Dont create ??hint *)
2267 | _ , Toption
_ -> hint_ty
2268 | _ -> Reason.Rwitness
p, Toption
(hint_ty) in
2269 let refine_type env lpos lty
rty =
2270 let reason = Reason.Ras lpos
in
2271 let env, rty = Env.expand_type
env rty in
2272 if snd
rty <> Tdynamic
&& SubType.is_sub_type
env lty
rty
2274 else safely_refine_type
env p reason lpos lty
rty
2277 if is_instance_var
e
2279 let env, _, ivar_ty
= raw_expr ~in_cond
:false env e in
2280 let env, ((ivar_pos
, _) as ivar
) = get_instance_var
env e in
2281 let env, hint_ty = refine_type env ivar_pos ivar_ty
hint_ty in
2282 let env = set_local
env ivar
hint_ty in
2285 refine_type env (fst
e) expr_ty
hint_ty
2287 make_result env (T.As
(te, hint
, is_nullable
)) hint_ty
2289 (* This is the function type as declared on the lambda itself.
2290 * If type hints are absent then use Tany instead. *)
2291 let declared_ft = Decl.fun_decl_in_env
env.Env.decl_env f
in
2292 (* When creating a closure, the 'this' type will mean the late bound type
2293 * of the current enclosing class
2296 { (Phase.env_with_self
env) with from_class
= Some CIstatic
} in
2297 let env, declared_ft = Phase.localize_ft ~use_pos
:p ~
ety_env env declared_ft in
2298 List.iter idl
(check_escaping_var
env);
2299 (* Ensure lambda arity is not Fellipsis in strict mode *)
2300 begin match declared_ft.ft_arity
with
2301 | Fellipsis
_ when Env.is_strict
env ->
2302 Errors.ellipsis_strict_mode ~require
:`Param_name
p
2305 (* Is the return type declared? *)
2306 let is_explicit_ret = Option.is_some f
.f_ret
in
2307 let reactivity = fun_reactivity env.Env.decl_env f
.f_user_attributes
in
2308 let check_body_under_known_params ?ret_ty ft
=
2309 let old_reactivity = Env.env_reactivity
env in
2310 let env = Env.set_env_reactive
env reactivity in
2311 let (is_coroutine
, _counter
, _, anon
) = anon_make
env p f ft idl
in
2312 let ft = { ft with ft_is_coroutine
= is_coroutine
; ft_reactive
= reactivity } in
2313 let (env, tefun
, ty) = anon ?ret_ty
env ft.ft_params
ft.ft_arity
in
2314 let env = Env.set_env_reactive
env old_reactivity in
2317 then (Reason.Rwitness
p, Tfun
{ ft with ft_ret
= declared_ft.ft_ret
})
2318 else (Reason.Rwitness
p, Tfun
{ ft with ft_ret
= ty }) in
2319 Typing_log.log_types
1 p env
2321 ("Typing.check_body_under_known_params",
2322 [Typing_log.Log_type
("ft", (Reason.Rwitness
p, Tfun
ft));
2323 Typing_log.Log_type
("inferred_ty", inferred_ty)])];
2324 env, tefun
, inferred_ty in
2325 let env, eexpected
= expand_expected
env expected in
2326 begin match eexpected
with
2327 | Some
(_pos
, _ur
, (_, Tfun expected_ft
)) ->
2328 (* First check that arities match up *)
2329 check_lambda_arity
p expected_ft
.ft_pos
declared_ft.ft_arity expected_ft
.ft_arity
;
2330 (* Use declared types for parameters in preference to those determined
2331 * by the context: they might be more general. *)
2332 let rec replace_non_declared_types params declared_ft_params expected_ft_params
=
2333 match params, declared_ft_params
, expected_ft_params
with
2334 | param
::params, declared_ft_param
::declared_ft_params
,
2335 expected_ft_param
::expected_ft_params
->
2336 let rest = replace_non_declared_types params declared_ft_params expected_ft_params
in
2337 let resolved_ft_param = if Option.is_some param
.param_hint
2338 then declared_ft_param
2339 else { declared_ft_param
with fp_type
= expected_ft_param
.fp_type
} in
2340 resolved_ft_param :: rest
2342 (* This means the expected_ft params list can have more parameters
2343 * than declared parameters in the lambda. For variadics, this is OK,
2344 * for non-variadics, this will be caught elsewhere in arity checks.
2348 let replace_non_declared_arity variadic declared_arity expected_arity
=
2350 | FVvariadicArg
{param_hint
= Some
(_); _} -> declared_arity
2351 | FVvariadicArg
_ ->
2353 match declared_arity
, expected_arity
with
2354 | Fvariadic
(min_arity
, declared
), Fvariadic
(_, expected) ->
2355 Fvariadic
(min_arity
, { declared
with fp_type
= expected.fp_type
})
2356 | _, _ -> declared_arity
2358 | _ -> declared_arity
2360 let expected_ft = { expected_ft with ft_arity
=
2361 replace_non_declared_arity
2362 f
.f_variadic
declared_ft.ft_arity
expected_ft.ft_arity
} in
2363 let expected_ft = { expected_ft with ft_params
=
2364 replace_non_declared_types f
.f_params
declared_ft.ft_params
expected_ft.ft_params
} in
2365 (* Don't bother passing in `void` if there is no explicit return *)
2367 match expected_ft.ft_ret
with
2368 | _, Tprim Tvoid
when not
is_explicit_ret -> None
2369 | _ -> Some
expected_ft.ft_ret
in
2370 Typing_log.increment_feature_count
env "Lambda [contextual params]";
2371 check_body_under_known_params ?
ret_ty expected_ft
2373 let explicit_variadic_param_or_non_variadic =
2374 begin match f
.f_variadic
with
2375 | FVvariadicArg
{param_hint
; _} -> Option.is_some param_hint
2376 | FVellipsis
_ -> false
2380 (* If all parameters are annotated with explicit types, then type-check
2381 * the body under those assumptions and pick up the result type *)
2382 let all_explicit_params =
2383 List.for_all f
.f_params
(fun param
-> Option.is_some param
.param_hint
) in
2384 if all_explicit_params && explicit_variadic_param_or_non_variadic
2386 Typing_log.increment_feature_count
env
2387 (if List.is_empty f
.f_params
then "Lambda [no params]" else "Lambda [explicit params]");
2388 check_body_under_known_params declared_ft
2392 | Some
(_, _, (_, Tany
)) ->
2393 (* If the expected type is Tany env then we're passing a lambda to an untyped
2394 * function and we just assume every parameter has type Tany env *)
2395 Typing_log.increment_feature_count
env "Lambda [untyped context]";
2396 check_body_under_known_params declared_ft
2398 (* If the expected type is something concrete but not a function
2399 * then we should reject in strict mode. Check body anyway *)
2400 if Env.is_strict
env
2401 then Errors.untyped_lambda_strict_mode
p;
2402 Typing_log.increment_feature_count
env "Lambda [non-function typed context]";
2403 check_body_under_known_params declared_ft
2405 Typing_log.increment_feature_count
env "Lambda [unknown params]";
2406 Typing_log.log_types
1 p env
2408 ("Typing.expr Efun unknown params",
2409 [Typing_log.Log_type
("declared_ft", (Reason.Rwitness
p, Tfun
declared_ft))])];
2410 (* check for recursive function calls *)
2411 let reactivity = fun_reactivity env.Env.decl_env f
.f_user_attributes
in
2412 let old_reactivity = Env.env_reactivity
env in
2413 let env = Env.set_env_reactive
env reactivity in
2414 let is_coroutine, counter
, pos, anon
= anon_make
env p f
declared_ft idl
in
2415 let env, tefun
, _, anon_id
= Errors.try_with_error
2417 let (_, tefun
, ty) = anon
env declared_ft.ft_params
declared_ft.ft_arity
in
2418 let anon_fun = reactivity, is_coroutine, counter
, pos, anon
in
2419 let env, anon_id
= Env.add_anonymous
env anon_fun in
2420 env, tefun
, ty, anon_id
)
2422 (* If the anonymous function declaration has errors itself, silence
2423 them in any subsequent usages. *)
2424 let anon_ign ?el
:_ ?
ret_ty:_ env fun_params
=
2425 Errors.ignore_
(fun () -> (anon
env fun_params
)) in
2426 let (_, tefun
, ty) = anon_ign env declared_ft.ft_params
declared_ft.ft_arity
in
2427 let anon_fun = reactivity, is_coroutine, counter
, pos, anon
in
2428 let env, anon_id
= Env.add_anonymous
env anon_fun in
2429 env, tefun
, ty, anon_id
) in
2430 let env = Env.set_env_reactive
env old_reactivity in
2431 env, tefun
, (Reason.Rwitness
p, Tanon
(declared_ft.ft_arity
, anon_id
))
2434 | Xml
(sid
, attrl
, el
) ->
2435 let cid = CI
(sid
, []) in
2436 let env, _te
, classes
= class_id_for_new
p env cid in
2437 let class_info = match classes
with
2439 (* OK to ignore rest of list; class_info only used for errors, and
2440 * cid = CI sid cannot produce a union of classes anyhow *)
2441 | (_, class_info, _)::_ -> Some
class_info
2443 let env, _te
, obj
= expr
env (fst sid
, New
(((), cid), [], [])) in
2444 let env, typed_attrs
, attr_types
= xhp_attribute_exprs
env class_info attrl
in
2445 let env, tel
= List.fold_left el ~init
:(env, []) ~f
:fold_xhp_body_elements
in
2446 let txml = T.Xml
(sid
, typed_attrs
, List.rev tel
) in
2447 (match class_info with
2448 | None
-> make_result env txml (Reason.Runknown_class
p, Tobject
)
2449 | Some
class_info ->
2450 let env = List.fold_left attr_types ~f
:begin fun env attr
->
2451 let namepstr, valpty
= attr
in
2452 let valp, valty
= valpty
in
2454 obj_get ~is_method
:false ~
nullsafe:None
env obj
cid
2455 namepstr (fun x
-> x
) in
2456 let ureason = Reason.URxhp
(class_info.tc_name
, snd
namepstr) in
2457 Type.sub_type
valp ureason env valty declty
2459 make_result env txml obj
2461 | Callconv
(kind
, e) ->
2462 let env, te, ty = expr
env e in
2463 let rec check_types env = function
2465 | _, T.Array_get
(((_, ty1), _) as te1, Some
_) ->
2466 let rec iter = function
2468 | _, (Tarraykind
_ | Ttuple
_ | Tshape
_) -> true
2469 | _, Tclass
((_, cn
), _)
2470 when cn
= SN.Collections.cDict
2471 || cn
= SN.Collections.cKeyset
2472 || cn
= SN.Collections.cVec
-> true
2473 | _, Tunresolved tyl
-> List.for_all ~f
:iter tyl
2475 let env, ety1
= Env.expand_type
env ty1 in
2476 if iter ety1
then check_types env te1 else begin
2477 let ty_str = Typing_print.error
(snd ety1
) in
2478 let msgl = Reason.to_string
("This is " ^
ty_str) (fst ety1
) in
2479 Errors.inout_argument_bad_type
(fst
e) msgl
2481 (* Other invalid expressions are caught in NastCheck. *)
2485 make_result env (T.Callconv
(kind
, te)) ty
2486 (* TODO TAST: change AST so that order of shape expressions is preserved.
2487 * At present, evaluation order is unspecified in TAST *)
2489 let env, fdm_with_expected
=
2490 match expand_expected
env expected with
2491 | env, Some
(pos, ur
, (_, Tshape
(_, expected_fdm
))) ->
2495 match ShapeMap.get k expected_fdm
with
2497 | Some sft
-> (v
, Some
(pos, ur
, sft
.sft_ty
))) fdm
in
2500 env, ShapeMap.map
(fun v
-> (v
, None
)) fdm
in
2502 (* allow_inter adds a type-variable *)
2505 (fun env _key
(e, expected) ->
2506 let env, te, ty = expr ?
expected env e in env, (te,ty))
2507 env fdm_with_expected
in
2509 let convert_expr_and_type_to_shape_field_type env _key
(_, ty) =
2510 let env, sft_ty
= TUtils.unresolved
env ty in
2511 (* An expression evaluation always corresponds to a shape_field_type
2512 with sft_optional = false. *)
2513 env, { sft_optional
= false; sft_ty
} in
2514 ShapeMap.map_env
convert_expr_and_type_to_shape_field_type env tfdm
in
2515 let env = check_shape_keys_validity
env p (ShapeMap.keys fdm
) in
2516 (* Fields are fully known, because this shape is constructed
2517 * using shape keyword and we know exactly what fields are set. *)
2518 make_result env (T.Shape
(ShapeMap.map
(fun (te,_) -> te) tfdm
))
2519 (Reason.Rwitness
p, Tshape
(FieldsFullyKnown
, fdm
))
2521 and class_const ?
(incl_tc
=false) env p (((), cid), mid
) =
2522 let env, ce
, cty
= static_class_id ~check_constraints
:false p env cid in
2523 let env, const_ty
, cc_abstract_info
=
2524 class_get ~is_method
:false ~is_const
:true ~incl_tc
env cty mid
cid in
2525 match cc_abstract_info
with
2526 | Some
(cc_pos
, cc_name
) ->
2527 let () = match cid with
2528 | CIstatic
| CIexpr
_ -> ();
2529 | _ -> Errors.abstract_const_usage
p cc_pos cc_name
; ()
2530 in env, T.make_typed_expr
p const_ty
(T.Class_const
(ce
, mid
)), const_ty
2532 env, T.make_typed_expr
p const_ty
(T.Class_const
(ce
, mid
)), const_ty
2534 and anon_sub_type
pos ur
env ty_sub ty_super
=
2535 Errors.try_add_err
pos (Reason.string_of_ureason ur
)
2536 (fun () -> SubType.sub_type
env ty_sub ty_super
)
2539 (*****************************************************************************)
2540 (* XHP attribute/body helpers. *)
2541 (*****************************************************************************)
2543 * Process a spread operator by computing the intersection of XHP attributes
2544 * between the spread expression and the XHP constructor onto which we're
2547 and xhp_spread_attribute
env c_onto valexpr
=
2548 let (p, _) = valexpr
in
2549 let env, te, valty
= expr
env valexpr
in
2550 (* Build the typed attribute node *)
2551 let typed_attr = T.Xhp_spread
te in
2552 let env, attr_ptys
= match c_onto
with
2554 | Some
class_info -> Typing_xhp.get_spread_attributes
env p class_info valty
2555 in env, typed_attr, attr_ptys
2558 * Simple XHP attributes (attr={expr} form) are simply interpreted as a member
2559 * variable prefixed with a colon, the types of which will be validated later
2561 and xhp_simple_attribute
env id valexpr
=
2562 let (p, _) = valexpr
in
2563 let env, te, valty
= expr
env valexpr
in
2564 (* This converts the attribute name to a member name. *)
2565 let name = ":"^
(snd
id) in
2566 let attr_pty = ((fst
id, name), (p, valty
)) in
2567 let typed_attr = T.Xhp_simple
(id, te) in
2568 env, typed_attr, [attr_pty]
2572 * Typecheck the attribute expressions - this just checks that the expressions are
2573 * valid, not that they match the declared type for the attribute and,
2574 * in case of spreads, makes sure they are XHP.
2576 and xhp_attribute_exprs
env cid attrl
=
2577 let handle_attr (env, typed_attrl
, attr_ptyl
) attr
=
2578 let env, typed_attr, attr_ptys
= match attr
with
2579 | Xhp_simple
(id, valexpr
) -> xhp_simple_attribute
env id valexpr
2580 | Xhp_spread valexpr
-> xhp_spread_attribute
env cid valexpr
2582 env, typed_attr::typed_attrl
, attr_ptys
@ attr_ptyl
2584 let env, typed_attrl
, attr_ptyl
= List.fold_left ~f
:handle_attr ~init
:(env, [], []) attrl
in
2585 env, List.rev typed_attrl
, List.rev attr_ptyl
2587 and check_xhp_children
env pos ty =
2588 let tys = match ty with
2589 | _, Tunresolved ts
-> ts
2591 if List.for_all ~f
:(Typing_xhp.xhp_child
env pos) tys then env
2593 let ty_str = Typing_print.error
(snd
ty) in
2594 let msgl = Reason.to_string
("This is "^
ty_str) (fst
ty) in
2595 Errors.illegal_xhp_child
pos msgl; env
2598 and fold_xhp_body_elements
(env, tel
) body
=
2599 let expr_pos = fst body
in
2600 let env, te, ty = expr
env body
in
2601 let env, ty = Env.expand_type
env ty in
2602 let env, ty = TUtils.fold_unresolved
env ty in
2603 let env = check_xhp_children
env expr_pos ty in
2606 (*****************************************************************************)
2607 (* Anonymous functions. *)
2608 (*****************************************************************************)
2609 and anon_bind_param
params (env, t_params
) ty : Env.env * Tast.fun_param list
=
2612 (* This code cannot be executed normally, because the arity is wrong
2613 * and it will error later. Bind as many parameters as we can and carry
2616 | param
:: paraml
->
2618 match param
.param_hint
with
2621 let h = Decl_hint.hint
env.Env.decl_env
h in
2622 (* When creating a closure, the 'this' type will mean the
2623 * late bound type of the current enclosing class
2626 { (Phase.env_with_self
env) with from_class
= Some CIstatic
} in
2627 let env, h = Phase.localize ~
ety_env env h in
2628 let pos = Reason.to_pos
(fst
ty) in
2629 (* Don't use Type.sub_type as it resets env.Env.pos unnecessarily *)
2630 let env = anon_sub_type
pos Reason.URparam
env ty h in
2631 (* Closures are allowed to have explicit type-hints. When
2632 * that is the case we should check that the argument passed
2633 * is compatible with the type-hint.
2634 * The body of the function should be type-checked with the
2635 * hint and not the type of the argument passed.
2636 * Otherwise it leads to strange results where
2637 * foo(?string $x = null) is called with a string and fails to
2638 * type-check. If $x is a string instead of ?string, null is not
2639 * subtype of string ...
2641 let env, t_param
= bind_param env (h, param
) in
2642 env, t_params
@ [t_param
]
2644 let env, t_param
= bind_param env (ty, param
) in
2645 env, t_params
@ [t_param
]
2647 and anon_bind_variadic
env vparam variadic_ty
=
2649 match vparam
.param_hint
with
2651 (* if the hint is missing, use the type we expect *)
2652 env, variadic_ty
, Reason.to_pos
(fst variadic_ty
)
2654 let h = Decl_hint.hint
env.Env.decl_env hint
in
2656 { (Phase.env_with_self
env) with from_class
= Some CIstatic
; } in
2657 let env, h = Phase.localize ~
ety_env env h in
2658 let pos = Reason.to_pos
(fst variadic_ty
) in
2659 let env = anon_sub_type
pos Reason.URparam
env variadic_ty
h in
2660 env, h, vparam
.param_pos
2662 let r = Reason.Rvar_param
pos in
2663 let arr_values = r, (snd
ty) in
2664 let ty = r, Tarraykind
(AKvarray
arr_values) in
2665 let env, t_variadic
= bind_param env (ty, vparam
) in
2669 and anon_bind_opt_param
env param
: Env.env =
2670 match param
.param_expr
with
2672 let ty = Reason.Rwitness param
.param_pos
, Typing_utils.tany
env in
2673 let env, _ = bind_param env (ty, param
) in
2676 let env, _te
, ty = expr
env default
in
2677 Typing_sequencing.sequence_check_expr default
;
2678 let env, _ = bind_param env (ty, param
) in
2681 and anon_check_param
env param
=
2682 match param
.param_hint
with
2685 let env, hty
= Phase.hint_locl
env hty
in
2686 let paramty = Env.get_local
env (Local_id.get param
.param_name
) in
2687 let hint_pos = Reason.to_pos
(fst hty
) in
2688 let env = Type.sub_type
hint_pos Reason.URhint
env paramty hty
in
2691 and anon_block
env b
=
2692 let is_not_next = function C.Next
-> false | _ -> true in
2693 let all_but_next = List.filter
C.all ~f
:is_not_next in
2694 let env, tb
= LEnv.stash_and_do
env all_but_next (fun env -> block
env b
) in
2697 (* Make a type-checking function for an anonymous function. *)
2698 and anon_make tenv
p f
ft idl
=
2699 let anon_lenv = tenv
.Env.lenv in
2700 let is_typing_self = ref false in
2701 let nb = Nast.assert_named_body f
.f_body
in
2702 let is_coroutine = f
.f_fun_kind
= Ast.FCoroutine
in
2706 (* Here ret_ty should include Awaitable wrapper *)
2707 fun ?el ?
ret_ty env supplied_params supplied_arity
->
2710 Errors.anonymous_recursive
p;
2711 expr_error env p (Reason.Rwitness
p)
2714 is_typing_self := true;
2715 Env.anon
anon_lenv env begin fun env ->
2716 let env = Env.clear_params
env in
2717 let make_variadic_arg env varg tyl
=
2718 let remaining_types =
2719 (* It's possible the variadic arg will capture the variadic
2720 * parameter of the supplied arity (if arity is Fvariadic)
2721 * and additional supplied params.
2723 * For example in cases such as:
2724 * lambda1 = (int $a, string...$c) ==> {};
2725 * lambda1(1, "hello", ...$y); (where $y is a variadic string)
2726 * lambda1(1, "hello", "world");
2727 * then ...$c will contain "hello" and everything in $y in the first
2728 * example, and "hello" and "world" in the second example.
2730 * To account for a mismatch in arity, we take the remaining supplied
2731 * parameters and return a list of all their types. We'll use this
2732 * to create a union type when creating the typed variadic arg.
2734 let remaining_params = List.drop supplied_params
(List.length f
.f_params
) in
2735 List.map ~f
:(fun param
-> param
.fp_type
) remaining_params
2737 let r = Reason.Rvar_param
(varg
.param_pos
) in
2738 let union = Tunresolved
(tyl
@ remaining_types) in
2739 let env, t_param
= anon_bind_variadic
env varg
(r, union) in
2740 env, T.FVvariadicArg t_param
2742 let env, t_variadic
=
2743 begin match f
.f_variadic
, supplied_arity
with
2744 | FVvariadicArg arg
, Fvariadic
(_, variadic
) ->
2745 make_variadic_arg env arg
[variadic
.fp_type
]
2746 | FVvariadicArg arg
, Fstandard
_ ->
2747 make_variadic_arg env arg
[]
2748 | FVellipsis
pos, _ -> env, T.FVellipsis
pos
2749 | _, _ -> env, T.FVnonVariadic
2751 let params = ref f
.f_params
in
2752 let env, t_params
= List.fold_left ~f
:(anon_bind_param
params) ~init
:(env, [])
2753 (List.map supplied_params
(fun x
-> x
.fp_type
)) in
2754 let env = List.fold_left ~f
:anon_bind_opt_param ~init
:env !params in
2755 let env = List.fold_left ~f
:anon_check_param ~init
:env f
.f_params
in
2756 let env = match el
with
2758 iter2_shortest
Unify.unify_param_modes
ft.ft_params supplied_params
;
2761 let var_param = match f
.f_variadic
with
2763 let param = TUtils.default_fun_param ~
pos
2764 (Reason.Rvar_param
pos, Tany
) in
2767 let rec iter l1 l2
=
2768 match l1
, l2
, var_param with
2771 | [], x2
::rl2
, Some def1
->
2772 param_modes ~is_variadic
:true def1 x2
;
2774 | x1
::rl1
, x2
::rl2
, _ -> param_modes x1 x2
; iter rl1 rl2
2776 iter ft.ft_params x
;
2777 wfold_left2 inout_write_back
env ft.ft_params x
in
2778 let env = Env.set_fn_kind
env f
.f_fun_kind
in
2782 (* Do we have a contextual return type? *)
2783 begin match ret_ty with
2785 let env, ret_ty = Env.fresh_unresolved_type
env in
2786 env, Typing_return.wrap_awaitable
env p ret_ty
2788 (* We might need to force it to be Awaitable if it is a type variable *)
2789 Typing_return.force_awaitable
env p ret_ty
2792 let ret = TI.instantiable_hint
env x
in
2793 (* If a 'this' type appears it needs to be compatible with the
2797 { (Phase.env_with_self
env) with
2798 from_class
= Some CIstatic
} in
2799 Phase.localize ~
ety_env env ret in
2800 let env = Env.set_return
env
2801 (Typing_return.make_info f
.f_fun_kind
[] env
2802 ~is_explicit
:(Option.is_some
ret_ty)
2803 ~is_by_ref
:f
.f_ret_by_ref
2805 let local_tpenv = env.Env.lenv.Env.tpenv
in
2806 let env, tb
= anon_block
env nb.fnb_nast
in
2808 if Nast_terminality.Terminal.block tenv
nb.fnb_nast
2809 || nb.fnb_unsafe
|| !auto_complete
2811 else fun_implicit_return
env p hret f
.f_fun_kind
2813 (* We don't want the *uses* of the function to affect its return type *)
2814 let env, hret
= Env.unbind
env hret
in
2815 is_typing_self := false;
2817 T.f_annotation
= Env.save
local_tpenv env;
2818 T.f_mode
= f
.f_mode
;
2820 T.f_name
= f
.f_name
;
2821 T.f_tparams
= f
.f_tparams
;
2822 T.f_where_constraints
= f
.f_where_constraints
;
2823 T.f_fun_kind
= f
.f_fun_kind
;
2824 T.f_user_attributes
= List.map f
.f_user_attributes
(user_attribute
env);
2825 T.f_body
= T.NamedBody
{
2827 T.fnb_unsafe
= nb.fnb_unsafe
;
2829 T.f_params
= t_params
;
2830 T.f_variadic
= t_variadic
; (* TODO TAST: Variadic efuns *)
2831 T.f_ret_by_ref
= f
.f_ret_by_ref
;
2833 let ty = (Reason.Rwitness
p, Tfun
ft) in
2834 let te = T.make_typed_expr
p ty (T.Efun
(tfun_, idl
)) in
2839 (*****************************************************************************)
2840 (* End of anonymous functions. *)
2841 (*****************************************************************************)
2843 and special_func
env p func
=
2844 let env, tfunc
, ty = (match func
with
2846 let env, te, ety
= expr
env e in
2847 let env, ty = Async.gena
env p ety
in
2850 let env, tel
, etyl
= exprs ~allow_non_awaited_awaitable_in_rx
:true env el
in
2851 let env, ty = Async.genva
env p etyl
in
2852 env, T.Genva tel
, ty
2853 | Gen_array_rec
e ->
2854 let env, te, ety
= expr
env e in
2855 let env, ty = Async.gen_array_rec
env p ety
in
2856 env, T.Gen_array_rec
te, ty
2859 (Reason.Rwitness
p, Tclass
((p, SN.Classes.cAwaitable
), [ty])) in
2860 env, T.make_typed_expr
p result_ty (T.Special_func tfunc
), result_ty
2862 and requires_consistent_construct
= function
2869 (* Caller will be looking for a particular form of expected type
2870 * e.g. a function type (when checking lambdas) or tuple type (when checking
2871 * tuples). First expand the expected type and elide single union; also
2872 * strip nullables, so ?t becomes t, as context will always accept a t if a ?t
2875 and expand_expected
env expected =
2879 | Some
(p, ur
, ty) ->
2880 let env, ty = Env.expand_type
env ty in
2882 | _, Tany
-> env, None
2883 | _, Tunresolved
[ty] -> env, Some
(p, ur
, ty)
2884 | _, Toption
ty -> env, Some
(p, ur
, ty)
2885 | _ -> env, Some
(p, ur
, ty)
2887 (* Do a subtype check of inferred type against expected type *)
2888 and check_expected_ty message
env inferred_ty expected =
2892 | Some
(p, ur
, expected_ty
) ->
2893 Typing_log.log_types
1 p env
2895 (Printf.sprintf
"Typing.check_expected_ty %s" message
,
2896 [Typing_log.Log_type
("inferred_ty", inferred_ty);
2897 Typing_log.Log_type
("expected_ty", expected_ty
)])];
2898 Type.sub_type
p ur
env inferred_ty expected_ty
2900 and new_object ~
expected ~check_parent ~check_not_abstract ~is_using_clause
p env cid el uel
=
2901 (* Obtain class info from the cid expression. We get multiple
2902 * results with a CIexpr that has a union type *)
2903 let env, tcid
, classes
= instantiable_cid
p env cid in
2904 let finish env tcid tel tuel
ty ctor_fty
=
2906 if check_parent
then env, ty
2907 else ExprDepTy.make
env cid ty in
2908 env, tcid
, tel
, tuel
, new_ty
, ctor_fty
in
2909 let rec gather env tel tuel res classes
=
2915 let _ = exprs env el
in
2916 let r = Reason.Runknown_class
p in
2917 finish env tcid tel tuel
(r, Tobject
) (r, TUtils.terr
env)
2918 | [ty,ctor_fty
] -> finish env tcid tel tuel
ty ctor_fty
2920 let tyl, ctyl
= List.unzip l
in
2921 let r = Reason.Rwitness
p in
2922 finish env tcid tel tuel
(r, Tunresolved
tyl) (r, Tunresolved ctyl
)
2925 | (cname
, class_info, c_ty
)::classes
->
2926 if check_not_abstract
&& class_info.tc_abstract
2927 && not
(requires_consistent_construct
cid) then
2928 uninstantiable_error
p cid class_info.tc_pos
class_info.tc_name
p c_ty
;
2929 let env, obj_ty_
, params =
2930 match cid, snd c_ty
with
2931 | CI
(_, _::_), Tclass
(_, tyl) -> env, (snd c_ty
), tyl
2933 let env, params = List.map_env
env class_info.tc_tparams
2934 (fun env _ -> Env.fresh_unresolved_type
env) in
2935 env, (Tclass
(cname
, params)), params in
2936 if not check_parent
&& not is_using_clause
&& class_info.tc_is_disposable
2937 then Errors.invalid_new_disposable
p;
2938 let r_witness = Reason.Rwitness
p in
2939 let obj_ty = (r_witness, obj_ty_
) in
2942 | CIstatic
-> (r_witness, TUtils.this_of
obj_ty)
2943 | CIexpr
_ -> (r_witness, snd
c_ty)
2948 else ExprDepTy.make
env cid c_ty in
2949 let env, _tcid
, tel
, tuel
, ctor_fty
=
2950 let env = check_expected_ty
"New" env new_ty
expected in
2951 call_construct
p env class_info params el uel
cid in
2952 if not
(snd
class_info.tc_construct
) then
2954 | CIstatic
-> Errors.new_inconsistent_construct
p cname `static
2955 | CIexpr
_ -> Errors.new_inconsistent_construct
p cname `classname
2960 match (fst
class_info.tc_construct
) with
2961 | Some
{ce_type
= lazy ty; _ } ->
2963 type_expansions
= [];
2964 substs
= SMap.empty
;
2967 validate_dty
= None
;
2969 let _, ctor_fty = Phase.localize ~
ety_env env ty in
2970 check_abstract_parent_meth
SN.Members.__construct
p ctor_fty
2973 gather env tel tuel
((obj_ty,ctor_fty)::res
) classes
2974 | CIstatic
| CI
_ | CIself
-> gather env tel tuel
((c_ty,ctor_fty)::res
) classes
2976 (* When constructing from a (classname) variable, the variable
2977 * dictates what the constructed object is going to be. This allows
2978 * for generic and dependent types to be correctly carried
2979 * through the 'new $foo()' iff the constructed obj_ty is a
2980 * supertype of the variable-dictated c_ty *)
2981 let env = SubType.sub_type
env c_ty obj_ty in
2982 gather env tel tuel
((c_ty,ctor_fty)::res
) classes
2984 gather env [] [] [] classes
2986 (* FIXME: we need to separate our instantiability into two parts. Currently,
2987 * all this function is doing is checking if a given type is inhabited --
2988 * that is, whether there are runtime values of type T. However,
2989 * instantiability should be the stricter notion that T has a runtime
2990 * constructor; that is, `new T()` should be valid. In particular, interfaces
2991 * are inhabited, but not instantiable.
2992 * To make this work with classname, we likely need to add something like
2993 * concrete_classname<T>, where T cannot be an interface.
2995 and instantiable_cid
p env cid =
2996 let env, te, classes
= class_id_for_new
p env cid in
2998 List.iter classes
begin fun ((pos, name), class_info, c_ty) ->
2999 if class_info.tc_kind
= Ast.Ctrait
|| class_info.tc_kind
= Ast.Cenum
3002 | CIexpr
_ | CI
_ ->
3003 uninstantiable_error
p cid class_info.tc_pos
name pos c_ty
3004 | CIstatic
| CIparent
| CIself
-> ()
3005 else if class_info.tc_kind
= Ast.Cabstract
&& class_info.tc_final
3007 uninstantiable_error
p cid class_info.tc_pos
name pos c_ty
3012 and uninstantiable_error reason_pos
cid c_tc_pos c_name c_usage_pos
c_ty =
3013 let reason_msgl = match cid with
3015 let ty_str = "This would be "^
Typing_print.error
(snd
c_ty) in
3016 [(reason_pos
, ty_str)]
3018 Errors.uninstantiable_class c_usage_pos c_tc_pos c_name
reason_msgl
3020 and exception_ty
pos env ty =
3021 let exn_ty = Reason.Rthrow
pos, Tclass
((pos, SN.Classes.cThrowable
), []) in
3022 Type.sub_type
pos (Reason.URthrow
) env ty exn_ty
3024 and shape_field_pos
= function
3025 | Ast.SFlit
(p, _) -> p
3026 | Ast.SFclass_const
((cls_pos
, _), (mem_pos
, _)) -> Pos.btw cls_pos mem_pos
3028 and check_shape_keys_validity
env pos keys =
3029 (* If the key is a class constant, get its class name and type. *)
3030 let get_field_info env key =
3031 let key_pos = shape_field_pos
key in
3032 (* Empty strings or literals that start with numbers are not
3033 permitted as shape field names. *)
3035 | Ast.SFlit
(_, key_name
) ->
3036 if (String.length key_name
= 0) then
3037 (Errors.invalid_shape_field_name_empty
key_pos)
3038 else if (key_name
.[0] >= '
0'
&& key_name
.[0] <='
9'
) then
3039 (Errors.invalid_shape_field_name_number
key_pos);
3041 | Ast.SFclass_const
(_, cls
as x
, y
) ->
3042 let env, _te
, ty = class_const
env pos (((), CI
(x
, [])), y
) in
3043 let env = Typing_enum.check_valid_array_key_type
3044 Errors.invalid_shape_field_type ~allow_any
:false
3046 env, key_pos, Some
(cls
, ty))
3049 let check_field witness_pos witness_info
env key =
3050 let env, key_pos, key_info
= get_field_info env key in
3051 (match witness_info
, key_info
with
3053 Errors.invalid_shape_field_literal
key_pos witness_pos
; env
3055 Errors.invalid_shape_field_const
key_pos witness_pos
; env
3057 | Some
(cls1
, ty1), Some
(cls2
, ty2
) ->
3058 if cls1
<> cls2
then
3059 Errors.shape_field_class_mismatch
3060 key_pos witness_pos
(strip_ns cls2
) (strip_ns cls1
);
3061 (* We want to use our own error message here instead of the normal
3062 * unification one. *)
3064 (fun () -> Unify.iunify
env ty1 ty2
)
3066 Errors.shape_field_type_mismatch
3068 (Typing_print.error
(snd ty2
)) (Typing_print.error
(snd
ty1));
3072 (* Sort the keys by their positions since the error messages will make
3073 * more sense if we take the one that appears first as canonical and if
3074 * they are processed in source order. *)
3075 let cmp_keys x y
= Pos.compare
(shape_field_pos x
) (shape_field_pos y
) in
3076 let keys = List.sort
cmp_keys keys in
3080 | witness
:: rest_keys
->
3081 let env, pos, info
= get_field_info env witness
in
3082 List.fold_left ~f
:(check_field pos info
) ~init
:env rest_keys
3084 and check_valid_rvalue
p env ty =
3085 let rec iter_over_types env tyl =
3091 let env, ety
= Env.expand_type
env ty in
3093 | r, Tprim Tnoreturn
->
3094 Errors.noreturn_usage
p
3095 (Reason.to_string
"A noreturn function always throws or exits" r);
3096 env, (r, Typing_utils.terr
env)
3098 | r, Tprim Tvoid
when not
(TUtils.is_void_type_of_null
env) ->
3100 (Reason.to_string
"A void function doesn't return a value" r);
3101 env, (r, Typing_utils.terr
env)
3103 | _, Tunresolved tyl2
->
3104 iter_over_types env (tyl2
@ tyl)
3107 iter_over_types env tyl in
3108 iter_over_types env [ty]
3110 and set_valid_rvalue
p env x
ty =
3111 let env, ty = check_valid_rvalue
p env ty in
3112 let env = set_local
env (p, x
) ty in
3113 (* We are assigning a new value to the local variable, so we need to
3114 * generate a new expression id
3116 let env = Env.set_local_expr_id
env x
(Ident.tmp
()) in
3119 (* Deal with assignment of a value of type ty2 to lvalue e1 *)
3120 and assign
p env e1 ty2
: _ * T.expr
* T.ty =
3121 assign_
p Reason.URassign
env e1 ty2
3123 and assign_
p ur
env e1 ty2
=
3124 let make_result env te1 ty1 = (env, T.make_typed_expr
(fst e1
) ty1 te1, ty1) in
3126 | (_, Lvar
((_, x
) as id)) ->
3127 let env, ty1 = set_valid_rvalue
p env x ty2
in
3128 make_result env (T.Lvar
id) ty1
3129 | (_, Lplaceholder
id) ->
3130 let placeholder_ty = Reason.Rplaceholder
p, (Tprim Tvoid
) in
3131 make_result env (T.Lplaceholder
id) placeholder_ty
3133 let env, folded_ty2
= TUtils.fold_unresolved
env ty2
in
3135 try_over_concrete_supertypes env folded_ty2
3136 begin fun env ty2
->
3138 (* Vector<t> or ImmVector<t> or ConstVector<t> or vec<T> *)
3139 | (_, Tclass
((_, x
), [elt_type
]))
3140 when x
= SN.Collections.cVector
3141 || x
= SN.Collections.cImmVector
3142 || x
= SN.Collections.cVec
3143 || x
= SN.Collections.cConstVector
->
3144 (* Vector assignment is illegal in a reactive context
3145 but vec assignment is okay *)
3146 if x
<> SN.Collections.cVec
3147 then Env.error_if_reactive_context
env @@ begin fun () ->
3148 Errors.nonreactive_append
p
3150 let env, tel
= List.map_env
env el
begin fun env e ->
3151 let env, te, _ = assign
(fst
e) env e elt_type
in
3154 make_result env (T.List tel
) ty2
3155 (* array<t> or varray<t> *)
3156 | (_, Tarraykind
(AKvec elt_type
))
3157 | (_, Tarraykind
(AKvarray elt_type
)) ->
3158 let env, tel
= List.map_env
env el
begin fun env e ->
3159 let env, te, _ = assign
(fst
e) env e elt_type
in
3162 make_result env (T.List tel
) ty2
3163 (* array or empty array or Tany *)
3164 | (r, (Tarraykind
(AKany
| AKempty
) | Tany
)) ->
3165 let env, tel
= List.map_env
env el
begin fun env e ->
3166 let env, te, _ = assign
(fst
e) env e (r, Typing_utils.tany
env) in
3169 make_result env (T.List tel
) ty2
3170 | (r, (Tdynamic
)) ->
3171 let env, tel
= List.map_env
env el
begin fun env e ->
3172 let env, te, _ = assign
(fst
e) env e (r, Tdynamic
) in
3175 make_result env (T.List tel
) ty2
3177 | ((r, Tclass
((_, coll
), [ty1; ty2
])) as folded_ety2
)
3178 when coll
= SN.Collections.cPair
->
3181 let env, te1, _ = assign
p env x1
ty1 in
3182 let env, te2, _ = assign
p env x2 ty2
in
3183 make_result env (T.List
[te1; te2]) folded_ety2
3185 Errors.pair_arity
p;
3186 make_result env T.Any
(r, Typing_utils.terr
env))
3187 (* tuple-like array *)
3188 | (r, Tarraykind
(AKtuple fields
)) ->
3190 let p2 = Reason.to_pos
r in
3191 let tyl = List.rev
(IMap.values fields
) in
3192 let size1 = List.length el
in
3193 let size2 = List.length
tyl in
3196 Errors.tuple_arity
p2 size2 p1 size1;
3197 make_result env T.Any
(r, Typing_utils.terr
env)
3200 let env, reversed_tel
=
3201 List.fold2_exn el
tyl ~f
:begin fun (env,tel
) lvalue ty2
->
3202 let env, te, _ = assign
p env lvalue ty2
in
3204 end ~init
:(env,[]) in
3205 make_result env (T.List
(List.rev reversed_tel
)) ty2
3206 (* Other, including tuples. Create a tuple type for the left hand
3207 * side and attempt subtype against it. In particular this deals with
3208 * types such as (string,int) | (int,bool) *)
3211 List.map_env
env el
(fun env _ -> Env.fresh_unresolved_type
env) in
3212 let env = Type.sub_type
p ur
env folded_ty2
3213 (Reason.Rwitness
(fst e1
), Ttuple
tyl) in
3214 let env, reversed_tel
=
3215 List.fold2_exn el
tyl ~init
:(env,[]) ~f
:(fun (env,tel
) lvalue ty2
->
3216 let env, te, _ = assign
p env lvalue ty2
in
3218 make_result env (T.List
(List.rev reversed_tel
)) ty2
3220 begin match resl with
3222 | _ -> assign_simple
p ur
env e1 ty2
3227 let lenv = env.Env.lenv in
3228 let no_fakes = LEnv.env_with_empty_fakes
env in
3229 (* In this section, we check that the assignment is compatible with
3230 * the real type of a member. Remember that members can change
3231 * type (cf fake_members). But when we assign a value to $this->x,
3232 * we want to make sure that the type assign to $this->x is compatible
3233 * with the actual type hint. In this portion of the code, type-check
3234 * the assignment in an environment without fakes, and therefore
3235 * check that the assignment is compatible with the type of
3238 let env, te1, real_type
= lvalue
no_fakes e1
in
3239 let env, exp_real_type
= Env.expand_type
env real_type
in
3240 let env = { env with Env.lenv = lenv } in
3241 let env, ety2
= Env.expand_type
env ty2
in
3242 let real_type_list =
3243 match exp_real_type
with
3244 | _, Tunresolved
tyl -> tyl
3247 let env = List.fold_left
real_type_list ~f
:begin fun env real_type
->
3248 Type.sub_type
p ur
env ety2 real_type
3251 | _, Obj_get
((_, This
| _, Lvar
_ as obj
),
3252 (_, Id
(_, member_name
)),
3254 let env, local = Env.FakeMembers.make
p env obj member_name
in
3255 let () = (match obj
with
3257 Typing_suggest.save_member member_name
env exp_real_type ty2
3260 let env, ty = set_valid_rvalue
p env local ty2
in
3262 | _, Class_get
(((), x
), (_, y
)) ->
3263 let env, local = Env.FakeMembers.make_static
p env x y
in
3264 let env, ty3
= set_valid_rvalue
p env local ty2
in
3268 Typing_suggest.save_member y
env exp_real_type ty2
;
3271 | _ -> env, te1, ty2
3273 | _, Array_get
((_, Lvar
(_, lvar
)) as shape
, ((Some
_) as e2
)) ->
3274 let access_type = Typing_arrays.static_array_access
env e2
in
3275 (* In the case of an assignment of the form $x['new_field'] = ...;
3276 * $x could be a shape where the field 'new_field' is not yet defined.
3277 * When that is the case we want to add the field to its type.
3279 let env, _te
, shape_ty
= expr
env shape
in
3280 let env, shape_ty
= Typing_arrays.update_array_type_on_lvar_assignment
3281 p access_type env shape_ty
in
3282 let env, _ = set_valid_rvalue
p env lvar shape_ty
in
3283 (* We still need to call assign_simple in order to bind the freshly
3284 * created variable in added shape field. Moreover, it's needed because
3285 * shape_ty could be more than just a shape. It could be an unresolved
3286 * type where some elements are shapes and some others are not.
3288 assign_simple
p ur
env e1 ty2
3290 Errors.this_lvalue
p;
3291 make_result env T.Any
(Reason.Rwitness
p, Typing_utils.terr
env)
3292 | pref
, Unop
(Ast.Uref
, e1'
) ->
3293 (* references can be "lvalues" in foreach bindings *)
3294 Errors.binding_ref_in_array pref
;
3295 let env, texpr
, ty = assign
p env e1' ty2
in
3296 make_result env (T.Unop
(Ast.Uref
, texpr
)) ty
3298 assign_simple
p ur
env e1 ty2
3300 and assign_simple
pos ur
env e1 ty2
=
3301 let env, te1, ty1 = lvalue
env e1
in
3302 let env, ty2
= check_valid_rvalue
pos env ty2
in
3303 let env, ty2
= TUtils.unresolved
env ty2
in
3304 let env = Type.sub_type
pos ur
env ty2
ty1 in
3307 and array_field
env = function
3308 | Nast.AFvalue ve
->
3309 let env, tve
, tv
= expr
env ve
in
3310 let env, tv
= Typing_env.unbind
env tv
in
3311 env, (T.AFvalue tve
, None
, tv
)
3312 | Nast.AFkvalue
(ke
, ve
) ->
3313 let env, tke
, tk
= expr
env ke
in
3314 let env, tve
, tv
= expr
env ve
in
3315 let env, tv
= Typing_env.unbind
env tv
in
3316 env, (T.AFkvalue
(tke
, tve
), Some tk
, tv
)
3318 and array_value ~
expected env x
=
3319 let env, te, ty = expr ?
expected ~
forbid_uref:true env x
in
3320 let env, ty = Typing_env.unbind
env ty in
3323 and array_field_value ~
expected env = function
3325 | Nast.AFkvalue
(_, x
) ->
3326 array_value ~
expected env x
3328 and array_field_key ~
expected env = function
3329 (* This shouldn't happen *)
3330 | Nast.AFvalue
(p, _) ->
3331 let ty = (Reason.Rwitness
p, Tprim Tint
) in
3332 env, (T.make_typed_expr
p ty T.Any
, ty)
3333 | Nast.AFkvalue
(x
, _) ->
3334 array_value ~
expected env x
3336 and akshape_field
env = function
3337 | Nast.AFkvalue
(k
, v
) ->
3338 let env, tek
, tk
= expr
env k
in
3339 let env, tk
= Typing_env.unbind
env tk
in
3340 let env, tk
= TUtils.unresolved
env tk
in
3341 let env, tev
, tv
= expr
env v
in
3342 let env, tv
= Typing_env.unbind
env tv
in
3343 let env, tv
= TUtils.unresolved
env tv
in
3345 match TUtils.shape_field_name
env k
with
3346 | Some
field_name -> field_name
3347 | None
-> assert false in (* Typing_arrays.is_shape_like_array
3348 * should have prevented this *)
3349 env, T.AFkvalue
(tek
, tev
), (field_name, (tk
, tv
))
3350 | Nast.AFvalue
_ -> assert false (* Typing_arrays.is_shape_like_array
3351 * should have prevented this *)
3353 and aktuple_afvalue
env v
=
3354 let env, tev
, tv
= expr
env v
in
3355 let env, tv
= Typing_env.unbind
env tv
in
3356 let env, ty = TUtils.unresolved
env tv
in
3359 and aktuple_field
env = function
3360 | Nast.AFvalue v
-> aktuple_afvalue
env v
3361 | Nast.AFkvalue
_ -> assert false (* check_consistent_fields
3362 * should have prevented this *)
3363 and check_parent_construct
pos env el uel env_parent
=
3364 let check_not_abstract = false in
3365 let env, env_parent
= Phase.localize_with_self
env env_parent
in
3366 let env, _tcid
, tel
, tuel
, parent
, fty =
3367 new_object ~
expected:None ~check_parent
:true ~
check_not_abstract
3368 ~is_using_clause
:false
3369 pos env CIparent el uel
in
3370 let env, parent
= Type.unify
pos (Reason.URnone
) env env_parent parent
in
3371 env, tel
, tuel
, (Reason.Rwitness
pos, Tprim Tvoid
), parent
, fty
3373 and call_parent_construct
pos env el uel
=
3374 let parent = Env.get_parent
env in
3377 check_parent_construct
pos env el uel
parent
3387 | Tvarray_or_darray
_
3397 ) -> (* continue here *)
3398 let ty = (Reason.Rwitness
pos, Typing_utils.tany
env) in
3399 let default = env, [], [], ty, ty, ty in
3400 match Env.get_self
env with
3401 | _, Tclass
((_, self
), _) ->
3402 (match Env.get_class
env self
with
3403 | Some
({tc_kind
= Ast.Ctrait
; _}
3405 (match trait_most_concrete_req_class trait
env with
3406 | None
-> Errors.parent_in_trait
pos; default
3407 | Some
(_, parent_ty
) ->
3408 check_parent_construct
pos env el uel parent_ty
3411 if not self_tc
.tc_members_fully_known
3412 then () (* Don't know the hierarchy, assume it's correct *)
3413 else Errors.undefined_parent
pos;
3415 | None
-> assert false)
3416 | _, (Terr
| Tany
| Tmixed
| Tnonnull
| Tarraykind
_ | Toption
_
3417 | Tprim
_ | Tfun
_ | Ttuple
_ | Tshape
_ | Tvar
_ | Tdynamic
3418 | Tabstract
(_, _) | Tanon
(_, _) | Tunresolved
_ | Tobject
3420 Errors.parent_outside_class
pos;
3421 let ty = (Reason.Rwitness
pos, Typing_utils.terr
env) in
3422 env, [], [], ty, ty, ty
3424 (* parent::method() in a class definition invokes the specific parent
3425 * version of the method ... it better be callable *)
3426 and check_abstract_parent_meth mname
pos fty =
3427 if is_abstract_ft
fty
3428 then Errors.parent_abstract_call mname
pos (Reason.to_pos
(fst
fty));
3431 and is_abstract_ft
fty = match fty with
3432 | _r, Tfun
{ ft_abstract
= true; _ } -> true
3433 | _r, (Terr
| Tany
| Tmixed
| Tnonnull
| Tarraykind
_ | Toption
_ | Tprim
_
3434 | Tvar
_ | Tfun
_ | Tclass
(_, _) | Tabstract
(_, _) | Ttuple
_
3435 | Tanon
_ | Tunresolved
_ | Tobject
| Tshape
_ | Tdynamic
3439 (* Depending on the kind of expression we are dealing with
3440 * The typing of call is different.
3443 and dispatch_call ~
expected ~is_using_clause ~is_expr_statement
p env call_type
3444 (fpos
, fun_expr
as e) hl el uel ~in_suspend
=
3445 let make_call env te thl tel tuel
ty =
3446 env, T.make_typed_expr
p ty (T.Call
(call_type
, te, thl
, tel
, tuel
)), ty in
3447 (* TODO: Avoid Tany annotations in TAST by eliminating `make_call_special` *)
3448 let make_call_special env id tel
ty =
3449 make_call env (T.make_typed_expr fpos
(Reason.Rnone
, TUtils.tany
env) (T.Id
id)) [] tel
[] ty in
3450 (* For special functions and pseudofunctions with a definition in hhi. *)
3451 let make_call_special_from_def env id tel
ty_ =
3452 let env, fty = fun_type_of_id
env id hl
in
3453 let ty = match fty with
3454 | _, Tfun
ft -> ft.ft_ret
3455 | _ -> (Reason.Rwitness
p, ty_) in
3456 make_call env (T.make_typed_expr fpos
fty (T.Id
id)) [] tel
[] ty in
3457 let overload_function = overload_function make_call fpos
in
3459 let check_coroutine_call env fty =
3460 let () = if is_return_disposable_fun_type env fty && not is_using_clause
3461 then Errors.invalid_new_disposable
p else () in
3463 - Some true if type is definitely a coroutine
3464 - Some false if type is definitely not a coroutine
3465 - None if type is Tunresolved that contains
3466 both coroutine and non-coroutine constituents *)
3467 let rec is_coroutine ty =
3469 | Tfun
{ ft_is_coroutine
= true; _ } ->
3472 Some
(Option.value_map
(Env.get_anonymous
env id) ~
default:false ~f
:(fun (_,b
,_,_,_) -> b
) )
3474 begin match List.map ts ~f
:is_coroutine with
3477 (*if rest of the list has the same value as the first element
3478 return value of the first element or None otherwise*)
3479 if List.for_all xs ~f
:(Option.value_map ~
default:false ~f
:((=)x
))
3486 match in_suspend
, is_coroutine fty with
3488 | false, Some
false -> ()
3490 (* non-coroutine call in suspend *)
3491 Errors.non_coroutine_call_in_suspend
3493 (Reason.to_string
("This is " ^
Typing_print.error
(snd
fty)) (fst
fty));
3495 (*coroutine call outside of suspend *)
3496 Errors.coroutine_call_outside_of_suspend
p; in
3498 let check_function_in_suspend name =
3500 then Errors.function_is_not_coroutine fpos
name in
3502 let check_class_function_in_suspend class_name function_name
=
3503 check_function_in_suspend (class_name ^
"::" ^ function_name
) in
3506 (* Special function `echo` *)
3507 | Id
((p, pseudo_func
) as id) when pseudo_func
= SN.SpecialFunctions.echo
->
3508 check_function_in_suspend SN.SpecialFunctions.echo
;
3509 Env.error_if_shallow_reactive_context
env @@ begin fun () ->
3510 Errors.echo_in_reactive_context
p;
3512 let env, tel
, _ = exprs ~accept_using_var
:true env el
in
3513 make_call_special env id tel
(Reason.Rwitness
p, Tprim Tvoid
)
3514 (* Special function `empty` *)
3515 | Id
((_, pseudo_func
) as id) when pseudo_func
= SN.PseudoFunctions.empty
->
3516 check_function_in_suspend SN.PseudoFunctions.empty
;
3517 let env, tel
, _ = exprs ~accept_using_var
:true env el
in
3519 Errors.unpacking_disallowed_builtin_function
p pseudo_func
;
3520 if Env.is_strict
env then
3521 Errors.empty_in_strict
p;
3522 make_call_special_from_def env id tel
(Tprim Tbool
)
3523 (* Special function `isset` *)
3524 | Id
((_, pseudo_func
) as id) when pseudo_func
= SN.PseudoFunctions.isset
->
3525 check_function_in_suspend SN.PseudoFunctions.isset
;
3526 let env, tel
, _ = exprs ~accept_using_var
:true env el
in
3528 Errors.unpacking_disallowed_builtin_function
p pseudo_func
;
3529 if Env.is_strict
env then
3530 Errors.isset_in_strict
p;
3531 make_call_special_from_def env id tel
(Tprim Tbool
)
3532 (* Special function `unset` *)
3533 | Id
((_, pseudo_func
) as id) when pseudo_func
= SN.PseudoFunctions.unset
->
3534 check_function_in_suspend SN.PseudoFunctions.unset
;
3535 let env, tel
, _ = exprs env el
in
3536 Core_list.iter tel ~f
:(Typing_mutability.check_unset_target
env);
3538 Errors.unpacking_disallowed_builtin_function
p pseudo_func
;
3539 let env = if Env.is_strict
env then
3541 | [(_, Array_get
((_, Class_const
_), Some
_))], [] ->
3542 Errors.const_mutation
p Pos.none
"";
3544 | [(_, Array_get
(ea
, Some
_))], [] ->
3545 let env, _te
, ty = expr
env ea
in
3546 let tany = Typing_utils.tany env in
3547 if List.exists ~f
:(fun super
-> SubType.is_sub_type
env ty super
) [
3548 (Reason.Rnone
, (Tclass
((Pos.none
, SN.Collections.cDict
),
3549 [(Reason.Rnone
, tany); (Reason.Rnone
, tany)])));
3550 (Reason.Rnone
, (Tclass
((Pos.none
, SN.Collections.cKeyset
),
3551 [(Reason.Rnone
, tany)])));
3552 (Reason.Rnone
, Tarraykind AKany
)
3555 let env, (r, ety
) = Env.expand_type
env ty in
3556 Errors.unset_nonidx_in_strict
3558 (Reason.to_string
("This is " ^
Typing_print.error ety
) r);
3561 | _ -> Errors.unset_nonidx_in_strict
p []; env)
3564 | [(p, Obj_get
(_, _, OG_nullsafe
))] ->
3566 Errors.nullsafe_property_write_context
p;
3567 make_call_special_from_def env id tel
(TUtils.terr
env)
3570 make_call_special_from_def env id tel
(Tprim Tvoid
))
3571 (* Special function `freeze` *)
3572 | Id
((_, freeze
) as id) when freeze
= SN.Rx.freeze
->
3573 check_function_in_suspend SN.Rx.freeze
;
3574 let env, tel
, _ = exprs env el
in
3576 Errors.unpacking_disallowed_builtin_function
p freeze
;
3577 if not
(Env.env_local_reactive
env) then
3578 Errors.freeze_in_nonreactive_context
p;
3579 let env = Typing_mutability.freeze_local
p env tel
in
3580 make_call_special_from_def env id tel
(Tprim Tvoid
)
3581 (* Pseudo-function `get_called_class` *)
3582 | Id
(cp
, get_called_class
) when
3583 get_called_class
= SN.StdlibFunctions.get_called_class
3584 && el
= [] && uel
= [] ->
3585 check_function_in_suspend SN.StdlibFunctions.get_called_class
;
3586 (* get_called_class fetches the late-bound class *)
3587 if Env.is_outside_class
env then Errors.static_outside_class
p;
3588 class_const
env p (((), CIstatic
), (cp
, SN.Members.mClass
))
3589 (* Special function `array_filter` *)
3590 | Id
((_, array_filter
) as id)
3591 when array_filter
= SN.StdlibFunctions.array_filter
&& el
<> [] && uel
= [] ->
3592 check_function_in_suspend SN.StdlibFunctions.array_filter
;
3593 (* dispatch the call to typecheck the arguments *)
3594 let env, fty = fun_type_of_id
env id hl
in
3595 let env, tel
, tuel
, res
= call ~
expected p env fty el uel
in
3596 (* but ignore the result and overwrite it with custom return type *)
3597 let x = List.hd_exn el
in
3598 let env, _tx
, ty = expr
env x in
3599 let explain_array_filter (r, t
) =
3600 (Reason.Rarray_filter
(p, r), t
) in
3601 let get_value_type env tv
=
3603 if List.length el
> 1
3605 else TUtils.non_null
env tv
in
3606 env, explain_array_filter tv
in
3607 let rec get_array_filter_return_type env ty =
3608 let env, ety
= Env.expand_type
env ty in
3610 | (_, Tarraykind
(AKany
| AKempty
)) as array_type
->
3612 | (_, Tarraykind
(AKtuple
_)) ->
3613 let env, ty = Typing_arrays.downcast_aktypes
env ty in
3614 get_array_filter_return_type env ty
3615 | (r, Tarraykind
(AKvec tv
| AKvarray tv
)) ->
3616 let env, tv
= get_value_type env tv
in
3617 env, (r, Tarraykind
(AKvec tv
))
3618 | (r, Tunresolved
x) ->
3619 let env, x = List.map_env
env x get_array_filter_return_type in
3620 env, (r, Tunresolved
x)
3622 env, (r, Typing_utils.tany env)
3624 env, (r, Typing_utils.terr
env)
3626 let tk, tv
= Env.fresh_type
(), Env.fresh_type
() in
3629 let keyed_container = (
3632 (Pos.none
, SN.Collections.cKeyedContainer
), [tk; tv
]
3635 let env = SubType.sub_type
env ety
keyed_container in
3636 let env, tv
= get_value_type env tv
in
3637 env, (r, Tarraykind
(AKmap
(
3638 (explain_array_filter tk),
3641 (fun _ -> Errors.try_
3646 (Pos.none
, SN.Collections.cContainer
), [tv
]
3649 let env = SubType.sub_type
env ety
container in
3650 let env, tv
= get_value_type env tv
in
3651 env, (r, Tarraykind
(AKmap
(
3652 (explain_array_filter (r, Tprim Tarraykey
)),
3654 (fun _ -> env, res
)))
3655 in let env, rty = get_array_filter_return_type env ty in
3658 | r, Tfun
ft -> r, Tfun
{ft with ft_ret
= rty}
3660 make_call env (T.make_typed_expr fpos
fty (T.Id
id)) hl tel tuel
rty
3661 (* Special function `type_structure` *)
3662 | Id
(p, type_structure
)
3663 when type_structure
= SN.StdlibFunctions.type_structure
3664 && (List.length el
= 2) && uel
= [] ->
3665 check_function_in_suspend SN.StdlibFunctions.type_structure
;
3669 | p, Nast.String cst
->
3670 (* find the class constant implicitly defined by the typeconst *)
3671 let cid = (match e1
with
3672 | _, Class_const
(cid, (_, x))
3673 | _, Class_get
(cid, (_, x)) when x = SN.Members.mClass
-> cid
3674 | _ -> ((), Nast.CIexpr e1
)) in
3675 class_const ~incl_tc
:true env p (cid, (p, cst
))
3677 Errors.illegal_type_structure
p "second argument is not a string";
3678 expr_error env p (Reason.Rwitness
p))
3679 | _ -> assert false)
3680 (* Special function `array_map` *)
3681 | Id
((_, array_map
) as x)
3682 when array_map
= SN.StdlibFunctions.array_map
&& el
<> [] && uel
= [] ->
3683 check_function_in_suspend SN.StdlibFunctions.array_map
;
3684 let env, fty = fun_type_of_id
env x [] in
3685 let env, fty = Env.expand_type
env fty in
3686 let env, fty = match fty, el
with
3687 | ((r_fty
, Tfun
fty), _::args
) when args
<> [] ->
3688 let arity = List.length args
in
3690 Builds a function with signature:
3692 function<T1, ..., Tn, Tr>(
3693 (function(T1, ..., Tn):Tr),
3699 where R is constructed by build_output_container applied to Tr
3701 let build_function env build_output_container
=
3703 (* If T1, ... Tn, Tr are provided explicitly, instantiate the function parameters with
3704 * those directly. *)
3705 if List.length hl
= 0
3707 let env, tr
= Env.fresh_unresolved_type
env in
3708 let env, vars
= List.map_env
env args
3709 ~f
:(fun env _ -> Env.fresh_unresolved_type
env) in
3711 else if List.length hl
<> List.length args
+ 1 then begin
3712 let env, tr
= Env.fresh_unresolved_type
env in
3713 Errors.expected_tparam
fty.ft_pos
(1 + (List.length args
));
3714 let env, vars
= List.map_env
env args
3715 ~f
:(fun env _ -> Env.fresh_unresolved_type
env) in
3718 let env, vars_and_tr
= List.map_env
env hl
Phase.hint_locl
in
3719 let vars, trl
= List.split_n vars_and_tr
(List.length vars_and_tr
- 1) in
3720 (* Since we split the arguments and return type at the last index and the length is
3721 non-zero this is safe. *)
3722 let tr = List.hd_exn trl
in
3725 let f = TUtils.default_fun_param
(
3728 ft_pos
= fty.ft_pos
;
3729 ft_deprecated
= None
;
3730 ft_abstract
= false;
3731 ft_is_coroutine
= false;
3732 ft_arity
= Fstandard
(arity, arity);
3734 ft_where_constraints
= [];
3735 ft_params
= List.map
vars TUtils.default_fun_param
;
3737 ft_ret_by_ref
= fty.ft_ret_by_ref
;
3738 ft_reactive
= fty.ft_reactive
;
3739 ft_mutability
= fty.ft_mutability
;
3740 ft_returns_mutable
= fty.ft_returns_mutable
;
3741 ft_return_disposable
= fty.ft_return_disposable
;
3742 ft_decl_errors
= None
;
3743 ft_returns_void_to_rx
= fty.ft_returns_void_to_rx
;
3746 let containers = List.map
vars (fun var
->
3747 let tc = Tclass
((fty.ft_pos
, SN.Collections.cContainer
), [var
]) in
3748 TUtils.default_fun_param
(r_fty
, tc)
3750 env, (r_fty
, Tfun
{fty with
3751 ft_arity
= Fstandard
(arity+1, arity+1);
3752 ft_params
= f::containers;
3753 ft_ret
= build_output_container
tr;
3758 Takes a Container type and returns a function that can "pack" a type
3759 into an array of appropriate shape, preserving the key type, i.e.:
3760 array -> f, where f R = array
3761 array<X> -> f, where f R = array<R>
3762 array<X, Y> -> f, where f R = array<X, R>
3763 Vector<X> -> f where f R = array<R>
3764 KeyedContainer<X, Y> -> f, where f R = array<X, R>
3765 Container<X> -> f, where f R = array<arraykey, R>
3766 X -> f, where f R = Y
3768 let rec build_output_container
3769 (env:Env.env) (x:locl
ty) : (Env.env * (locl
ty -> locl
ty)) =
3770 let env, x = Env.expand_type
env x in (match x with
3771 | (_, Tarraykind
(AKany
| AKempty
)) as array_type
->
3772 env, (fun _ -> array_type
)
3773 | (_, Tarraykind
(AKtuple
_ )) ->
3774 let env, x = Typing_arrays.downcast_aktypes
env x in
3775 build_output_container env x
3776 | (r, Tarraykind
(AKvec
_ | AKvarray
_)) ->
3777 env, (fun tr -> (r, Tarraykind
(AKvec
(tr))) )
3779 env, (fun _ -> (r, Typing_utils.tany env))
3781 env, (fun _ -> (r, Typing_utils.terr
env))
3782 | (r, Tunresolved
x) ->
3783 let env, x = List.map_env
env x build_output_container in
3784 env, (fun tr -> (r, Tunresolved
(List.map
x (fun f -> f tr))))
3786 let tk, tv
= Env.fresh_type
(), Env.fresh_type
() in
3787 let try_vector env =
3791 (fty.ft_pos
, SN.Collections.cConstVector
), [tv
]
3794 let env = SubType.sub_type
env x vector in
3795 env, (fun tr -> (r, Tarraykind
(
3798 let try_keyed_container env =
3799 let keyed_container = (
3802 (fty.ft_pos
, SN.Collections.cKeyedContainer
), [tk; tv
]
3805 let env = SubType.sub_type
env x keyed_container in
3806 env, (fun tr -> (r, Tarraykind
(AKmap
(
3810 let try_container env =
3814 (fty.ft_pos
, SN.Collections.cContainer
), [tv
]
3817 let env = SubType.sub_type
env x container in
3818 env, (fun tr -> (r, Tarraykind
(AKmap
(
3819 (r, Tprim Tarraykey
),
3824 (fun _ -> Errors.try_
3826 try_keyed_container env)
3827 (fun _ -> Errors.try_
3830 (fun _ -> env, (fun _ -> (Reason.Rwitness
p, Typing_utils.tany env)))))) in
3832 Single argument calls preserve the key type, multi argument
3833 calls always return an array<Tr>
3837 let env, _tx
, x = expr
env x in
3838 let env, output_container
= build_output_container env x in
3839 build_function env output_container
3841 build_function env (fun tr ->
3842 (r_fty
, Tarraykind
(AKvec
(tr)))))
3844 let env, tel
, tuel
, ty = call ~
expected p env fty el
[] in
3845 make_call env (T.make_typed_expr fpos
fty (T.Id
x)) hl tel tuel
ty
3846 (* Special function `idx` *)
3847 | Id
((_, idx
) as id) when idx
= SN.FB.idx
->
3848 check_function_in_suspend SN.FB.idx
;
3849 (* Directly call get_fun so that we can muck with the type before
3850 * instantiation -- much easier to work in terms of Tgeneric Tk/Tv than
3851 * trying to figure out which Tvar is which. *)
3852 Typing_hooks.dispatch_fun_id_hook
(p, SN.FB.idx
);
3853 (match Env.get_fun
env (snd
id) with
3855 let param1, param2
, param3
=
3856 match fty.ft_params
with
3857 | [param1; param2
; param3
] -> param1, param2
, param3
3858 | _ -> assert false in
3859 let { fp_type
= (r2
, _); _ } = param2
in
3860 let { fp_type
= (r3
, _); _ } = param3
in
3861 let params, ret = match List.length el
with
3863 let ty1 = match param1.fp_type
with
3864 | (r11
, Toption
(r12
, Tapply
(coll
, [tk; (r13
, _) as tv
]))) ->
3865 (r11
, Toption
(r12
, Tapply
(coll
, [tk; (r13
, Toption tv
)])))
3866 | _ -> assert false in
3867 let param1 = { param1 with fp_type
= ty1 } in
3868 let ty2 = (r2
, Toption
(r2
, Tgeneric
"Tk")) in
3869 let param2 = { param2 with fp_type
= ty2 } in
3870 let rret = fst
fty.ft_ret
in
3871 let ret = (rret, Toption
(rret, Tgeneric
"Tv")) in
3872 [param1; param2], ret
3874 let param2 = { param2 with fp_type
= (r2
, Tgeneric
"Tk") } in
3875 let param3 = { param3 with fp_type
= (r3
, Tgeneric
"Tv") } in
3876 let ret = (fst
fty.ft_ret
, Tgeneric
"Tv") in
3877 [param1; param2; param3], ret
3878 | _ -> fty.ft_params
, fty.ft_ret
in
3879 let fty = { fty with ft_params
= params; ft_ret
= ret } in
3880 let ety_env = Phase.env_with_self
env in
3881 let env, fty = Phase.localize_ft ~use_pos
:p ~
ety_env env fty in
3882 let tfun = Reason.Rwitness
fty.ft_pos
, Tfun
fty in
3883 let env, tel
, _tuel
, ty = call ~
expected p env tfun el
[] in
3884 let env, ty = match ty with
3886 let env, ty = TUtils.non_null
env ty in
3887 env, (r, Toption
ty)
3889 make_call env (T.make_typed_expr fpos
tfun (T.Id
id)) [] tel
[] ty
3890 | None
-> unbound_name env id)
3892 (* Special function `Shapes::idx` *)
3893 | Class_const
(((), CI
((_, shapes
), _)) as class_id
, ((_, idx
) as method_id
))
3894 when shapes
= SN.Shapes.cShapes
&& idx
= SN.Shapes.idx
->
3895 check_class_function_in_suspend SN.Shapes.cShapes
SN.Shapes.idx
;
3896 overload_function p env class_id method_id el uel
3897 begin fun env fty res el
-> match el
with
3899 let env, _ts
, shape_ty
= expr
env shape
in
3900 Typing_shapes.idx
env p fty shape_ty field None
3901 | [shape
; field
; default] ->
3902 let env, _ts
, shape_ty
= expr
env shape
in
3903 let env, _td
, default_ty
= expr
env default in
3904 Typing_shapes.idx
env p fty shape_ty field
3905 (Some
((fst
default), default_ty
))
3908 (* Special function `Shapes::keyExists` *)
3909 | Class_const
(((), CI
((_, shapes
), _)) as class_id
, ((_, key_exists
) as method_id
))
3910 when shapes
= SN.Shapes.cShapes
&& key_exists
= SN.Shapes.keyExists
->
3911 check_class_function_in_suspend SN.Shapes.cShapes
SN.Shapes.keyExists
;
3912 overload_function p env class_id method_id el uel
3913 begin fun env fty res el
-> match el
with
3915 let env, _te
, shape_ty
= expr
env shape
in
3916 (* try accessing the field, to verify existence, but ignore
3917 * the returned type and keep the one coming from function
3918 * return type hint *)
3919 let env, _ = Typing_shapes.idx
env p fty shape_ty field None
in
3923 (* Special function `Shapes::removeKey` *)
3924 | Class_const
(((), CI
((_, shapes
), _)) as class_id
, ((_, remove_key
) as method_id
))
3925 when shapes
= SN.Shapes.cShapes
&& remove_key
= SN.Shapes.removeKey
->
3926 check_class_function_in_suspend SN.Shapes.cShapes
SN.Shapes.removeKey
;
3927 overload_function p env class_id method_id el uel
3928 begin fun env _ res el
-> match el
with
3929 | [shape
; field
] -> begin match shape
with
3930 | (_, Lvar
(_, lvar
))
3931 | (_, Callconv
(Ast.Pinout
, (_, Lvar
(_, lvar
))))
3932 | (_, Unop
(Ast.Uref
, (_, Lvar
(_, lvar
)))) ->
3933 let env, _te
, shape_ty
= expr ~is_func_arg
:true env shape
in
3935 Typing_shapes.remove_key
p env shape_ty field
in
3936 let env, _ = set_valid_rvalue
p env lvar shape_ty
in
3939 Errors.invalid_shape_remove_key
(fst shape
);
3944 (* Special function `Shapes::toArray` *)
3945 | Class_const
(((), CI
((_, shapes
), _)) as class_id
, ((_, to_array
) as method_id
))
3946 when shapes
= SN.Shapes.cShapes
&& to_array
= SN.Shapes.toArray
->
3947 check_class_function_in_suspend SN.Shapes.cShapes
SN.Shapes.toArray
;
3948 overload_function p env class_id method_id el uel
3949 begin fun env _ res el
-> match el
with
3951 let env, _te
, shape_ty
= expr
env shape
in
3952 Typing_shapes.to_array
env shape_ty res
3956 (* Special function `parent::__construct` *)
3957 | Class_const
(((), CIparent
), ((callee_pos
, construct
) as id))
3958 when construct
= SN.Members.__construct
->
3959 check_class_function_in_suspend "parent" SN.Members.__construct
;
3960 Typing_hooks.dispatch_parent_construct_hook
env callee_pos
;
3961 let env, tel
, tuel
, ty, pty
, ctor_fty =
3962 call_parent_construct
p env el uel
in
3963 make_call env (T.make_typed_expr fpos
ctor_fty
3964 (T.Class_const
((pty
, T.CIparent
), id))) hl tel tuel
ty
3966 (* Calling parent method *)
3967 | Class_const
(((), CIparent
), m
) ->
3968 let env, tcid
, ty1 = static_class_id ~check_constraints
:false p env CIparent
in
3969 if Env.is_static
env
3971 (* in static context, you can only call parent::foo() on static
3974 class_get ~is_method
:true ~is_const
:false ~explicit_tparams
:hl
env ty1 m CIparent
in
3975 let fty = check_abstract_parent_meth
(snd m
) p fty in
3976 check_coroutine_call env fty;
3977 let env, tel
, tuel
, ty =
3978 call ~
expected ~is_expr_statement
3979 ~method_call_info
:(TR.make_call_info ~receiver_is_self
:false
3980 ~is_static
:true ty1 (snd m
))
3982 make_call env (T.make_typed_expr fpos
fty
3983 (T.Class_const
(tcid
, m
))) hl tel tuel
ty
3986 (* in instance context, you can call parent:foo() on static
3987 * methods as well as instance methods *)
3988 if not
(class_contains_smethod
env ty1 m
)
3990 (* parent::nonStaticFunc() is really weird. It's calling a method
3991 * defined on the parent class, but $this is still the child class.
3992 * We can deal with this by hijacking the continuation that
3993 * calculates the SN.Typehints.this type *)
3994 let env, this_ty
= ExprDepTy.make
env CIstatic
3995 (Reason.Rwitness fpos
, TUtils.this_of
(Env.get_self
env)) in
3996 let k_lhs _ = this_ty
in
3997 let env, method_
, _ =
3998 obj_get_ ~is_method
:true ~
nullsafe:None ~pos_params
:(Some el
) ~
valkind:`other
env ty1
4000 begin fun (env, fty, _) ->
4001 let fty = check_abstract_parent_meth
(snd m
) p fty in
4002 check_coroutine_call env fty;
4003 let env, _tel
, _tuel
, method_
= call ~
expected
4004 ~method_call_info
:(TR.make_call_info ~receiver_is_self
:false
4005 ~is_static
:false ty1 (snd m
))
4011 make_call env (T.make_typed_expr fpos this_ty
4012 (T.Class_const
(tcid
, m
))) hl
[] [] method_
4015 class_get ~is_method
:true ~is_const
:false ~explicit_tparams
:hl
env ty1 m CIparent
in
4016 let fty = check_abstract_parent_meth
(snd m
) p fty in
4017 check_coroutine_call env fty;
4018 let env, tel
, tuel
, ty =
4020 ~method_call_info
:(TR.make_call_info ~receiver_is_self
:false
4021 ~is_static
:true ty1 (snd m
))
4023 make_call env (T.make_typed_expr fpos
fty
4024 (T.Class_const
(tcid
, m
))) hl tel tuel
ty
4026 (* Call class method *)
4027 | Class_const
(((), e1
), m
) ->
4028 let env, te1, ty1 = static_class_id ~check_constraints
:true p env e1
in
4030 class_get ~is_method
:true ~is_const
:false ~explicit_tparams
:hl
4031 ~pos_params
:el
env ty1 m e1
in
4032 let () = match e1
with
4033 | CIself
when is_abstract_ft
fty ->
4034 begin match Env.get_self
env with
4035 | _, Tclass
((_, self
), _) ->
4036 (* at runtime, self:: in a trait is a call to whatever
4037 * self:: is in the context of the non-trait "use"-ing
4038 * the trait's code *)
4039 begin match Env.get_class
env self
with
4040 | Some
{ tc_kind
= Ast.Ctrait
; _ } -> ()
4041 | _ -> Errors.self_abstract_call
(snd m
) p (Reason.to_pos
(fst
fty))
4045 | CI
(c, _) when is_abstract_ft
fty ->
4046 Errors.classname_abstract_call
(snd
c) (snd m
) p (Reason.to_pos
(fst
fty))
4047 | CI
((_, classname
), _) ->
4048 begin match Typing_heap.Classes.get classname
with
4050 let (_, method_name
) = m
in
4051 begin match SMap.get method_name class_def
.tc_smethods
with
4054 if elt
.ce_synthesized
then
4055 Errors.static_synthetic_method classname
(snd m
) p (Reason.to_pos
(fst
fty))
4058 (* This technically should be an error, but if we throw here we'll break a ton of our
4059 tests since they reference classes that only exist in www, and any missing classes will
4060 get caught elsewhere in the pipeline. *)
4064 check_coroutine_call env fty;
4065 let env, tel
, tuel
, ty =
4067 ~method_call_info
:(TR.make_call_info ~receiver_is_self
:(e1
= CIself
)
4068 ~is_static
:true ty1 (snd m
))
4069 ~is_expr_statement
p env fty el uel
in
4070 make_call env (T.make_typed_expr fpos
fty
4071 (T.Class_const
(te1, m
))) hl tel tuel
ty
4073 (* Call instance method *)
4074 | Obj_get
(e1
, (pos_id
, Id m
), nullflavor
) ->
4075 let is_method = call_type
= Cnormal
in
4076 let env, te1, ty1 = expr ~accept_using_var
:true env e1
in
4078 (match nullflavor
with
4079 | OG_nullthrows
-> None
4080 | OG_nullsafe
-> Some
p
4082 let tel = ref [] and tuel
= ref [] and tftyl
= ref [] in
4083 let k = (fun (env, fty, _) ->
4084 check_coroutine_call env fty;
4085 let env, tel_
, tuel_
, method_
=
4087 ~method_call_info
:(TR.make_call_info ~receiver_is_self
:false
4088 ~is_static
:false ty1 (snd m
))
4089 ~is_expr_statement
p env fty el uel
in
4090 tel := tel_
; tuel
:= tuel_
;
4091 tftyl
:= fty :: !tftyl
;
4092 env, method_
, None
) in
4093 let env, ty = obj_get ~
is_method ~
nullsafe ~pos_params
:el
4094 ~explicit_tparams
:hl
env ty1 (CIexpr e1
) m
k in
4098 | tftyl
-> (Reason.none
, Tunresolved tftyl
)
4100 make_call env (T.make_typed_expr fpos
tfty (T.Obj_get
(te1,
4101 T.make_typed_expr pos_id
tfty (T.Id m
), nullflavor
))) hl
!tel !tuel
ty
4103 (* Function invocation *)
4105 Typing_hooks.dispatch_id_hook
x env;
4106 let env, fty = fun_type_of_id
env x hl
in
4107 check_coroutine_call env fty;
4108 let env, tel, tuel
, ty =
4109 call ~
expected ~is_expr_statement
p env fty el uel
in
4110 make_call env (T.make_typed_expr fpos
fty (T.Fun_id
x)) hl
tel tuel
ty
4111 | Id
(_, id as x) ->
4112 Typing_hooks.dispatch_id_hook
x env;
4113 let env, fty = fun_type_of_id
env x hl
in
4114 check_coroutine_call env fty;
4115 let env, tel, tuel
, ty =
4116 call ~
expected ~is_expr_statement
p env fty el uel
in
4117 if id = SN.Rx.mutable_
then begin
4118 Typing_mutability.check_rx_mutable_arguments
p env tel;
4119 if not
(Env.env_local_reactive
env) then
4120 Errors.mutable_in_nonreactive_context
p;
4122 make_call env (T.make_typed_expr fpos
fty (T.Id
x)) hl
tel tuel
ty
4124 let env, te, fty = expr
env e in
4125 check_coroutine_call env fty;
4126 let env, tel, tuel
, ty =
4127 call ~
expected ~is_expr_statement
p env fty el uel
in
4128 make_call env te hl
tel tuel
ty
4130 and fun_type_of_id
env x hl
=
4131 Typing_hooks.dispatch_fun_id_hook
x;
4133 match Env.get_fun
env (snd
x) with
4134 | None
-> let env, _, ty = unbound_name env x in env, ty
4136 let ety_env = Phase.env_with_self
env in
4137 let env, fty = Phase.localize_ft ~use_pos
:(fst
x) ~explicit_tparams
:hl ~
ety_env env fty in
4138 env, (Reason.Rwitness
fty.ft_pos
, Tfun
fty)
4142 (*****************************************************************************)
4143 (* Function type-checking expressions accessing an array (example: $x[...]).
4144 * The parameter is_lvalue is true when the expression is on the left hand
4145 * side of an assignment (example: $x[...] = 0).
4147 (*****************************************************************************)
4148 and array_get ?
(lhs_of_null_coalesce
=false) is_lvalue p env ty1 e2
ty2 =
4149 (* This is a little weird -- we enforce the right arity when you use certain
4150 * collections, even in partial mode (where normally completely omitting the
4151 * type parameter list is admitted). Basically the "omit type parameter"
4152 * hole was for compatibility with certain interfaces like ArrayAccess, not
4153 * for collections! But it's hard to go back on now, so since we've always
4154 * errored (with an inscrutable error message) when you try to actually use
4155 * a collection with omitted type parameters, we can continue to error and
4156 * give a more useful error message. *)
4157 let env, ety1
= Env.expand_type
env ty1 in
4158 let arity_error (_, name) =
4159 Errors.array_get_arity
p name (Reason.to_pos
(fst ety1
)) in
4160 let nullable_container_get ty =
4161 if lhs_of_null_coalesce
4162 (* Normally, we would not allow indexing into a nullable container,
4163 however, because the pattern shows up so frequently, we are allowing
4164 indexing into a nullable container as long as it is on the lhs of a
4167 array_get ~lhs_of_null_coalesce
is_lvalue p env ty e2
ty2
4169 Errors.null_container
p
4171 "This is what makes me believe it can be null"
4174 env, (Reason.Rwitness
p, Typing_utils.terr
env)
4177 | Tunresolved
tyl ->
4178 let env, tyl = List.map_env
env tyl begin fun env ty1 ->
4179 array_get ~lhs_of_null_coalesce
is_lvalue p env ty1 e2
ty2
4181 env, (fst ety1
, Tunresolved
tyl)
4182 | Tarraykind
(AKvarray
ty | AKvec
ty) ->
4183 let ty1 = Reason.Ridx
(fst e2
, fst ety1
), Tprim Tint
in
4184 let env = Type.sub_type
p Reason.index_array
env ty2 ty1 in
4186 | Tarraykind
(AKvarray_or_darray
ty) ->
4187 let ty1 = Reason.Rvarray_or_darray_key
p, Tprim Tarraykey
in
4188 let env = Type.sub_type
p Reason.index_array
env ty2 ty1 in
4190 | Tclass
((_, cn
) as id, argl
)
4191 when cn
= SN.Collections.cVector
4192 || cn
= SN.Collections.cVec
->
4193 let ty = match argl
with
4195 | _ -> arity_error id; err_witness env p in
4196 let ty1 = Reason.Ridx_vector
(fst e2
), Tprim Tint
in
4197 let env = Type.sub_type
p (Reason.index_class cn
) env ty2 ty1 in
4199 | Tclass
((_, cn
) as id, argl
)
4200 when cn
= SN.Collections.cMap
4201 || cn
= SN.Collections.cStableMap
4202 || cn
= SN.Collections.cDict
4203 || cn
= SN.Collections.cKeyset
->
4204 if cn
= SN.Collections.cKeyset
&& is_lvalue then begin
4205 Errors.keyset_set
p (Reason.to_pos
(fst ety1
));
4206 env, (Reason.Rwitness
p, Typing_utils.terr
env)
4208 let (k, v
) = match argl
with
4209 | [t
] when cn
= SN.Collections.cKeyset
-> (t
, t
)
4210 | [k; v
] when cn
<> SN.Collections.cKeyset
-> (k, v
)
4213 let any = err_witness env p in
4216 let env, ty2 = Env.unbind
env ty2 in
4217 let env = Type.sub_type
p (Reason.index_class cn
) env ty2 k in
4219 (* Certain container/collection types are intended to be immutable/const,
4220 * thus they should never appear as a lvalue when indexing i.e.
4222 * $x[0] = 100; // ERROR
4225 | Tclass
((_, cn
) as id, argl
)
4226 when cn
= SN.Collections.cConstMap
4227 || cn
= SN.Collections.cImmMap
4228 || cn
= SN.Collections.cIndexish
4229 || cn
= SN.Collections.cKeyedContainer
->
4231 error_const_mutation
env p ety1
4233 let (k, v
) = match argl
with
4237 let any = err_witness env p in
4240 let env = Type.sub_type
p (Reason.index_class cn
) env ty2 k in
4242 | Tclass
((_, cn
) as id, argl
)
4243 when not
is_lvalue &&
4244 (cn
= SN.Collections.cConstVector
|| cn
= SN.Collections.cImmVector
) ->
4245 let ty = match argl
with
4247 | _ -> arity_error id; err_witness env p in
4248 let ty1 = Reason.Ridx
(fst e2
, fst ety1
), Tprim Tint
in
4249 let env = Type.sub_type
p (Reason.index_class cn
) env ty2 ty1 in
4251 | Tclass
((_, cn
), _)
4253 (cn
= SN.Collections.cConstVector
|| cn
= SN.Collections.cImmVector
) ->
4254 error_const_mutation
env p ety1
4255 | Tarraykind
(AKdarray
(k, v
) | AKmap
(k, v
)) ->
4256 let env, ty2 = Env.unbind
env ty2 in
4257 let env = Type.sub_type
p Reason.index_array
env ty2 k in
4259 | Tarraykind
((AKshape
_ | AKtuple
_) as akind
) ->
4260 let key = Typing_arrays.static_array_access
env (Some e2
) in
4261 let env, result
= match key, akind
with
4262 | Typing_arrays.AKtuple_index index
, AKtuple fields
->
4263 begin match IMap.get index fields
with
4265 let ty1 = Reason.Ridx
(fst e2
, fst ety1
), Tprim Tint
in
4266 let env = Type.sub_type
p Reason.index_array
env ty2 ty1 in
4270 | Typing_arrays.AKshape_key
field_name, AKshape fdm
->
4271 begin match Nast.ShapeMap.get
field_name fdm
with
4273 let env, ty2 = Env.unbind
env ty2 in
4274 let env = Type.sub_type
p Reason.index_array
env ty2 k in
4279 begin match result
with
4280 | Some
ty -> env, ty
4282 (* Key is dynamic, or static and not in the array - treat it as
4283 regular map or vec like array *)
4284 let env, ty1 = Typing_arrays.downcast_aktypes
env ety1
in
4285 array_get
is_lvalue p env ty1 e2
ty2
4287 | Terr
-> env, (Reason.Rwitness
p, Typing_utils.terr
env)
4288 | Tdynamic
-> env, ety1
4289 | Tany
| Tarraykind
(AKany
| AKempty
) ->
4290 env, (Reason.Rnone
, Typing_utils.tany env)
4292 let ty = Reason.Rwitness
p, Tprim Tstring
in
4293 let int = Reason.Ridx
(fst e2
, fst ety1
), Tprim Tint
in
4294 let env = Type.sub_type
p Reason.index_array
env ty2 int in
4300 let idx = int_of_string n
in
4301 let nth = List.nth_exn
tyl idx in
4304 Errors.typing_error
p (Reason.string_of_ureason
Reason.index_tuple
);
4305 env, (Reason.Rwitness
p, Typing_utils.terr
env)
4308 Errors.typing_error
p (Reason.string_of_ureason
Reason.URtuple_access
);
4309 env, (Reason.Rwitness
p, Typing_utils.terr
env)
4311 | Tclass
((_, cn
) as id, argl
) when cn
= SN.Collections.cPair
->
4312 let (ty1, ty2) = match argl
with
4313 | [ty1; ty2] -> (ty1, ty2)
4316 let any = err_witness env p in
4322 let idx = int_of_string n
in
4323 let nth = List.nth_exn
[ty1; ty2] idx in
4326 Errors.typing_error
p @@
4327 Reason.string_of_ureason
(Reason.index_class cn
);
4328 env, (Reason.Rwitness
p, Typing_utils.terr
env)
4331 Errors.typing_error
p (Reason.string_of_ureason
Reason.URpair_access
);
4332 env, (Reason.Rwitness
p, Typing_utils.terr
env)
4334 | Tshape
(_, fdm
) ->
4336 (match TUtils.shape_field_name
env e2
with
4338 (* there was already an error in shape_field name,
4339 don't report another one for a missing field *)
4340 env, (Reason.Rwitness
p, Typing_utils.terr
env)
4341 | Some field
-> (match ShapeMap.get field fdm
with
4343 Errors.undefined_field
4345 ~
name:(TUtils.get_printable_shape_field_name field
)
4346 ~shape_type_pos
:(Reason.to_pos
(fst ety1
));
4347 env, (Reason.Rwitness
p, Typing_utils.terr
env)
4348 | Some
{ sft_optional
= true; _ }
4349 when not
is_lvalue && not lhs_of_null_coalesce
->
4350 let declared_field =
4352 ~
f:(fun x -> Ast.ShapeField.compare field
x = 0)
4353 (ShapeMap.keys fdm
) in
4354 let declaration_pos = match declared_field with
4355 | Ast.SFlit
(p, _) | Ast.SFclass_const
((p, _), _) -> p in
4356 Errors.array_get_with_optional_field
4359 (TUtils.get_printable_shape_field_name field
);
4360 env, (Reason.Rwitness
p, Typing_utils.terr
env)
4361 | Some
{ sft_optional
= _; sft_ty
} -> env, sft_ty
)
4363 | Toption
ty -> nullable_container_get ty
4364 | Tprim
Nast.Tvoid
when TUtils.is_void_type_of_null
env ->
4365 nullable_container_get (Reason.Rnone
, Tany
)
4367 if Env.is_strict
env
4368 then error_array
env p ety1
4369 else env, (Reason.Rwitness
p, Typing_utils.tany env)
4370 | Tabstract
(AKnewtype
(ts
, [ty]), Some
(r, Tshape
(fk
, fields
)))
4371 when ts
= SN.FB.cTypeStructure
->
4372 let env, fields
= TS.transform_shapemap
env ty fields
in
4373 let ty = r, Tshape
(fk
, fields
) in
4374 array_get ~lhs_of_null_coalesce
is_lvalue p env ty e2
ty2
4377 try_over_concrete_supertypes env ety1
4379 array_get ~lhs_of_null_coalesce
is_lvalue p env ty e2
ty2
4381 begin match resl with
4384 when List.for_all
rest ~
f:(fun x -> ty_equal
(snd
x) (snd res
)) -> res
4385 | _ -> error_array
env p ety1
4387 | Tmixed
| Tnonnull
| Tprim
_ | Tvar
_ | Tfun
_
4388 | Tclass
(_, _) | Tanon
(_, _) ->
4389 error_array
env p ety1
4391 and array_append
p env ty1 =
4392 let env, ty1 = TUtils.fold_unresolved
env ty1 in
4393 let resl = try_over_concrete_supertypes env ty1
4398 | Tany
| Tarraykind
(AKany
| AKempty
) ->
4399 env, (r, Typing_utils.tany env)
4402 env, (r, Typing_utils.terr
env)
4403 (* No reactive append on vector and set *)
4404 | Tclass
((_, n
), [ty])
4405 when (n
= SN.Collections.cVector
4406 || n
= SN.Collections.cSet
) ->
4407 Env.error_if_reactive_context
env @@ begin fun () ->
4408 Errors.nonreactive_append
p;
4412 | Tclass
((_, n
), [ty])
4413 when n
= SN.Collections.cVec
|| n
= SN.Collections.cKeyset
->
4415 | Tclass
((_, n
), [])
4416 when n
= SN.Collections.cVector
|| n
= SN.Collections.cSet
->
4417 (* Handle the case where "Vector" or "Set" was used as a typehint
4418 without type parameters *)
4419 env, (r, Typing_utils.tany env)
4420 | Tclass
((_, n
), [tkey
; tvalue
]) when n
= SN.Collections.cMap
->
4421 Env.error_if_reactive_context
env @@ begin fun () ->
4422 Errors.nonreactive_append
p;
4424 (* You can append a pair to a map *)
4425 env, (Reason.Rmap_append
p, Tclass
((p, SN.Collections.cPair
),
4427 | Tclass
((_, n
), []) when n
= SN.Collections.cMap
->
4428 Env.error_if_reactive_context
env @@ begin fun () ->
4429 Errors.nonreactive_append
p;
4431 (* Handle the case where "Map" was used as a typehint without
4433 env, (Reason.Rmap_append
p,
4434 Tclass
((p, SN.Collections.cPair
), []))
4435 | Tarraykind
(AKvec
ty | AKvarray
ty) ->
4437 | Tdynamic
-> env, ty
4439 if Env.is_strict
env
4440 then error_array_append
env p ty1
4441 else env, (Reason.Rwitness
p, Typing_utils.tany env)
4442 | Tmixed
| Tnonnull
| Tarraykind
_ | Toption
_ | Tprim
_
4443 | Tvar
_ | Tfun
_ | Tclass
(_, _) | Ttuple
_
4444 | Tanon
(_, _) | Tunresolved
_ | Tshape
_ | Tabstract
_ ->
4445 error_array_append
env p ty1
4449 | _ -> error_array_append
env p ty1
4452 and error_array
env p (r, ty) =
4453 Errors.array_access
p (Reason.to_pos
r) (Typing_print.error
ty);
4454 env, err_witness env p
4456 and error_array_append
env p (r, ty) =
4457 Errors.array_append
p (Reason.to_pos
r) (Typing_print.error
ty);
4458 env, err_witness env p
4460 and error_const_mutation
env p (r, ty) =
4461 Errors.const_mutation
p (Reason.to_pos
r) (Typing_print.error
ty);
4462 env, err_witness env p
4465 * Checks if a class (given by cty) contains a given static method.
4467 * We could refactor this + class_get
4469 and class_contains_smethod
env cty
(_pos
, mid
) =
4470 let lookup_member ty =
4472 | _, Tclass
((_, c), _) ->
4473 (match Env.get_class
env c with
4476 Option.is_some
@@ Env.get_static_member
true env class_ mid
4479 let _env, tyl = TUtils.get_concrete_supertypes
env cty
in
4480 List.exists
tyl ~
f:lookup_member
4482 and class_get ~
is_method ~is_const ?
(explicit_tparams
=[]) ?
(incl_tc
=false)
4483 ?
(pos_params
: expr list
option) env cty
(p, mid
) cid =
4486 this_for_method
env cid cty
4490 type_expansions
= [];
4492 substs
= SMap.empty
;
4493 from_class
= Some
cid;
4494 validate_dty
= None
;
4496 class_get_ ~
is_method ~is_const ~
ety_env ~explicit_tparams ~incl_tc
4497 ~pos_params
env cid cty
(p, mid
)
4499 and class_get_ ~
is_method ~is_const ~
ety_env ?
(explicit_tparams
=[])
4500 ?
(incl_tc
=false) ~pos_params
env cid cty
4502 let env, cty
= Env.expand_type
env cty
in
4504 | r, Tany
-> env, (r, Typing_utils.tany env), None
4505 | r, Terr
-> env, err_witness env (Reason.to_pos
r), None
4506 | _, Tdynamic
-> env, cty
, None
4507 | _, Tunresolved
tyl ->
4508 let env, tyl = List.map_env
env tyl begin fun env ty ->
4510 class_get_ ~
is_method ~is_const ~
ety_env ~explicit_tparams ~incl_tc
4511 ~pos_params
env cid ty (p, mid
)
4514 let env, method_
= TUtils.in_var
env (fst cty
, Tunresolved
tyl) in
4516 | _, Tabstract
(_, Some
ty) ->
4517 class_get_ ~
is_method ~is_const ~
ety_env ~explicit_tparams ~incl_tc
4518 ~pos_params
env cid ty (p, mid
)
4519 | _, Tabstract
(_, None
) ->
4520 let resl = try_over_concrete_supertypes env cty
(fun env ty ->
4521 class_get_ ~
is_method ~is_const ~
ety_env ~explicit_tparams ~incl_tc
4522 ~pos_params
env cid ty (p, mid
)) in
4523 begin match resl with
4525 Errors.non_class_member
4526 mid
p (Typing_print.error
(snd cty
))
4527 (Reason.to_pos
(fst cty
));
4528 (env, err_witness env p, None
)
4529 | ((_, (_, ty), _) as res
)::rest ->
4530 if List.exists
rest (fun (_, (_, ty'
), _) -> ty'
<> ty)
4533 Errors.ambiguous_member
4534 mid
p (Typing_print.error
(snd cty
))
4535 (Reason.to_pos
(fst cty
));
4536 (env, err_witness env p, None
)
4540 | _, Tclass
((_, c), paraml
) ->
4541 let class_ = Env.get_class
env c in
4543 | None
-> env, (Reason.Rwitness
p, Typing_utils.tany env), None
4545 Typing_hooks.dispatch_smethod_hook
class_ paraml ~pos_params
(p, mid
)
4546 env ety_env.from_class ~
is_method ~is_const
;
4547 (* We need to instantiate generic parameters in the method signature *)
4550 substs
= Subst.make
class_.tc_tparams paraml
} in
4551 if is_const
then begin
4553 if incl_tc
then Env.get_const
env class_ mid
else
4554 match Env.get_typeconst
env class_ mid
with
4556 Errors.illegal_typeconst_direct_access
p;
4559 Env.get_const
env class_ mid
4563 smember_not_found
p ~is_const ~
is_method class_ mid
;
4564 env, (Reason.Rnone
, Typing_utils.terr
env), None
4565 | Some
{ cc_type
; cc_abstract
; cc_pos
; _ } ->
4566 let env, cc_type
= Phase.localize ~
ety_env env cc_type
in
4569 then Some
(cc_pos
, class_.tc_name ^
"::" ^ mid
)
4572 let smethod = Env.get_static_member
is_method env class_ mid
in
4575 (match Env.get_static_member
is_method env class_
4576 SN.Members.__callStatic
with
4578 smember_not_found
p ~is_const ~
is_method class_ mid
;
4579 env, (Reason.Rnone
, Typing_utils.terr
env), None
4580 | Some
{ce_visibility
= vis
; ce_type
= lazy (r, Tfun
ft); _} ->
4581 let p_vis = Reason.to_pos
r in
4582 TVis.check_class_access
p env (p_vis, vis
) cid class_;
4584 Phase.localize_ft ~use_pos
:p ~
ety_env ~explicit_tparams
:explicit_tparams
env ft in
4585 let arity_pos = match ft.ft_params
with
4586 | [_; { fp_pos
; fp_kind
= FPnormal
; _ }] -> fp_pos
4587 (* we should really assert here but this is not yet validated *)
4590 ft_arity
= Fellipsis
(0, arity_pos);
4591 ft_tparams
= []; ft_params
= [];
4593 env, (r, Tfun
ft), None
4594 | _ -> assert false)
4595 | Some
{ ce_visibility
= vis
; ce_type
= lazy method_
; _ } ->
4596 let p_vis = Reason.to_pos
(fst method_
) in
4597 TVis.check_class_access
p env (p_vis, vis
) cid class_;
4599 begin match method_
with
4600 (* We special case Tfun here to allow passing in explicit tparams to localize_ft. *)
4603 Phase.localize_ft ~use_pos
:p ~
ety_env ~explicit_tparams
:explicit_tparams
env ft
4604 in env, (r, Tfun
ft)
4605 | _ -> Phase.localize ~
ety_env env method_
4610 | _, (Tmixed
| Tnonnull
| Tarraykind
_ | Toption
_
4611 | Tprim
_ | Tvar
_ | Tfun
_ | Ttuple
_ | Tanon
(_, _) | Tobject
4613 (* should never happen; static_class_id takes care of these *)
4614 env, (Reason.Rnone
, Typing_utils.tany env), None
4616 and smember_not_found
pos ~is_const ~
is_method class_ member_name
=
4618 if is_const
then `class_constant
4619 else if is_method then `static_method
4620 else `class_variable
in
4622 let cid = (class_.tc_pos
, class_.tc_name
) in
4623 Errors.smember_not_found
kind pos cid member_name hint
4625 match Env.suggest_static_member
is_method class_ member_name
with
4627 (match Env.suggest_member
is_method class_ member_name
with
4628 | None
when not
class_.tc_members_fully_known
->
4629 (* no error in this case ... the member might be present
4630 * in one of the parents of class_ that the typing cannot see *)
4635 error (`closest
(pos2
, v
))
4638 error (`did_you_mean
(pos2
, v
))
4640 and member_not_found
pos ~
is_method class_ member_name
r =
4641 let kind = if is_method then `method_
else `member
in
4642 let cid = class_.tc_pos
, class_.tc_name
in
4643 let reason = Reason.to_string
4644 ("This is why I think it is an object of type "^strip_ns
class_.tc_name
) r
4647 Errors.member_not_found
kind pos cid member_name hint
reason in
4648 match Env.suggest_member
is_method class_ member_name
with
4650 (match Env.suggest_static_member
is_method class_ member_name
with
4651 | None
when not
class_.tc_members_fully_known
->
4652 (* no error in this case ... the member might be present
4653 * in one of the parents of class_ that the typing cannot see *)
4657 | Some
(def_pos
, v
) ->
4658 error (`closest
(def_pos
, v
))
4660 | Some
(def_pos
, v
) ->
4661 error (`did_you_mean
(def_pos
, v
))
4663 and obj_get ~
is_method ~
nullsafe ?
(valkind = `other
) ?
(explicit_tparams
=[])
4664 ?
(pos_params
: expr list
option) env ty1 cid id k =
4667 | Some
p when not
(type_could_be_null
env ty1) ->
4668 let env, (r, _) = Env.expand_type
env ty1 in
4669 Errors.nullsafe_not_needed
p
4671 "This is what makes me believe it cannot be null" r);
4674 let env, method_
, _ =
4675 obj_get_with_visibility ~
is_method ~
nullsafe ~
valkind ~pos_params
4676 ~explicit_tparams
env ty1 cid id k in
4679 and obj_get_with_visibility ~
is_method ~
nullsafe ~
valkind ~pos_params
4680 ?
(explicit_tparams
=[]) env ty1 cid id k =
4681 obj_get_ ~
is_method ~
nullsafe ~
valkind ~pos_params ~explicit_tparams
env ty1
4682 cid id k (fun ty -> ty)
4684 (* We know that the receiver is a concrete class: not a generic with
4685 * bounds, or a Tunresolved. *)
4686 and obj_get_concrete_ty ~
is_method ~
valkind ~pos_params ?
(explicit_tparams
=[])
4687 env concrete_ty class_id
(id_pos
, id_str
as id) k_lhs =
4688 let default () = env, (Reason.Rwitness id_pos
, Typing_utils.tany env), None
in
4689 let mk_ety_env r class_info x paraml
=
4690 let this_ty = k_lhs (r, (Tclass
(x, paraml
))) in
4692 type_expansions
= [];
4694 substs
= Subst.make
class_info.tc_tparams paraml
;
4695 from_class
= Some class_id
;
4696 validate_dty
= None
;
4699 match concrete_ty
with
4700 | (r, Tclass
(x, paraml
)) ->
4701 begin match Env.get_class
env (snd
x) with
4705 | Some
class_info when not
is_method
4706 && not
(Env.is_strict
env)
4707 && class_info.tc_name
= SN.Classes.cStdClass
->
4710 | Some
class_info ->
4712 if List.length
paraml = 0
4713 then List.map
class_info.tc_tparams
4714 (fun _ -> Reason.Rwitness id_pos
, Typing_utils.tany env)
4716 let old_member_info = Env.get_member
is_method env class_info id_str
in
4717 let self = Env.get_self_id
env in
4718 let member_info, shadowed
= if SMap.mem
self class_info.tc_ancestors
4720 (* We look up the current context to see if there is a field/method with
4721 * private visibility. If there is one, that one takes precedence *)
4722 begin match Env.get_class
env self with
4723 | None
-> old_member_info, false
4724 | Some self_class
->
4725 match Env.get_member
is_method env self_class id_str
with
4726 | Some
{ ce_visibility
= Vprivate
_; _ } as member_info ->
4728 | _ -> old_member_info, false
4730 else old_member_info, false
4732 Typing_hooks.dispatch_cmethod_hook
class_info paraml ~pos_params
id
4733 env (Some class_id
) ~
is_method;
4735 begin match member_info with
4736 | None
when not
is_method ->
4737 if not
(SN.Members.is_special_xhp_attribute id_str
)
4738 then member_not_found id_pos ~
is_method class_info id_str
r;
4742 begin match Env.get_member
is_method env class_info SN.Members.__call
with
4744 member_not_found id_pos ~
is_method class_info id_str
r;
4747 | Some
{ce_visibility
= vis
; ce_type
= lazy (r, Tfun
ft); _} ->
4748 let mem_pos = Reason.to_pos
r in
4749 TVis.check_obj_access id_pos
env (mem_pos, vis
);
4751 (* the return type of __call can depend on the class params or be this *)
4752 let ety_env = mk_ety_env r class_info x paraml in
4753 let env, ft = Phase.localize_ft ~use_pos
:id_pos ~
ety_env env ft in
4755 let arity_pos = match ft.ft_params
with
4756 | [_; { fp_pos
; fp_kind
= FPnormal
; _ }] -> fp_pos
4757 (* we should really assert here but this is not yet validated *)
4760 (* we change the params of the underlying declaration to act as a
4761 * variadic function ... this transform cannot be done when processing
4762 * the declaration of call because direct calls to $inst->__call are also
4766 ft_arity
= Fellipsis
(0, arity_pos); ft_tparams
= []; ft_params
= []; } in
4768 let member_ty = (r, Tfun
ft) in
4769 env, member_ty, Some
(mem_pos, vis
)
4773 end (* match Env.get_member is_method env class_info SN.Members.__call *)
4775 | Some
({ce_visibility
= vis
; ce_type
= lazy member_
; _ } as member_ce
) ->
4776 let mem_pos = Reason.to_pos
(fst member_
) in
4777 if shadowed
then begin match old_member_info with
4778 | Some
({ce_visibility
= old_vis
; ce_type
= lazy old_member
; _ }) ->
4779 let old_mem_pos = Reason.to_pos
(fst old_member
) in
4780 begin match class_id
with
4781 | CIexpr
(_, This
) when snd
x = self -> ()
4782 | _ -> Errors.ambiguous_object_access
4783 id_pos id_str
mem_pos (TUtils.string_of_visibility old_vis
) old_mem_pos self (snd
x)
4787 TVis.check_obj_access id_pos
env (mem_pos, vis
);
4788 let member_ty = Typing_enum.member_type
env member_ce
in
4789 let ety_env = mk_ety_env r class_info x paraml in
4790 let env, member_ty =
4791 begin match member_ty with
4793 (* We special case function types here to be able to pass explicit type
4796 Phase.localize_ft ~use_pos
:id_pos ~explicit_tparams ~
ety_env env ft in
4798 | _ -> Phase.localize ~
ety_env env member_ty
4801 if member_ce
.ce_const
&& valkind = `lvalue
then
4802 if not
(env.Env.inside_constructor
&&
4803 (* expensive call behind short circuiting && *)
4804 SubType.is_sub_type
env (Env.get_self
env) concrete_ty
) then
4805 Errors.assigning_to_const id_pos
;
4807 env, member_ty, Some
(mem_pos, vis
)
4808 end (* match member_info *)
4810 end (* match Env.get_class env (snd x) *)
4812 let ty = Reason.Rdynamic_prop id_pos
, Tdynamic
in
4820 Errors.non_object_member
4821 id_str id_pos
(Typing_print.error (snd concrete_ty
))
4822 (Reason.to_pos
(fst concrete_ty
));
4826 (* k_lhs takes the type of the object receiver *)
4827 and obj_get_ ~
is_method ~
nullsafe ~
valkind ~
(pos_params
: expr list
option) ?
(explicit_tparams
=[])
4828 env ty1 cid (id_pos
, id_str
as id) k k_lhs =
4829 let env, ety1
= Env.expand_type
env ty1 in
4830 let nullable_obj_get ty = match nullsafe with
4832 let env, method_
, x = obj_get_ ~
is_method ~
nullsafe ~
valkind
4833 ~pos_params ~explicit_tparams
env ty cid id k k_lhs in
4834 let env, method_
= TUtils.non_null
env method_
in
4835 env, (Reason.Rnullsafe_op
p1, Toption method_
), x
4837 Errors.null_member id_str id_pos
4839 "This is what makes me believe it can be null"
4842 k (env, (fst ety1
, Typing_utils.terr
env), None
) in
4844 | _, Tunresolved
tyl ->
4845 let (env, vis
), tyl = List.map_env
(env, None
) tyl
4846 begin fun (env, vis
) ty ->
4848 obj_get_ ~
is_method ~
nullsafe ~
valkind ~pos_params
4849 ~explicit_tparams
env ty cid id k k_lhs in
4850 (* There is one special case where we need to expose the
4851 * visibility outside of obj_get (checkout inst_meth special
4853 * We keep a witness of the "most restrictive" visibility
4854 * we encountered (position + visibility), to be able to
4855 * special case inst_meth.
4857 let vis = TVis.min_vis_opt
vis vis'
in
4860 let env, method_
= TUtils.in_var
env (fst ety1
, Tunresolved
(tyl)) in
4863 | p'
, (Tabstract
(ak
, Some
ty)) ->
4864 let k_lhs'
ty = match ak
with
4865 | AKnewtype
(_, _) -> k_lhs ty
4866 | _ -> k_lhs (p'
, Tabstract
(ak
, Some
ty)) in
4867 obj_get_ ~
is_method ~
nullsafe ~
valkind ~pos_params ~explicit_tparams
env ty cid id k k_lhs'
4869 | p'
, (Tabstract
(ak
,_)) ->
4871 try_over_concrete_supertypes env ety1
4873 (* We probably don't want to rewrap new types for the 'this' closure *)
4874 (* TODO AKENN: we shouldn't refine constraints by changing
4875 * the type like this *)
4876 let k_lhs'
ty = match ak
with
4877 | AKnewtype
(_, _) -> k_lhs ty
4878 | _ -> k_lhs (p'
, Tabstract
(ak
, Some
ty)) in
4879 obj_get_concrete_ty ~
is_method ~
valkind ~pos_params ~explicit_tparams
env ty cid id k_lhs'
4881 begin match resl with
4883 Errors.non_object_member
4884 id_str id_pos
(Typing_print.error (snd ety1
))
4885 (Reason.to_pos
(fst ety1
));
4886 k (env, err_witness env id_pos
, None
)
4888 | ((_env, (_, ty), _vis
) as res
)::rest ->
4889 if List.exists
rest (fun (_, (_,ty'
), _) -> ty'
<> ty)
4892 Errors.ambiguous_member
4893 id_str id_pos
(Typing_print.error (snd ety1
))
4894 (Reason.to_pos
(fst ety1
));
4895 k (env, err_witness env id_pos
, None
)
4900 | _, Toption
ty -> nullable_obj_get ty
4901 | r, Tprim
Nast.Tvoid
when TUtils.is_void_type_of_null
env ->
4902 nullable_obj_get (r, Tany
)
4904 k (obj_get_concrete_ty ~
is_method ~
valkind ~pos_params ~explicit_tparams
env ety1
cid id k_lhs)
4907 (* Return true if the type ty1 contains the null value *)
4908 and type_could_be_null
env ty1 =
4909 let _, tyl = TUtils.get_concrete_supertypes
env ty1 in
4913 | Toption
_ | Tunresolved
_ | Tmixed
| Tany
| Terr
| Tdynamic
-> true
4914 | Tprim Tvoid
-> TUtils.is_void_type_of_null
env
4915 | Tarraykind
_ | Tprim
_ | Tvar
_ | Tfun
_ | Tabstract
_
4916 | Tclass
(_, _) | Ttuple
_ | Tanon
(_, _) | Tobject
4917 | Tshape
_ | Tnonnull
-> false)
4919 and class_id_for_new
p env cid =
4920 let env, te, ty = static_class_id ~check_constraints
:false p env cid in
4921 (* Need to deal with union case *)
4922 let rec get_info res
tyl =
4924 | [] -> env, te, res
4927 | Tunresolved
tyl'
->
4928 get_info res
(tyl'
@ tyl)
4930 (* Instantiation on an abstract class (e.g. from classname<T>) is
4931 * via the base type (to check constructor args), but the actual
4932 * type `ty` must be preserved. *)
4933 match TUtils.get_base_type
env ty with
4934 | _, Tclass
(sid
, _) ->
4936 let class_ = Env.get_class
env (snd sid
) in
4938 | None
-> get_info res
tyl
4939 | Some
class_info -> get_info ((sid
, class_info, ty)::res
) tyl
4941 | _, (Tany
| Terr
| Tmixed
| Tnonnull
| Tarraykind
_ | Toption
_
4942 | Tprim
_ | Tvar
_ | Tfun
_ | Tabstract
(_, _) | Ttuple
_
4943 | Tanon
(_, _) | Tunresolved
_ | Tobject
| Tshape
_ | Tdynamic
) ->
4947 (* To be a valid trait declaration, all of its 'require extends' must
4948 * match; since there's no multiple inheritance, it follows that all of
4949 * the 'require extends' must belong to the same inheritance hierarchy
4950 * and one of them should be the child of all the others *)
4951 and trait_most_concrete_req_class trait
env =
4952 List.fold_left trait
.tc_req_ancestors ~
f:begin fun acc
(_p
, ty) ->
4953 let _r, (_p
, name), _paraml
= TUtils.unwrap_class_type
ty in
4954 let keep = match acc
with
4955 | Some
(c, _ty
) -> SMap.mem
name c.tc_ancestors
4960 let class_ = Env.get_class
env name in
4963 | Some
{ tc_kind
= Ast.Cinterface
; _ } -> acc
4964 | Some
{ tc_kind
= Ast.Ctrait
; _ } ->
4965 (* this is an error case for which the nastCheck spits out
4966 * an error, but does *not* currently remove the offending
4967 * 'require extends' or 'require implements' *)
4969 | Some
c -> Some
(c, ty)
4973 (* If there are no explicit type arguments then generate fresh type variables
4974 * for all of them. Otherwise, check the arity, and use the explicit types. *)
4975 and resolve_type_arguments
env p class_id tparaml hintl
=
4976 (* For explicit type arguments we support a wildcard syntax `_` for which
4977 * Hack will generate a fresh type variable *)
4978 let resolve_type_argument env hint
=
4980 | (_, Happly
((_, id), [])) when id = SN.Typehints.wildcard
->
4981 Env.fresh_unresolved_type
env
4983 Phase.hint_locl
env hint
in
4984 let length_hintl = List.length hintl
in
4985 let length_tparaml = List.length tparaml
in
4986 if length_hintl <> length_tparaml
4988 if length_hintl <> 0
4989 then Errors.type_arity
p (snd class_id
) (string_of_int
length_tparaml);
4990 List.map_env
env tparaml
begin fun env _ ->
4991 Env.fresh_unresolved_type
env end
4994 List.map_env
env hintl
resolve_type_argument
4996 (* Do all of the above, and also check any constraints associated with the type parameters.
4998 and resolve_type_arguments_and_check_constraints ~check_constraints
4999 env p class_id from_class tparaml hintl
=
5000 let env, type_argl
= resolve_type_arguments
env p class_id tparaml hintl
in
5001 let this_ty = (Reason.Rwitness
(fst class_id
), Tclass
(class_id
, type_argl
)) in
5003 if check_constraints
5004 then let ety_env = {
5005 type_expansions
= [];
5007 substs
= Subst.make tparaml type_argl
;
5008 from_class
= Some from_class
;
5009 validate_dty
= None
;
5011 Phase.check_tparams_constraints ~use_pos
:p ~
ety_env env tparaml
5015 (* When invoking a method the class_id is used to determine what class we
5016 * lookup the method in, but the type of 'this' will be the late bound type.
5020 * public static function get(): this { return new static(); }
5022 * public static function alias(): this { return self::get(); }
5025 * In C::alias, when we invoke self::get(), 'self' is resolved to the class
5026 * in the lexical scope (C), so call C::get. However the method is executed in
5027 * the current context, so static inside C::get will be resolved to the late
5028 * bound type (get_called_class() within C::alias).
5030 * This means when determining the type of this, CIparent and CIself should be
5031 * changed to CIstatic. For the other cases of C::get() or $c::get(), we only
5032 * look at the left hand side of the '::' and use the type type associated
5035 * Thus C::get() will return a type C, while $c::get() will return the same
5038 and this_for_method
env cid default_ty
= match cid with
5039 | CIparent
| CIself
| CIstatic
->
5040 let p = Reason.to_pos
(fst default_ty
) in
5041 let env, _te
, ty = static_class_id ~check_constraints
:false p env CIstatic
in
5042 ExprDepTy.make
env CIstatic
ty
5046 and static_class_id ~check_constraints
p env =
5047 let make_result env te ty =
5048 env, (ty, te), ty in
5051 (match Env.get_self
env with
5052 | _, Tclass
((_, self), _) ->
5053 (match Env.get_class
env self with
5055 {tc_kind
= Ast.Ctrait
; _}
5057 (match trait_most_concrete_req_class trait
env with
5059 Errors.parent_in_trait
p;
5060 make_result env T.CIparent
(Reason.Rwitness
p, Typing_utils.terr
env)
5061 | Some
(_, parent_ty
) ->
5062 (* inside a trait, parent is SN.Typehints.this, but with the
5063 * type of the most concrete class that the trait has
5064 * "require extend"-ed *)
5065 let r = Reason.Rwitness
p in
5066 let env, parent_ty
= Phase.localize_with_self
env parent_ty
in
5067 make_result env T.CIparent
(r, TUtils.this_of parent_ty
)
5070 let parent = Env.get_parent
env in
5071 let parent_defined = snd
parent <> Typing_utils.tany env in
5072 if not
parent_defined
5073 then Errors.parent_undefined
p;
5074 let r = Reason.Rwitness
p in
5075 let env, parent = Phase.localize_with_self
env parent in
5076 (* parent is still technically the same object. *)
5077 make_result env T.CIparent
(r, TUtils.this_of
(r, snd
parent))
5079 | _, (Terr
| Tany
| Tmixed
| Tnonnull
| Tarraykind
_ | Toption
_ | Tprim
_
5080 | Tfun
_ | Ttuple
_ | Tshape
_ | Tvar
_ | Tdynamic
5081 | Tanon
(_, _) | Tunresolved
_ | Tabstract
(_, _) | Tobject
5083 let parent = Env.get_parent
env in
5084 let parent_defined = snd
parent <> Typing_utils.tany env in
5085 if not
parent_defined
5086 then Errors.parent_undefined
p;
5087 let r = Reason.Rwitness
p in
5088 let env, parent = Phase.localize_with_self
env parent in
5089 (* parent is still technically the same object. *)
5090 make_result env T.CIparent
(r, TUtils.this_of
(r, snd
parent))
5093 make_result env T.CIstatic
5094 (Reason.Rwitness
p, TUtils.this_of
(Env.get_self
env))
5096 make_result env T.CIself
5097 (Reason.Rwitness
p, snd
(Env.get_self
env))
5098 | CI
(c, hl
) as e1
->
5099 let class_ = Env.get_class
env (snd
c) in
5102 make_result env (T.CI
(c, hl
)) (Reason.Rwitness
p, Typing_utils.tany env)
5105 resolve_type_arguments_and_check_constraints ~check_constraints
5106 env p c e1
class_.tc_tparams hl
in
5107 make_result env (T.CI
(c, hl
)) ty
5109 | CIexpr
(p, _ as e) ->
5110 let env, te, ty = expr
env e in
5111 let rec resolve_ety ty =
5112 let env, ty = TUtils.fold_unresolved
env ty in
5113 let _, ty = Env.expand_type
env ty in
5114 match TUtils.get_base_type
env ty with
5115 | _, Tabstract
(AKnewtype
(classname
, [the_cls
]), _) when
5116 classname
= SN.Classes.cClassname
-> resolve_ety the_cls
5117 | _, Tabstract
(AKgeneric
_, _)
5119 | r, Tunresolved
tyl -> r, Tunresolved
(List.map
tyl resolve_ety)
5120 | _, Tvar
_ as ty -> resolve_ety ty
5121 | _, Tdynamic
as ty -> ty
5122 | _, (Tany
| Tprim Tstring
| Tabstract
(_, None
) | Tmixed
| Tobject
)
5123 when not
(Env.is_strict
env) ->
5124 Reason.Rwitness
p, Typing_utils.tany env
5125 | _, (Terr
| Tany
| Tmixed
| Tnonnull
| Tarraykind
_ | Toption
_
5126 | Tprim
_ | Tfun
_ | Ttuple
_
5127 | Tabstract
((AKenum
_ | AKdependent
_ | AKnewtype
_), _)
5128 | Tanon
(_, _) | Tobject
| Tshape
_ as ty
5130 Errors.expected_class ~suffix
:(", but got "^
Typing_print.error ty) p;
5131 Reason.Rwitness
p, Typing_utils.terr
env in
5132 let result_ty = resolve_ety ty in
5133 make_result env (T.CIexpr
te) result_ty
5135 and call_construct
p env class_ params el uel
cid =
5136 let cid = if cid = CIparent
then CIstatic
else cid in
5137 let env, tcid
, cid_ty
= static_class_id ~check_constraints
:false p env cid in
5139 type_expansions
= [];
5141 substs
= Subst.make
class_.tc_tparams
params;
5142 from_class
= Some
cid;
5143 validate_dty
= None
;
5145 let env = Phase.check_tparams_constraints ~use_pos
:p ~
ety_env env class_.tc_tparams
in
5146 if class_.tc_is_xhp
then env, tcid
, [], [], (Reason.Rnone
, TUtils.tany env) else
5147 let cstr = Env.get_construct
env class_ in
5148 let mode = Env.get_mode
env in
5149 Typing_hooks.dispatch_constructor_hook
class_ params env p;
5150 match (fst
cstr) with
5153 (mode = FileInfo.Mstrict
|| mode = FileInfo.Mpartial
) &&
5154 class_.tc_members_fully_known
5155 then Errors.constructor_no_args
p;
5156 let env, tel, _tyl
= exprs env el
in
5157 env, tcid
, tel, [], (Reason.Rnone
, TUtils.terr
env)
5158 | Some
{ ce_visibility
= vis; ce_type
= lazy m
; _ } ->
5159 TVis.check_obj_access
p env (Reason.to_pos
(fst m
), vis);
5160 let env, m
= Phase.localize ~
ety_env env m
in
5161 let env, tel, tuel
, _ty
= call ~
expected:None
p env m el uel
in
5162 env, tcid
, tel, tuel
, m
5164 and check_arity ?
(did_unpack
=false) pos pos_def
(arity:int) exp_arity
=
5165 let exp_min = (Typing_defs.arity_min exp_arity
) in
5167 then Errors.typing_too_few_args
pos pos_def
;
5168 match exp_arity
with
5169 | Fstandard
(_, exp_max
) ->
5170 let arity = if did_unpack
then arity + 1 else arity in
5172 then Errors.typing_too_many_args
pos pos_def
;
5173 | Fvariadic
_ | Fellipsis
_ -> ()
5175 and check_lambda_arity lambda_pos def_pos lambda_arity expected_arity
=
5176 let expected_min = Typing_defs.arity_min expected_arity
in
5177 match lambda_arity
, expected_arity
with
5178 | Fstandard
(lambda_min
, _), Fstandard
_ ->
5179 if lambda_min
< expected_min
5180 then Errors.typing_too_few_args lambda_pos def_pos
;
5181 if lambda_min
> expected_min
5182 then Errors.typing_too_many_args lambda_pos def_pos
5185 and check_deprecated
p { ft_pos
; ft_deprecated
; _ } =
5186 match ft_deprecated
with
5187 | Some s
-> Errors.deprecated_use
p ft_pos s
5190 (* The variadic capture argument is an array listing the passed
5191 * variable arguments for the purposes of the function body; callsites
5192 * should not unify with it *)
5193 and variadic_param
env ft =
5194 match ft.ft_arity
with
5195 | Fvariadic
(_, param) -> env, Some
param
5196 | Fellipsis
(_, pos) ->
5197 env, Some
(TUtils.default_fun_param ~
pos (Reason.Rvar_param
pos, Tany
))
5198 | Fstandard
_ -> env, None
5200 and param_modes ?
(is_variadic
=false) { fp_pos
; fp_kind
; _ } (pos, e) =
5201 match fp_kind
, e with
5202 | FPnormal
, Unop
(Ast.Uref
, _) ->
5203 Errors.pass_by_ref_annotation_unexpected
pos fp_pos is_variadic
5204 | FPnormal
, Callconv
_ ->
5205 Errors.inout_annotation_unexpected
pos fp_pos is_variadic
5207 | FPref
, Unop
(Ast.Uref
, _) -> ()
5208 | FPref
, Callconv
(kind, _) ->
5210 (* HHVM supports pass-by-ref for arguments annotated as 'inout'. *)
5214 Errors.pass_by_ref_annotation_missing
pos fp_pos
5215 (* HHVM also allows '&' on arguments to inout parameters via interop layer. *)
5216 | FPinout
, Unop
(Ast.Uref
, _)
5217 | FPinout
, Callconv
(Ast.Pinout
, _) -> ()
5219 Errors.inout_annotation_missing
pos fp_pos
5221 and inout_write_back
env { fp_type
; _ } (_, e) =
5223 | Callconv
(Ast.Pinout
, e1
) ->
5224 (* Translate the write-back semantics of inout parameters.
5226 * This matters because we want to:
5227 * (1) make sure we can write to the original argument
5228 * (modifiable lvalue check)
5229 * (2) allow for growing of locals / Tunresolveds (type side effect)
5230 * but otherwise unify the argument type with the parameter hint
5232 let env, _te
, _ty
= assign_
(fst e1
) Reason.URparam_inout
env e1 fp_type
in
5236 and call ~
expected ?
(is_expr_statement
=false) ?method_call_info
pos env fty el uel
=
5237 let env, tel, tuel
, ty = call_ ~
expected ~is_expr_statement ~method_call_info
pos env fty el uel
in
5238 (* We need to solve the constraints after every single function call.
5239 * The type-checker is control-flow sensitive, the same value could
5240 * have different type depending on the branch that we are in.
5241 * When this is the case, a call could violate one of the constraints
5243 let env = Env.check_todo
env in
5246 and call_ ~
expected ~method_call_info ~is_expr_statement
pos env fty el uel
=
5247 let make_unpacked_traversable_ty pos ty =
5248 let unpack_r = Reason.Runpack_param
pos in
5249 unpack_r, Tclass
((pos, SN.Collections.cTraversable
), [ty])
5251 let env, efty
= Env.expand_type
env fty in
5253 | _, (Terr
| Tany
| Tunresolved
[] | Tdynamic
) ->
5254 let el = el @ uel
in
5255 let env, tel = List.map_env
env el begin fun env elt
->
5256 let env, te, arg_ty
=
5257 expr ~
expected:(pos, Reason.URparam
, (Reason.Rnone
, Typing_utils.tany env)) ~is_func_arg
:true env elt
5261 | _, Callconv
(Ast.Pinout
, e1
) ->
5262 let env, _te
, _ty
= assign_
(fst e1
) Reason.URparam_inout
env e1 efty
in
5264 | _, Unop
(Ast.Uref
, e1
) ->
5265 let env, _te
, _ty
= assign_
(fst e1
) Reason.URparam
env e1 efty
in
5268 let env, _arg_ty
= check_valid_rvalue
pos env arg_ty
in
5271 let env = call_untyped_unpack
env uel
in
5272 Typing_hooks.dispatch_fun_call_hooks
[] (List.map
(el @ uel
) fst
) env;
5274 if snd efty
= Tdynamic
then
5275 (Reason.Rdynamic_call
pos, Tdynamic
)
5276 else (Reason.Rnone
, Typing_utils.tany env)
5279 | _, Tunresolved
[ty] ->
5280 call ~
expected pos env ty el uel
5281 | r, Tunresolved
tyl ->
5282 let env, retl
= List.map_env
env tyl begin fun env ty ->
5283 let env, _, _, ty = call ~
expected pos env ty el uel
in env, ty
5285 let env, ty = TUtils.in_var
env (r, Tunresolved retl
) in
5288 (* Typing of format string functions. It is dependent on the arguments (el)
5289 * so it cannot be done earlier.
5291 Typing_reactivity.verify_void_return_to_rx ~is_expr_statement
pos env ft;
5292 let pos_def = Reason.to_pos r2
in
5293 let env, ft = Typing_exts.retype_magic_func
env ft el in
5294 check_deprecated
pos ft;
5295 let env, var_param = variadic_param
env ft in
5297 (* Force subtype with expected result *)
5298 let env = check_expected_ty
"Call result" env ft.ft_ret
expected in
5300 let is_lambda e = match snd
e with Efun
_ -> true | _ -> false in
5302 let get_next_param_info paraml =
5305 false, Some
param, paraml
5307 true, var_param, paraml in
5309 (* Given an expected function type ft, check types for the non-unpacked
5310 * arguments. Don't check lambda expressions if check_lambdas=false *)
5311 let rec check_args check_lambdas
env el paraml =
5313 (* We've got an argument *)
5314 | ((pos, _ as e), opt_result
) :: el ->
5315 (* Pick up next parameter type info *)
5316 let is_variadic, opt_param
, paraml = get_next_param_info paraml in
5317 let env, one_result
=
5318 if is_lambda e && not check_lambdas
|| Option.is_some opt_result
5319 then env, opt_result
5321 begin match opt_param
with
5324 expr ~is_func_arg
:true ~accept_using_var
:param.fp_accept_disposable
5325 ~
expected:(pos, Reason.URparam
, param.fp_type
) env e in
5326 let env = call_param
env param (e, ty) ~
is_variadic in
5329 let env, te, ty = expr ~
expected:(pos, Reason.URparam
, (Reason.Rnone
, Typing_utils.tany env))
5330 ~is_func_arg
:true env e in
5333 let env, rl
, paraml = check_args check_lambdas
env el paraml in
5334 env, (e, one_result
)::rl
, paraml
5339 (* First check the non-lambda arguments. For generic functions, this
5340 * is likely to resolve type variables to concrete types *)
5341 let rl = List.map
el (fun e -> (e, None
)) in
5342 let env, rl, _ = check_args false env rl ft.ft_params
in
5343 (* Now check the lambda arguments, hopefully with type variables resolved *)
5344 let env, rl, paraml = check_args true env rl ft.ft_params
in
5345 (* We expect to see results for all arguments after this second pass *)
5349 | None
-> failwith
"missing parameter in check_args" in
5351 let l = List.map
rl (fun (_, opt
) -> get_param opt
) in
5352 Core_list.unzip
l in
5353 TR.check_call env method_call_info
pos r2
ft tys;
5354 let env, tuel
, arity, did_unpack
=
5356 | [] -> env, [], List.length
el, false
5358 (* Enforces that e is unpackable. If e is a tuple, check types against
5359 * parameter types *)
5360 let env, te, ety
= expr
env e in
5363 let rec check_elements env tyl paraml =
5367 let is_variadic, opt_param
, paraml = get_next_param_info paraml in
5368 match opt_param
with
5371 let env = call_param
env param (e, ty) ~
is_variadic in
5372 check_elements env tyl paraml in
5373 let env = check_elements env tyl paraml in
5374 env, [te], List.length
el + List.length
tyl, false
5376 let param_tyl = List.map
paraml (fun param -> param.fp_type
) in
5377 let add_variadic_param_ty param_tyl =
5378 match var_param with
5379 | Some
param -> param.fp_type
:: param_tyl
5380 | None
-> param_tyl in
5381 let param_tyl = add_variadic_param_ty param_tyl in
5383 let env = List.fold_right
param_tyl ~init
:env
5384 ~
f:(fun param_ty env ->
5385 let traversable_ty = make_unpacked_traversable_ty pos param_ty in
5386 Type.sub_type
pos Reason.URparam
env ety
traversable_ty)
5388 env, [te], List.length
el, true
5390 (* If we unpacked an array, we don't check arity exactly. Since each
5391 * unpacked array consumes 1 or many parameters, it is nonsensical to say
5392 * that not enough args were passed in (so we don't do the min check).
5394 let () = check_arity ~did_unpack
pos pos_def arity ft.ft_arity
in
5395 (* Variadic params cannot be inout so we can stop early *)
5396 let env = wfold_left2 inout_write_back
env ft.ft_params
el in
5397 Typing_hooks.dispatch_fun_call_hooks
5398 ft.ft_params
(List.map
(el @ uel
) fst
) env;
5400 TR.get_adjusted_return_type
env method_call_info
ft.ft_ret
in
5401 env, tel, tuel
, ret_ty
5402 | r2
, Tanon
(arity, id) ->
5403 let env, tel, tyl = exprs ~is_func_arg
:true env el in
5404 let expr_for_unpacked_expr_list env = function
5405 | [] -> env, [], None
, Pos.none
5406 | (pos, _) as e :: _ ->
5407 let env, te, ety
= expr
env e in
5408 env, [te], Some ety
, pos
5410 let append_tuple_types tyl = function
5411 | Some
(_, Ttuple tuple_tyl
) -> tyl @ tuple_tyl
5414 let determine_arity env min_arity
pos = function
5416 | Some
(_, Ttuple
_) ->
5417 env, Fstandard
(min_arity
, min_arity
)
5419 (* We need to figure out the underlying type of the unpacked expr type.
5421 * For example, assume the call is:
5423 * where $y is a variadic or collection of strings.
5425 * $y may have the type Tarraykind or Traversable, however we need to
5426 * pass Fvariadic a param of type string.
5428 * Assuming $y has type Tarraykind, in order to get the underlying type
5429 * we create a fresh_type(), wrap it in a Traversable and make that
5430 * Traversable a super type of the expr type (Tarraykind). This way
5431 * we can infer the underlying type and create the correct param for
5434 let ty = Env.fresh_type
() in
5435 let traversable_ty = make_unpacked_traversable_ty pos ty in
5436 let env = Type.sub_type
pos Reason.URparam
env ety
traversable_ty in
5442 fp_accept_disposable
= false;
5443 fp_mutability
= None
;
5444 fp_rx_condition
= None
;
5447 env, Fvariadic
(min_arity
, param)
5449 let env, tuel
, uety_opt
, uepos
= expr_for_unpacked_expr_list env uel
in
5450 let tyl = append_tuple_types tyl uety_opt
in
5451 let env, call_arity
= determine_arity env (List.length
tyl) uepos uety_opt
in
5452 let anon = Env.get_anonymous
env id in
5453 let fpos = Reason.to_pos r2
in
5456 Errors.anonymous_recursive_call
pos;
5457 env, tel, tuel
, err_witness env pos
5458 | Some
(_, _, counter
, _, anon) ->
5459 let () = check_arity
pos fpos (Typing_defs.arity_min call_arity
) arity in
5460 let tyl = List.map
tyl TUtils.default_fun_param
in
5461 counter
:= !counter
+ 1;
5462 let env, _, ty = anon ~
el env tyl call_arity
in
5464 | _, Tarraykind
_ when not
(Env.is_strict
env) ->
5465 (* Relaxing call_user_func to work with an array in partial mode *)
5466 let env = call_untyped_unpack
env uel
in
5467 env, [], [], (Reason.Rnone
, Typing_utils.tany env)
5470 let env = call_untyped_unpack
env uel
in
5471 env, [], [], err_witness env pos
5474 and call_param
env param ((pos, _ as e), arg_ty
) ~
is_variadic =
5475 (match param.fp_name
with
5477 | Some
name -> Typing_suggest.save_param
name env param.fp_type arg_ty
5479 param_modes ~
is_variadic param e;
5480 let env, arg_ty
= check_valid_rvalue
pos env arg_ty
in
5482 (* When checking params the type 'x' may be expression dependent. Since
5483 * we store the expression id in the local env for Lvar, we want to apply
5486 let env, dep_ty
= match snd
e with
5487 | Lvar
_ -> ExprDepTy.make
env (CIexpr
e) arg_ty
5488 | _ -> env, arg_ty
in
5489 Type.sub_type
pos Reason.URparam
env dep_ty
param.fp_type
5491 and call_untyped_unpack
env uel
= match uel
with
5492 (* In the event that we don't have a known function call type, we can still
5493 * verify that any unpacked arguments (`...$args`) are something that can
5494 * be actually unpacked. *)
5497 let env, _, ety
= expr
env e in
5499 | _, Ttuple
_ -> env (* tuples are always fine *)
5502 let ty = Env.fresh_type
() in
5503 let unpack_r = Reason.Runpack_param
pos in
5504 let unpack_ty = unpack_r, Tclass
((pos, SN.Collections.cTraversable
), [ty]) in
5505 Type.sub_type
pos Reason.URparam
env ety
unpack_ty
5510 Errors.bad_call
p (Typing_print.error ty)
5512 and unop ~is_func_arg ~
forbid_uref p env uop
te ty =
5513 let check_dynamic env ty ~
f =
5514 if TUtils.is_dynamic
env ty then
5515 env, (fst
ty, Tdynamic
)
5518 let make_result env te result_ty =
5519 env, T.make_typed_expr
p result_ty (T.Unop
(uop
, te)), result_ty in
5520 let check_arithmetic env ty =
5521 check_dynamic env ty ~
f:begin fun () ->
5522 Type.sub_type
p Reason.URnone
env ty (Reason.Rarith
p, Tprim Tnum
), ty
5527 Async.enforce_nullable_or_not_awaitable
env p ty;
5528 (* !$x (logical not) works with any type, so we just return Tbool *)
5529 make_result env te (Reason.Rlogic_ret
p, Tprim Tbool
)
5531 (* ~$x (bitwise not) only works with int *)
5533 check_dynamic env ty ~
f:begin fun () ->
5534 let int_ty = (Reason.Rarith
p, Tprim Tint
) in
5535 Type.sub_type
p Reason.URnone
env ty int_ty, int_ty
5538 make_result env te ty
5543 (* increment and decrement operators modify the value,
5544 * check for immutability violation here *)
5546 | _, T.ImmutableVar
(p, x) ->
5547 Errors.let_var_immutability_violation
p (Local_id.get_name
x);
5548 expr_error env p (Reason.Rwitness
p)
5550 let env, ty = check_arithmetic env ty in
5552 if Env.env_local_reactive
env then
5553 Typing_mutability.handle_assignment_mutability
env te te
5556 make_result env te ty
5560 (* math operators work with int or floats, so we call sub_type *)
5561 let env, ty = check_arithmetic env ty in
5562 make_result env te ty
5564 if Env.env_local_reactive
env
5565 && not
(TypecheckerOptions.unsafe_rx
(Env.get_options
env))
5566 then Errors.reference_in_rx
p;
5569 then Errors.binding_ref_in_array
p
5570 else if is_func_arg
then
5572 if TypecheckerOptions.disallow_array_cell_pass_by_ref
5573 (Env.get_options
env)
5574 then match snd
te with
5575 | T.Array_get
_ -> Errors.passing_array_cell_by_ref
p
5578 else if Env.is_strict
env
5579 then Errors.reference_expr
p;
5580 make_result env te ty
5582 (* Silencing does not change the type *)
5583 make_result env te ty
5585 and binop in_cond
p env bop
p1 te1 ty1 p2 te2 ty2 =
5587 match Env.expand_type
env ty with
5588 | (_, (_, (Tany
| Terr
))) -> true
5589 | (_, (_, Tunresolved
tyl)) -> List.for_all
tyl is_any
5591 (* Test if `ty` is *not* the any type (or a variant thereof) and
5592 * is a subtype of the primitive type `prim`. *)
5593 let is_sub_prim env ty prim
=
5594 let ty_prim = (Reason.Rarith
p, Tprim prim
) in
5595 if not
(is_any ty) && SubType.is_sub_type
env ty ty_prim
5596 then Some
(fst
ty) else None
in
5597 (* Test if `ty` is *not* the any type (or a variant thereof) and
5598 * is a subtype of `num` but is not a subtype of `int` *)
5599 let is_sub_num_not_sub_int env ty =
5600 let ty_num = (Reason.Rarith
p, Tprim Tnum
) in
5601 let ty_int = (Reason.Rarith
p, Tprim Tint
) in
5602 if not
(is_any ty) && SubType.is_sub_type
env ty ty_num
5603 && not
(SubType.is_sub_type
env ty ty_int)
5604 then Some
(fst
ty) else None
in
5605 (* Force ty1 to be a subtype of ty2 (unless it is any) *)
5606 let enforce_sub_ty env ty1 ty2 =
5607 let env = Type.sub_type
p Reason.URnone
env ty1 ty2 in
5608 Env.expand_type
env ty1 in
5609 let make_result env te1 te2 ty =
5610 env, T.make_typed_expr
p ty (T.Binop
(bop
, te1, te2)), ty in
5611 let check_dynamic f =
5612 if TUtils.is_dynamic
env ty1 then
5614 match is_sub_prim env ty2 Tfloat
with
5616 (* dynamic op float = float *)
5619 (* dynamic op _ = num *)
5620 (fst
ty2, Tprim Tnum
)
5622 make_result env te1 te2 result_prim
5623 else if TUtils.is_dynamic
env ty2 then
5625 match is_sub_prim env ty1 Tfloat
with
5627 (* dynamic op float = float *)
5630 (* dynamic op _ = num *)
5631 (fst
ty1, Tprim Tnum
)
5633 make_result env te1 te2 result_prim
5638 let env, ty1 = TUtils.fold_unresolved
env ty1 in
5639 let env, ty2 = TUtils.fold_unresolved
env ty2 in
5640 let env, ety1
= Env.expand_type
env ty1 in
5641 let env, ety2
= Env.expand_type
env ty2 in
5642 (match ety1
, ety2
with
5643 (* For array<V1>+array<V2> and array<K1,V1>+array<K2,V2>, allow
5644 * the addition to produce a supertype. (We could also handle
5645 * when they have mismatching annotations, but we get better error
5646 * messages if we just let those get unified in the next case. *)
5647 (* The general types are:
5648 * function<Tk,Tv>(array<Tk,Tv>, array<Tk,Tv>): array<Tk,Tv>
5649 * function<T>(array<T>, array<T>): array<T>
5650 * and subtyping on the arguments deals with everything
5652 | (_, Tarraykind
(AKmap
_ as ak
)), (_, Tarraykind
(AKmap
_))
5653 | (_, Tarraykind
(AKvec
_ as ak
)), (_, Tarraykind
(AKvec
_)) ->
5654 let env, a_sup
= Env.fresh_unresolved_type
env in
5655 let env, b_sup
= Env.fresh_unresolved_type
env in
5656 let res_ty = Reason.Rarray_plus_ret
p, Tarraykind
(
5658 | AKvec
_ -> AKvec a_sup
5659 | AKmap
_ -> AKmap
(a_sup
, b_sup
)
5662 let env = Type.sub_type
p1 Reason.URnone
env ety1
res_ty in
5663 let env = Type.sub_type
p2 Reason.URnone
env ety2
res_ty in
5664 make_result env te1 te2 res_ty
5665 | (_, Tarraykind
_), (_, Tarraykind
(AKshape
_)) ->
5666 let env, ty2 = Typing_arrays.downcast_aktypes
env ty2 in
5667 binop in_cond
p env bop
p1 te1 ty1 p2 te2 ty2
5668 | (_, Tarraykind
(AKshape
_)), (_, Tarraykind
_) ->
5669 let env, ty1 = Typing_arrays.downcast_aktypes
env ty1 in
5670 binop in_cond
p env bop
p1 te1 ty1 p2 te2 ty2
5671 | (_, Tarraykind
_), (_, Tarraykind
_)
5672 | (_, (Tany
| Terr
)), (_, Tarraykind
_)
5673 | (_, Tarraykind
_), (_, Tany
) ->
5674 let env, ty = Type.unify
p Reason.URnone
env ty1 ty2 in
5675 make_result env te1 te2 ty
5676 | (_, Tdynamic
), (_, Tdynamic
) ->
5677 make_result env te1 te2 (Reason.Rarith
p, Tdynamic
)
5678 | (_, (Tany
| Terr
| Tmixed
| Tnonnull
| Tarraykind
_ | Toption
_ | Tdynamic
5679 | Tprim
_ | Tvar
_ | Tfun
_ | Tabstract
(_, _) | Tclass
(_, _)
5680 | Ttuple
_ | Tanon
(_, _) | Tunresolved
_ | Tobject
| Tshape
_
5683 let env, texpr
, ty =
5684 binop in_cond
p env Ast.Minus
p1 te1 ty1 p2 te2 ty2 in
5685 match snd texpr
with
5686 | T.Binop
(_, te1, te2) -> make_result env te1 te2 ty
5689 | Ast.Minus
| Ast.Star
-> check_dynamic begin fun () ->
5690 let env, ty1 = enforce_sub_ty env ty1 (Reason.Rarith
p1, Tprim Tnum
) in
5691 let env, ty2 = enforce_sub_ty env ty2 (Reason.Rarith
p2, Tprim Tnum
) in
5692 (* If either side is a float then float: 1.0 - 1 -> float *)
5694 * function(float, num): float
5695 * function(num, float): float
5697 match is_sub_prim env ty1 Tfloat
, is_sub_prim env ty2 Tfloat
with
5698 | (Some
r, _) | (_, Some
r) ->
5699 make_result env te1 te2 (r, Tprim Tfloat
)
5701 (* Both sides are integers, then integer: 1 - 1 -> int *)
5703 * function(int, int): int
5705 match is_sub_prim env ty1 Tint
, is_sub_prim env ty2 Tint
with
5706 | (Some
_, Some
_) ->
5707 make_result env te1 te2 (Reason.Rarith_ret
p, Tprim Tint
)
5709 (* Either side is a non-int num then num *)
5711 * function(num, num): num
5713 match is_sub_num_not_sub_int env ty1,
5714 is_sub_num_not_sub_int env ty2 with
5715 | (Some
r, _) | (_, Some
r) ->
5716 make_result env te1 te2 (r, Tprim Tnum
)
5718 | _, _ -> make_result env te1 te2 ty1
5720 | Ast.Slash
| Ast.Starstar
-> check_dynamic begin fun () ->
5721 let env, ty1 = enforce_sub_ty env ty1 (Reason.Rarith
p1, Tprim Tnum
) in
5722 let env, ty2 = enforce_sub_ty env ty2 (Reason.Rarith
p2, Tprim Tnum
) in
5723 (* If either side is a float then float *)
5725 * function(float, num) : float
5726 * function(num, float) : float
5727 * [Actually, for division result can be false if second arg is zero]
5729 match is_sub_prim env ty1 Tfloat
, is_sub_prim env ty2 Tfloat
with
5730 | (Some
r, _) | (_, Some
r) ->
5731 make_result env te1 te2 (r, Tprim Tfloat
)
5732 (* Otherwise it has type
5733 * function(num, num) : num
5734 * [Actually, for division result can be false if second arg is zero]
5737 let r = match bop
with
5738 | Ast.Slash
-> Reason.Rret_div
p
5739 | _ -> Reason.Rarith_ret
p in
5740 make_result env te1 te2 (r, Tprim Tnum
)
5742 | Ast.Percent
-> check_dynamic begin fun () ->
5743 (* Integer remainder function has type
5744 * function(int, int) : int
5745 * [Actually, result can be false if second arg is zero]
5747 let env, _ = enforce_sub_ty env ty1 (Reason.Rarith
p1, Tprim Tint
) in
5748 let env, _ = enforce_sub_ty env ty2 (Reason.Rarith
p1, Tprim Tint
) in
5749 make_result env te1 te2 (Reason.Rarith_ret
p, Tprim Tint
)
5752 if TUtils.is_dynamic
env ty1 && TUtils.is_dynamic
env ty2 then
5753 make_result env te1 te2 (Reason.Rbitwise
p, Tdynamic
) else
5754 begin match is_sub_prim env ty1 Tbool
, is_sub_prim env ty2 Tbool
with
5758 * function(bool, bool) : bool
5760 let env, _ = if TUtils.is_dynamic
env ty1 then env, ty1 else
5761 enforce_sub_ty env ty1 (Reason.Rlogic_ret
p1, Tprim Tbool
) in
5762 let env, _ = if TUtils.is_dynamic
env ty2 then env, ty2 else
5763 enforce_sub_ty env ty2 (Reason.Rlogic_ret
p1, Tprim Tbool
) in
5764 make_result env te1 te2 (Reason.Rlogic_ret
p, Tprim Tbool
)
5767 * function(int, int) : int
5769 let env, _ = if TUtils.is_dynamic
env ty1 then env, ty1 else
5770 enforce_sub_ty env ty1 (Reason.Rarith
p1, Tprim Tint
) in
5771 let env, _ = if TUtils.is_dynamic
env ty2 then env, ty2 else
5772 enforce_sub_ty env ty2 (Reason.Rarith
p1, Tprim Tint
) in
5773 make_result env te1 te2 (Reason.Rarith_ret
p, Tprim Tint
)
5775 (* Equality and disequality:
5776 * function<T>(T, T): bool
5778 | Ast.Eqeq
| Ast.Diff
->
5779 make_result env te1 te2 (Reason.Rcomp
p, Tprim Tbool
)
5780 | Ast.EQeqeq
| Ast.Diff2
->
5781 make_result env te1 te2 (Reason.Rcomp
p, Tprim Tbool
)
5782 | Ast.Lt
| Ast.Lte
| Ast.Gt
| Ast.Gte
| Ast.Cmp
->
5783 let ty_result = match bop
with Ast.Cmp
-> Tprim Tint
| _ -> Tprim Tbool
in
5784 let ty_num = (Reason.Rcomp
p, Tprim
Nast.Tnum
) in
5785 let ty_string = (Reason.Rcomp
p, Tprim
Nast.Tstring
) in
5787 (Reason.Rcomp
p, Tclass
((p, SN.Classes.cDateTime
), [])) in
5788 let ty_datetimeimmutable =
5789 (Reason.Rcomp
p, Tclass
((p, SN.Classes.cDateTimeImmutable
), [])) in
5791 List.exists
tyl ~
f:(SubType.is_sub_type
env ty1) &&
5792 List.exists
tyl ~
f:(SubType.is_sub_type
env ty2) in
5793 (* So we have three different types here:
5794 * function(num, num): bool
5795 * function(string, string): bool
5796 * function(DateTime | DateTimeImmutable, DateTime | DateTimeImmutable): bool
5798 if not
(both_sub [ty_num] || both_sub [ty_string] ||
5799 both_sub [ty_datetime; ty_datetimeimmutable] ||
5800 TUtils.is_dynamic
env ty1 || TUtils.is_dynamic
env ty2)
5802 let ty1 = Typing_expand.fully_expand
env ty1 in
5803 let ty2 = Typing_expand.fully_expand
env ty2 in
5804 let tys1 = Typing_print.error (snd
ty1) in
5805 let tys2 = Typing_print.error (snd
ty2) in
5806 Errors.comparison_invalid_types
p
5807 (Reason.to_string
("This is " ^
tys1) (fst
ty1))
5808 (Reason.to_string
("This is " ^
tys2) (fst
ty2))
5810 make_result env te1 te2 (Reason.Rcomp
p, ty_result)
5812 (* A bit weird, this one:
5813 * function(Stringish | string, Stringish | string) : string)
5815 let env = SubType.sub_string
p1 env ty1 in
5816 let env = SubType.sub_string
p2 env ty2 in
5817 make_result env te1 te2 (Reason.Rconcat_ret
p, Tprim Tstring
)
5821 make_result env te1 te2 (Reason.Rlogic_ret
p, Tprim Tbool
)
5822 | Ast.Amp
| Ast.Bar
| Ast.Ltlt
| Ast.Gtgt
->
5823 (* If both are dynamic, we can only return dynamic *)
5824 if TUtils.is_dynamic
env ty1 && TUtils.is_dynamic
env ty2 then
5825 make_result env te1 te2 (Reason.Rbitwise_ret
p, Tdynamic
) else
5826 (* Otherwise at least one of these is an int, so the result is an int *)
5827 let env, _ = if TUtils.is_dynamic
env ty1
5829 else enforce_sub_ty env ty1 (Reason.Rbitwise
p1, Tprim Tint
) in
5830 let env, _ = if TUtils.is_dynamic
env ty2
5832 enforce_sub_ty env ty2 (Reason.Rbitwise
p2, Tprim Tint
) in
5833 make_result env te1 te2 (Reason.Rbitwise_ret
p, Tprim Tint
)
5834 | Ast.QuestionQuestion
5838 (* This function captures the common bits of logic behind refinement
5839 * of the type of a local variable or a class member variable as a
5840 * result of a dynamic check (e.g., nullity check, simple type check
5841 * using functions like is_int, is_string, is_array etc.). The
5842 * argument refine is a function that takes the type of the variable
5843 * and returns a refined type (making necessary changes to the
5844 * environment, which is threaded through).
5846 and refine_lvalue_type
env e ~refine
=
5850 | _, Dollardollar
x ->
5851 let ty = Env.get_local
env (snd
x) in
5852 let env, refined_ty
= refine
env ty in
5853 set_local
env x refined_ty
5854 | p, Class_get
(((), cname
), (_, member_name
)) as e ->
5855 let env, _te
, ty = expr
env e in
5856 let env, refined_ty
= refine
env ty in
5857 let env, local = Env.FakeMembers.make_static
p env cname member_name
in
5858 let lvar = (p, local) in
5859 set_local
env lvar refined_ty
5860 (* TODO TAST: generate an assignment to the fake local in the TAST *)
5861 | p, Obj_get
((_, This
| _, Lvar
_ as obj
), (_, Id
(_, member_name
)), _) ->
5862 let env, _te
, ty = expr
env e in
5863 let env, refined_ty
= refine
env ty in
5864 let env, local = Env.FakeMembers.make
p env obj member_name
in
5865 let lvar = (p, local) in
5866 set_local
env lvar refined_ty
5869 and condition_nullity ~nonnull
env e =
5870 let refine = if nonnull
5871 then TUtils.non_null
5872 else (fun env ty -> env, ty) in
5873 refine_lvalue_type
env e ~
refine
5875 and condition_isset
env = function
5876 | _, Array_get
(x, _) -> condition_isset
env x
5877 | v
-> condition_nullity ~nonnull
:true env v
5879 and if_next_condition
env tparamt
e =
5880 match LEnv.get_cont_option
env C.Next
with
5882 | Some
_ -> condition env tparamt
e
5885 * Build an environment for the true or false branch of
5886 * conditional statements.
5888 and condition ?lhs_of_null_coalesce
env tparamet
=
5890 let env, _te
, ty = raw_expr ?lhs_of_null_coalesce ~in_cond
:true env x in
5891 Async.enforce_nullable_or_not_awaitable
env (fst
x) ty;
5892 check_valid_rvalue
(fst
x) env ty
5893 in let condition = condition ?lhs_of_null_coalesce
5895 | _, Expr_list
[] -> env
5896 | _, Expr_list
[x] ->
5897 let env, _ = expr env x in
5898 condition env tparamet
x
5899 | r, Expr_list
(x::xs
) ->
5900 let env, _ = expr env x in
5901 condition env tparamet
(r, Expr_list xs
)
5902 | _, Call
(Cnormal
, (_, Id
(_, func
)), _, [param], [])
5903 when SN.PseudoFunctions.isset
= func
&& tparamet
&&
5904 not
(Env.is_strict
env) ->
5905 condition_isset
env param
5906 | _, Call
(Cnormal
, (_, Id
(_, func
)), _, [e], [])
5907 when not tparamet
&& SN.StdlibFunctions.is_null
= func
->
5908 condition_nullity ~nonnull
:true env e
5909 | _, Binop
((Ast.Eqeq
| Ast.EQeqeq
), (_, Null
), e)
5910 | _, Binop
((Ast.Eqeq
| Ast.EQeqeq
), e, (_, Null
)) when not tparamet
->
5911 let env, _ = expr env e in
5912 condition_nullity ~nonnull
:true env e
5913 | (p, (Lvar
_ | Obj_get
_ | Class_get
_) as e) ->
5914 let env, ty = expr env e in
5915 let env, ety
= Env.expand_type
env ty in
5917 | _, Tarraykind
(AKany
| AKempty
)
5918 | _, Tprim Tbool
-> env
5919 | _, (Terr
| Tany
| Tmixed
| Tnonnull
| Tarraykind
_ | Toption
_ | Tdynamic
5920 | Tprim
_ | Tvar
_ | Tfun
_ | Tabstract
(_, _) | Tclass
(_, _)
5921 | Ttuple
_ | Tanon
(_, _) | Tunresolved
_ | Tobject
| Tshape
_
5923 condition env (not tparamet
) (p, Binop
(Ast.Eqeq
, e, (p, Null
))))
5924 | _, Binop
(Ast.Eq None
, var
, e) when tparamet
->
5925 let env, _ = expr env e in
5926 condition_nullity ~nonnull
:true env var
5927 | p1, Binop
(Ast.Eq None
, (_, (Lvar
_ | Obj_get
_) as lv
), (p2, _)) ->
5928 let env, _ = expr env (p1, Binop
(Ast.Eq None
, lv
, (p2, Null
))) in
5929 condition env tparamet lv
5930 | p, Binop
((Ast.Diff
| Ast.Diff2
as op
), e1
, e2
) ->
5931 let op = if op = Ast.Diff
then Ast.Eqeq
else Ast.EQeqeq
in
5932 condition env (not tparamet
) (p, Binop
(op, e1
, e2
))
5933 | _, Id
(_, s
) when s
= SN.Rx.is_enabled
->
5934 (* when Rx\IS_ENABLED is false - switch env to non-reactive *)
5936 then Env.set_env_reactive
env Nonreactive
5938 | _, Binop
(Ast.AMpamp
, e1
, e2
) when tparamet
->
5939 let env = condition env true e1
in
5940 let env = condition env true e2
in
5942 | _, Binop
(Ast.BArbar
, e1
, e2
) when not tparamet
->
5943 let env = condition env false e1
in
5944 let env = condition env false e2
in
5946 | _, Call
(Cnormal
, (p, Id
(_, f)), _, [lv
], [])
5947 when tparamet
&& f = SN.StdlibFunctions.is_array
->
5948 is_array
env `PHPArray
p f lv
5949 | _, Call
(Cnormal
, (p, Id
(_, f)), _, [lv
], [])
5950 when tparamet
&& f = SN.StdlibFunctions.is_vec ->
5951 is_array
env `HackVec
p f lv
5952 | _, Call
(Cnormal
, (p, Id
(_, f)), _, [lv
], [])
5953 when tparamet
&& f = SN.StdlibFunctions.is_dict
->
5954 is_array
env `HackDict
p f lv
5955 | _, Call
(Cnormal
, (p, Id
(_, f)), _, [lv
], [])
5956 when tparamet
&& f = SN.StdlibFunctions.is_keyset
->
5957 is_array
env `HackKeyset
p f lv
5958 | _, Call
(Cnormal
, (p, Id
(_, f)), _, [lv
], [])
5959 when tparamet
&& f = SN.StdlibFunctions.is_int
->
5960 is_type
env lv Tint
(Reason.Rpredicated
(p, f))
5961 | _, Call
(Cnormal
, (p, Id
(_, f)), _, [lv
], [])
5962 when tparamet
&& f = SN.StdlibFunctions.is_bool
->
5963 is_type
env lv Tbool
(Reason.Rpredicated
(p, f))
5964 | _, Call
(Cnormal
, (p, Id
(_, f)), _, [lv
], [])
5965 when tparamet
&& f = SN.StdlibFunctions.is_float
->
5966 is_type
env lv Tfloat
(Reason.Rpredicated
(p, f))
5967 | _, Call
(Cnormal
, (p, Id
(_, f)), _, [lv
], [])
5968 when tparamet
&& f = SN.StdlibFunctions.is_string
->
5969 is_type
env lv Tstring
(Reason.Rpredicated
(p, f))
5970 | _, Call
(Cnormal
, (p, Id
(_, f)), _, [lv
], [])
5971 when tparamet
&& f = SN.StdlibFunctions.is_resource
->
5972 is_type
env lv Tresource
(Reason.Rpredicated
(p, f))
5973 | _, Call
(Cnormal
, (_, Class_const
(((), CI
((_, class_name), _)), (_, method_name
))), _, [shape
; field
], [])
5974 when tparamet
&& class_name = SN.Shapes.cShapes
&& method_name
= SN.Shapes.keyExists
->
5975 key_exists
env shape field
5976 | _, Unop
(Ast.Unot
, e) ->
5977 condition env (not tparamet
) e
5978 | p, InstanceOf
(ivar
, ((), cid)) when tparamet
&& is_instance_var ivar
->
5979 (* Check the expession and determine its static type *)
5980 let env, _te
, x_ty
= raw_expr ~in_cond
:false env ivar
in
5982 (* What is the local variable bound to the expression? *)
5983 let env, ((ivar_pos
, _) as ivar
) = get_instance_var
env ivar
in
5985 (* The position p here is not really correct... it's the position
5986 * of the instanceof expression, not the class id. But we don't store
5987 * position data for the latter. *)
5988 let env, _te
, obj_ty = static_class_id ~check_constraints
:false p env cid in
5990 if SubType.is_sub_type
env obj_ty (
5991 Reason.none
, Tclass
((Pos.none
, SN.Classes.cAwaitable
), [Reason.none
, Typing_utils.tany env])
5992 ) then () else Async.enforce_nullable_or_not_awaitable
env (fst ivar
) x_ty
;
5994 let safe_instanceof_enabled =
5995 TypecheckerOptions.experimental_feature_enabled
5996 (Env.get_options
env) TypecheckerOptions.experimental_instanceof
in
5997 let rec resolve_obj env obj_ty =
5998 (* Expand so that we don't modify x *)
5999 let env, obj_ty = Env.expand_type
env obj_ty in
6001 (* If it's a generic that's expression dependent, we need to
6002 look at all of its upper bounds and create an unresolved type to
6003 represent all of the possible types.
6005 | r, Tabstract
(AKgeneric s
, _) when AbstractKind.is_generic_dep_ty s
->
6006 let upper_bounds = TySet.elements
(Env.get_upper_bounds
env s
) in
6007 let env, tyl = List.map_env
env upper_bounds resolve_obj in
6008 env, (r, Tunresolved
tyl)
6009 | _, Tabstract
(AKgeneric
name, _) ->
6010 if safe_instanceof_enabled
6011 then Errors.instanceof_generic_classname
p name;
6013 | _, Tabstract
(AKdependent
(`this
, []), Some
(_, Tclass
_)) ->
6015 (* Technically instanceof static is not strong enough to prove
6016 * that a type is exactly the same as the late bound type.
6017 * For now we allow this lie to exist. To solve
6018 * this we either need to create a new type that means
6019 * subtype of static or provide a way of specifying exactly
6020 * the late bound type i.e. $x::class === static::class
6022 if cid = CIstatic
then
6023 ExprDepTy.make
env CIstatic
obj_ty
6027 | _, Tabstract
((AKdependent
_ | AKnewtype
_), Some
ty) ->
6029 | _, Tclass
((_, cid as _c
), tyl) ->
6030 begin match Env.get_class
env cid with
6031 (* Why would this happen? *)
6033 env, (Reason.Rwitness ivar_pos
, Tobject
)
6035 | Some
class_info ->
6036 if SubType.is_sub_type
env x_ty
obj_ty
6038 (* If the right side of the `instanceof` object is
6039 * a super type of what we already knew. In this case,
6040 * since we already have a more specialized object, we
6041 * don't touch the original object. Check out the unit
6042 * test srecko.php if this is unclear.
6044 * Note that if x_ty is Typing_utils.tany env, no amount of subtype
6045 * checking will be able to specify it
6046 * further. This is arguably desirable to maintain
6047 * the invariant that removing annotations gets rid
6048 * of typing errors in partial mode (See also
6052 (* We only implement the safe instanceof in strict mode *)
6053 (* Also: for generic types we implememt it only with
6054 * experimental feature enabled *)
6055 if Env.is_strict
env && (tyl = [] || safe_instanceof_enabled)
6056 then safe_instanceof
env p _c
class_info ivar_pos x_ty
obj_ty
6059 | r, Tunresolved
tyl ->
6060 let env, tyl = List.map_env
env tyl resolve_obj in
6061 env, (r, Tunresolved
tyl)
6062 | _, (Terr
| Tany
| Tmixed
| Tnonnull
| Tarraykind
_ | Tprim
_ | Tvar
_
6063 | Tfun
_ | Tabstract
((AKenum
_ | AKnewtype
_ | AKdependent
_), _)
6064 | Ttuple
_ | Tanon
(_, _) | Toption
_ | Tobject
| Tshape
_
6066 env, (Reason.Rwitness ivar_pos
, Tobject
)
6068 let env, x_ty
= resolve_obj env obj_ty in
6069 set_local
env ivar x_ty
6070 | p, Is
(ivar
, h) when tparamet
&& is_instance_var ivar
->
6071 (* Check the expession and determine its static type *)
6072 let env, _te
, ivar_ty
= raw_expr ~in_cond
:false env ivar
in
6073 (* What is the local variable bound to the expression? *)
6074 let env, ((ivar_pos
, _) as ivar
) = get_instance_var
env ivar
in
6075 (* Resolve the typehint to a type *)
6076 let env, hint_ty = Phase.hint_locl
env h in
6077 let reason = Reason.Ris ivar_pos
in
6078 (* Expand so that we don't modify ivar *)
6079 let env, hint_ty = Env.expand_type
env hint_ty in
6081 if snd
hint_ty <> Tdynamic
&& SubType.is_sub_type
env ivar_ty
hint_ty
6083 else safely_refine_type
env p reason ivar_pos ivar_ty
hint_ty in
6084 set_local
env ivar
hint_ty
6085 | _, Binop
((Ast.Eqeq
| Ast.EQeqeq
), e, (_, Null
))
6086 | _, Binop
((Ast.Eqeq
| Ast.EQeqeq
), (_, Null
), e) ->
6087 let env, _ = expr env e in
6088 condition_nullity ~nonnull
:false env e
6090 let env, _ = expr env e in
6093 and safely_refine_type
env p reason ivar_pos ivar_ty
hint_ty =
6094 match snd ivar_ty
, snd
hint_ty with
6095 | _, Tclass
((_, cid) as _c
, tyl) ->
6096 begin match Env.get_class
env cid with
6097 | Some
class_info ->
6098 let env, tparams_with_new_names
, tyl_fresh
=
6099 isexpr_generate_fresh_tparams
env class_info reason tyl in
6100 safely_refine_class_type
6101 env p _c
class_info ivar_ty
hint_ty tparams_with_new_names
6104 env, (Reason.Rwitness ivar_pos
, Tobject
)
6106 | Ttuple ivar_tyl
, Ttuple hint_tyl
6107 when (List.length ivar_tyl
) = (List.length hint_tyl
) ->
6109 List.map2_env
env ivar_tyl hint_tyl
begin fun env ivar_ty
hint_ty ->
6110 safely_refine_type
env p reason ivar_pos ivar_ty
hint_ty
6113 env, (reason, Ttuple
tyl)
6115 TUtils.non_null
env ivar_ty
6116 | _, Tabstract
(AKdependent
(`this
, []), Some
(_, Tclass
_)) ->
6117 ExprDepTy.make
env CIstatic
hint_ty
6118 | _, (Tany
| Tmixed
| Tprim
_ | Toption
_ | Ttuple
_
6119 | Tshape
_ | Tvar
_ | Tabstract
_ | Tarraykind
_ | Tanon
_
6120 | Tunresolved
_ | Tobject
| Terr
| Tfun
_ | Tdynamic
) ->
6121 (* TODO(kunalm) Implement the type refinement for each type *)
6124 and safe_instanceof
env p class_name class_info ivar_pos ivar_ty
obj_ty =
6125 (* Generate fresh names consisting of formal type parameter name
6126 * with unique suffix *)
6127 let env, tparams_with_new_names
=
6128 List.map_env
env class_info.tc_tparams
6129 (fun env ((_, (_,name), _) as tp
) ->
6130 let env, name = Env.add_fresh_generic_parameter
env name in
6131 env, Some
(tp
, name)) in
6132 let new_names = List.map
6133 ~
f:(fun x -> snd
@@ Option.value_exn
x)
6134 tparams_with_new_names
in
6136 snd
class_name ^
"<" ^
6137 String.concat
"," new_names
6139 let reason = Reason.Rinstanceof
(ivar_pos
, s) in
6140 let tyl_fresh = List.map
6141 ~
f:(fun new_name
-> (reason, Tabstract
(AKgeneric new_name
, None
)))
6144 safely_refine_class_type
6145 env p class_name class_info ivar_ty
obj_ty tparams_with_new_names
tyl_fresh in
6148 and isexpr_generate_fresh_tparams
env class_info reason hint_tyl
=
6149 let tparams_len = List.length
class_info.tc_tparams
in
6150 let hint_tyl = List.take
hint_tyl tparams_len in
6151 let pad_len = tparams_len - (List.length
hint_tyl) in
6153 List.map
hint_tyl (fun x -> Some
x) @ (List.init
pad_len (fun _ -> None
)) in
6154 let replace_wildcard env hint_ty ((_, (_, tparam_name
), _) as tp
) =
6156 | Some
(_, Tabstract
(AKgeneric
name, _))
6157 when Env.is_fresh_generic_parameter
name ->
6158 env, (Some
(tp
, name), (reason, Tabstract
(AKgeneric
name, None
)))
6162 let env, new_name
= Env.add_fresh_generic_parameter
env tparam_name
in
6163 env, (Some
(tp
, new_name
), (reason, Tabstract
(AKgeneric new_name
, None
)))
6165 let env, tparams_and_tyl
= List.map2_env
env hint_tyl class_info.tc_tparams
6166 ~
f:replace_wildcard in
6167 let tparams_with_new_names, tyl_fresh = List.unzip tparams_and_tyl
in
6168 env, tparams_with_new_names, tyl_fresh
6170 and safely_refine_class_type
6171 env p class_name class_info ivar_ty
obj_ty tparams_with_new_names tyl_fresh =
6172 (* Type of variable in block will be class name
6173 * with fresh type parameters *)
6174 let obj_ty = (fst
obj_ty, Tclass
(class_name, tyl_fresh)) in
6176 (* Add in constraints as assumptions on those type parameters *)
6178 type_expansions
= [];
6179 substs
= Subst.make
class_info.tc_tparams
tyl_fresh;
6180 this_ty = obj_ty; (* In case `this` appears in constraints *)
6182 validate_dty
= None
;
6184 let add_bounds env ((_, _, cstr_list
), ty_fresh
) =
6185 List.fold_left cstr_list ~init
:env ~
f:begin fun env (ck
, ty) ->
6186 (* Substitute fresh type parameters for
6187 * original formals in constraint *)
6188 let env, ty = Phase.localize ~
ety_env env ty in
6189 SubType.add_constraint
p env ck ty_fresh
ty end in
6191 List.fold_left
(List.zip_exn
class_info.tc_tparams
tyl_fresh)
6192 ~
f:add_bounds ~init
:env in
6194 (* Finally, if we have a class-test on something with static class type,
6195 * then we can chase the hierarchy and decompose the types to deduce
6196 * further assumptions on type parameters. For example, we might have
6197 * class B<Tb> { ... }
6198 * class C extends B<int>
6199 * and have obj_ty = C and x_ty = B<T> for a generic parameter T.
6200 * Then SubType.add_constraint will deduce that T=int and add int as
6201 * both lower and upper bound on T in env.lenv.tpenv
6203 let env = SubType.add_constraint
p env Ast.Constraint_as
obj_ty ivar_ty
in
6205 (* It's often the case that the fresh name isn't necessary. For
6206 * example, if C<T> extends B<T>, and we have $x:B<t> for some type t
6207 * then $x instanceof B should refine to $x:C<t>.
6208 * We take a simple approach:
6209 * For a fresh type parameter T#1, if
6210 * 1) There is an eqality constraint T#1 = t then replace T#1 with t.
6211 * 2) T#1 is covariant, and T#1 <: t and occurs nowhere else in the constraints
6212 * 3) T#1 is contravariant, and t <: T#1 and occurs nowhere else in the constraints
6214 let tparams_in_constraints = Env.get_tpenv_tparams
env in
6215 let tyl_fresh_simplified =
6216 List.map2_exn
tparams_with_new_names tyl_fresh
6217 ~
f:begin fun x y
-> match x, y
with
6218 | Some
((variance
, _, _), newname
), ty_fresh
->
6219 begin match variance
,
6220 TySet.elements
(Env.get_lower_bounds
env newname
),
6221 TySet.elements
(Env.get_equal_bounds
env newname
),
6222 TySet.elements
(Env.get_upper_bounds
env newname
) with
6223 (* Special case for mixed=?nonnull as a lower bound *)
6224 | _, [(_, Toption
(_, Tnonnull
)) as ty], _, _ -> ty
6225 | _, [(_, Tmixed
) as ty], _, _ -> ty
6226 | _, _, [ty], _ -> ty
6227 | Ast.Covariant
, _, _, [ty]
6228 | Ast.Contravariant
, [ty], _, _
6229 when not
(SSet.mem newname
tparams_in_constraints) -> ty
6230 | _, _, _, _ -> ty_fresh
6232 | None
, ty_fresh
-> ty_fresh
6234 let obj_ty_simplified = (fst
obj_ty, Tclass
(class_name, tyl_fresh_simplified)) in
6235 env, obj_ty_simplified
6237 and is_instance_var
= function
6238 | _, (Lvar
_ | This
) -> true
6239 | _, Obj_get
((_, This
), (_, Id
_), _) -> true
6240 | _, Obj_get
((_, Lvar
_), (_, Id
_), _) -> true
6241 | _, Class_get
(_, _) -> true
6244 and get_instance_var
env = function
6245 | p, Class_get
(((), cname
), (_, member_name
)) ->
6246 let env, local = Env.FakeMembers.make_static
p env cname member_name
in
6248 | p, Obj_get
((_, This
| _, Lvar
_ as obj
), (_, Id
(_, member_name
)), _) ->
6249 let env, local = Env.FakeMembers.make
p env obj member_name
in
6251 | _, Lvar
(p, x) -> env, (p, x)
6252 | p, This
-> env, (p, this
)
6253 | _ -> failwith
"Should only be called when is_instance_var is true"
6255 and is_type
env e tprim
r =
6256 refine_lvalue_type
env e ~
refine:(fun env _ -> env, (r, Tprim tprim
))
6258 (* Refine type for is_array, is_vec, is_keyset and is_dict tests
6259 * `pred_name` is the function name itself (e.g. 'is_vec')
6260 * `p` is position of the function name in the source
6261 * `arg_expr` is the argument to the function
6263 and is_array
env ty p pred_name arg_expr
=
6264 refine_lvalue_type
env arg_expr ~
refine:begin fun env arg_ty
->
6265 let r = Reason.Rpredicated
(p, pred_name
) in
6266 let env, tarrkey_name
= Env.add_fresh_generic_parameter
env "Tk" in
6267 let tarrkey = (r, Tabstract
(AKgeneric tarrkey_name
, None
)) in
6268 let env = SubType.add_constraint
p env Ast.Constraint_as
6269 tarrkey (r, Tprim Tarraykey
) in
6270 let env, tfresh_name
= Env.add_fresh_generic_parameter
env "T" in
6271 let tfresh = (r, Tabstract
(AKgeneric tfresh_name
, None
)) in
6272 (* This is the refined type of e inside the branch *)
6273 let refined_ty = (r,
6276 Tclass
((Pos.none
, SN.Collections.cDict
), [tarrkey; tfresh])
6278 Tclass
((Pos.none
, SN.Collections.cVec
), [tfresh])
6280 Tclass
((Pos.none
, SN.Collections.cKeyset
), [tarrkey])
6282 let safe_isarray_enabled =
6283 TypecheckerOptions.experimental_feature_enabled
6284 (Env.get_options
env) TypecheckerOptions.experimental_isarray
in
6285 if safe_isarray_enabled
6286 then Tarraykind
(AKvarray_or_darray
tfresh)
6287 else Tarraykind AKany
) in
6288 (* Add constraints on generic parameters that must
6289 * hold for refined_ty <:arg_ty. For example, if arg_ty is Traversable<T>
6290 * and refined_ty is keyset<T#1> then we know T#1 <: T *)
6291 let env = SubType.add_constraint
p env Ast.Constraint_as
refined_ty arg_ty
in
6295 and key_exists
env shape field
=
6296 refine_lvalue_type
env shape ~
refine:begin fun env shape_ty
->
6297 match TUtils.shape_field_name
env field
with
6298 | None
-> env, shape_ty
6299 | Some
field_name -> Typing_shapes.refine_shape
field_name env shape_ty
6302 and string2
env idl
=
6304 List.fold_left idl ~init
:(env,[]) ~
f:begin fun (env,tel) x ->
6305 let env, te, ty = expr env x in
6307 let env = SubType.sub_string
p env ty in
6312 (* If the current class inherits from classes that take type arguments, we need
6313 * to check that the arguments provided are consistent with the constraints on
6314 * the type parameters. *)
6315 and check_implements_tparaml
(env: Env.env) ht
=
6316 let _r, (p, c), paraml = TUtils.unwrap_class_type ht
in
6317 let class_ = Decl_env.get_class_dep
env.Env.decl_env
c in
6320 (* The class lives in PHP land *)
6323 let size1 = List.length
class_.dc_tparams
in
6324 let size2 = List.length
paraml in
6325 if size1 <> size2 then Errors.class_arity
p class_.dc_pos
c size1;
6326 let subst = Inst.make_subst
class_.dc_tparams
paraml in
6327 iter2_shortest
begin fun (_, (p, _), cstrl
) ty ->
6328 List.iter cstrl
begin fun (ck
, cstr) ->
6329 (* Constraint might contain uses of generic type parameters *)
6330 let cstr = Inst.instantiate
subst cstr in
6332 | Ast.Constraint_as
->
6333 Type.sub_type_decl
p Reason.URnone
env ty cstr
6334 | Ast.Constraint_eq
->
6335 (* This code could well be unreachable, because we don't allow
6336 * equality constraints on class generics. *)
6337 Type.sub_type_decl
p Reason.URnone
env ty cstr;
6338 Type.sub_type_decl
p Reason.URnone
env cstr ty
6339 | Ast.Constraint_super
->
6340 Type.sub_type_decl
p Reason.URnone
env cstr ty
6342 end class_.dc_tparams
paraml
6344 (* In order to type-check a class, we need to know what "parent"
6345 * refers to. Sometimes people write "parent::", when that happens,
6346 * we need to know the type of parent.
6348 and class_def_parent
env class_def class_type
=
6349 match class_def
.c_extends
with
6350 | (_, Happly
((_, x), _) as parent_ty
) :: _ ->
6351 let parent_type = Decl_env.get_class_dep
env.Env.decl_env
x in
6352 (match parent_type with
6353 | Some
parent_type -> check_parent class_def class_type
parent_type
6355 let parent_ty = Decl_hint.hint
env.Env.decl_env
parent_ty in
6356 env, Some
x, parent_ty
6357 (* The only case where we have more than one parent class is when
6358 * dealing with interfaces and interfaces cannot use parent.
6361 | _ -> env, None
, (Reason.Rnone
, Typing_utils.tany env)
6363 and check_parent class_def class_type
parent_type =
6364 let position = fst class_def
.c_name
in
6365 if class_type
.tc_const
&& not
parent_type.dc_const
6366 then Errors.self_const_parent_not
position;
6367 if parent_type.dc_const
&& not class_type
.tc_const
6368 then Errors.parent_const_self_not
position;
6369 (* Are all the parents in Hack? Do we know all their methods?
6370 * If so, let's check that the abstract methods have been implemented.
6372 if class_type
.tc_members_fully_known
6373 then check_parent_abstract
position parent_type class_type
;
6374 if parent_type.dc_final
6375 then Errors.extend_final
position parent_type.dc_pos
parent_type.dc_name
6378 and check_parent_sealed child_type
parent_type =
6379 match parent_type.dc_sealed_whitelist
with
6382 begin match child_type
.tc_kind
with
6386 if not
(SSet.mem child_type
.tc_name whitelist
)
6388 let error = Errors.extend_sealed
6389 child_type
.tc_pos
parent_type.dc_pos
parent_type.dc_name
in
6390 match parent_type.dc_kind
, child_type
.tc_kind
with
6392 | Ast.Cnormal
, _ -> error "class" "extend"
6393 | Ast.Cinterface
, Ast.Cinterface
-> error "interface" "extend"
6394 | Ast.Cinterface
, _ -> error "interface" "implement"
6398 Errors.trait_implement_sealed
6399 child_type
.tc_pos
parent_type.dc_pos
parent_type.dc_name
6403 and check_parents_sealed
env child_def child_type
=
6404 List.iter (child_def
.c_extends
@ child_def
.c_implements
) begin function
6405 | _, Happly
((_, name), _) ->
6406 begin match Decl_env.get_class_dep
env.Env.decl_env
name with
6407 | Some
parent_type -> check_parent_sealed child_type
parent_type
6413 and check_parent_abstract
position parent_type class_type
=
6414 let is_final = class_type
.tc_final
in
6415 if parent_type.dc_kind
= Ast.Cabstract
&&
6416 (class_type
.tc_kind
<> Ast.Cabstract
|| is_final)
6418 check_extend_abstract_meth ~
is_final position class_type
.tc_methods
;
6419 check_extend_abstract_meth ~
is_final position class_type
.tc_smethods
;
6420 check_extend_abstract_const ~
is_final position class_type
.tc_consts
;
6421 check_extend_abstract_typeconst
6422 ~
is_final position class_type
.tc_typeconsts
;
6425 and class_def tcopt
c =
6426 let env = EnvFromDef.class_env tcopt
c in
6427 let tc = Env.get_class
env (snd
c.c_name
) in
6428 add_decl_errors
(Option.(map
tc (fun tc -> value_exn
tc.tc_decl_errors
)));
6429 let c = TNBody.class_meth_bodies tcopt
c in
6430 if not
!auto_complete
then begin
6431 NastCheck.class_ env c;
6432 NastInitCheck.class_ env c;
6436 (* This can happen if there was an error during the declaration
6440 Typing_requirements.check_class
env tc;
6441 Some
(class_def_
env c tc)
6443 and class_def_
env c tc =
6444 Typing_hooks.dispatch_enter_class_def_hook
c tc;
6446 let kind = match c.c_kind
with
6447 | Ast.Cenum
-> SN.AttributeKinds.enum
6448 | _ -> SN.AttributeKinds.cls
in
6449 Typing_attributes.check_def
env new_object
kind c.c_user_attributes
in
6450 let pc, _ = c.c_name
in
6452 (c.c_extends
@ c.c_implements
@ c.c_uses
)
6453 (Decl_hint.hint
env.Env.decl_env
) in
6454 TI.check_tparams_instantiable
env (fst
c.c_tparams
);
6455 let env, constraints
=
6456 Phase.localize_generic_parameters_with_bounds
env (fst
c.c_tparams
)
6457 ~
ety_env:(Phase.env_with_self
env) in
6458 let env = add_constraints
(fst
c.c_name
) env constraints
in
6459 Typing_variance.class_ (Env.get_options
env) (snd
c.c_name
) tc impl;
6460 List.iter impl (check_implements_tparaml
env);
6461 check_parents_sealed
env c tc;
6463 let env, parent_id
, parent = class_def_parent
env c tc in
6464 let is_final = tc.tc_final
in
6465 if (tc.tc_kind
= Ast.Cnormal
|| is_final) && tc.tc_members_fully_known
6467 check_extend_abstract_meth ~
is_final pc tc.tc_methods
;
6468 check_extend_abstract_meth ~
is_final pc tc.tc_smethods
;
6469 check_extend_abstract_const ~
is_final pc tc.tc_consts
;
6470 check_extend_abstract_typeconst ~
is_final pc tc.tc_typeconsts
;
6472 let env = Env.set_parent
env parent in
6473 let env = match parent_id
with
6475 | Some parent_id
-> Env.set_parent_id
env parent_id
in
6476 if tc.tc_final
then begin
6478 | Ast.Cinterface
-> Errors.interface_final
(fst
c.c_name
)
6479 | Ast.Cabstract
-> ()
6480 | Ast.Ctrait
-> Errors.trait_final
(fst
c.c_name
)
6482 Errors.internal_error
pc "The parser should not parse final on enums"
6485 SMap.iter (check_static_method
tc.tc_methods
) tc.tc_smethods
;
6486 List.iter impl (class_implements_type
env c);
6487 if tc.tc_is_disposable
6488 then List.iter (c.c_extends
@ c.c_uses
) (Typing_disposable.enforce_is_disposable
env);
6489 let typed_vars = List.map
c.c_vars
(class_var_def
env ~is_static
:false c) in
6490 let typed_methods = List.map
c.c_methods
(method_def
env) in
6491 let typed_typeconsts = List.map
c.c_typeconsts
(typeconst_def
env) in
6492 let typed_consts, const_types
=
6493 List.unzip
(List.map
c.c_consts
(class_const_def
env)) in
6494 let env = Typing_enum.enum_class_check
env tc c.c_consts const_types
in
6495 let typed_constructor = class_constr_def
env c in
6496 let env = Env.set_static
env in
6497 let typed_static_vars =
6498 List.map
c.c_static_vars
(class_var_def
env ~is_static
:true c) in
6499 let typed_static_methods = List.map
c.c_static_methods
(method_def
env) in
6500 Typing_hooks.dispatch_exit_class_def_hook
c tc;
6502 T.c_annotation
= Env.save
env.Env.lenv.Env.tpenv
env;
6503 T.c_mode
= c.c_mode
;
6504 T.c_final
= c.c_final
;
6505 T.c_is_xhp
= c.c_is_xhp
;
6506 T.c_kind
= c.c_kind
;
6507 T.c_name
= c.c_name
;
6508 T.c_tparams
= c.c_tparams
;
6509 T.c_extends
= c.c_extends
;
6510 T.c_uses
= c.c_uses
;
6511 T.c_xhp_attr_uses
= c.c_xhp_attr_uses
;
6512 T.c_xhp_category
= c.c_xhp_category
;
6513 T.c_req_extends
= c.c_req_extends
;
6514 T.c_req_implements
= c.c_req_implements
;
6515 T.c_implements
= c.c_implements
;
6516 T.c_consts
= typed_consts;
6517 T.c_typeconsts
= typed_typeconsts;
6518 T.c_static_vars
= typed_static_vars;
6519 T.c_vars
= typed_vars;
6520 T.c_constructor
= typed_constructor;
6521 T.c_static_methods
= typed_static_methods;
6522 T.c_methods
= typed_methods;
6523 T.c_user_attributes
= List.map
c.c_user_attributes
(user_attribute
env);
6524 T.c_enum
= c.c_enum
;
6527 and check_static_method obj method_name static_method
=
6528 if SMap.mem method_name obj
6530 let lazy (static_method_reason
, _) = static_method
.ce_type
in
6531 let dyn_method = SMap.find_unsafe method_name obj
in
6532 let lazy (dyn_method_reason
, _) = dyn_method.ce_type
in
6533 Errors.static_dynamic
6534 (Reason.to_pos static_method_reason
)
6535 (Reason.to_pos dyn_method_reason
)
6540 and check_extend_abstract_meth ~
is_final p smap
=
6541 SMap.iter begin fun x ce
->
6542 match ce
.ce_type
with
6543 | lazy (r, Tfun
{ ft_abstract
= true; _ }) ->
6544 Errors.implement_abstract ~
is_final p (Reason.to_pos
r) "method" x
6548 (* Type constants must be bound to a concrete type for non-abstract classes.
6550 and check_extend_abstract_typeconst ~
is_final p smap
=
6551 SMap.iter begin fun x tc ->
6552 if tc.ttc_type
= None
then
6553 Errors.implement_abstract ~
is_final p (fst
tc.ttc_name
) "type constant" x
6556 and check_extend_abstract_const ~
is_final p smap
=
6557 SMap.iter begin fun x cc
->
6558 match cc
.cc_type
with
6559 | r, _ when cc
.cc_abstract
&& not cc
.cc_synthesized
->
6560 Errors.implement_abstract ~
is_final p (Reason.to_pos
r) "constant" x
6571 | Tvarray_or_darray
_
6584 and typeconst_def
env {
6585 c_tconst_name
= (pos, _) as id;
6586 c_tconst_constraint
;
6589 let env, cstr = opt
Phase.hint_locl
env c_tconst_constraint
in
6590 let env, ty = opt
Phase.hint_locl
env c_tconst_type
in
6592 Option.map2
ty cstr ~
f:(Type.sub_type
pos Reason.URtypeconst_cstr
env)
6595 T.c_tconst_name
= id;
6596 T.c_tconst_constraint
= c_tconst_constraint
;
6597 T.c_tconst_type
= c_tconst_type
;
6600 and class_const_def
env (h, id, e) =
6601 let env, ty, opt_expected
=
6603 | None
-> env, Env.fresh_type
(), None
6605 let env, ty = Phase.hint_locl
env h in
6606 env, ty, Some
(fst
id, Reason.URhint
, ty)
6610 let env, te, ty'
= expr ?
expected:opt_expected
env e in
6611 ignore
(Type.sub_type
(fst
id) Reason.URhint
env ty'
ty);
6612 (h, id, Some
te), ty'
6616 and class_constr_def
env c =
6617 let env = { env with Env.inside_constructor
= true } in
6618 Option.map
c.c_constructor
(method_def
env)
6620 and class_implements_type
env c1 ctype2
=
6622 List.map
(fst c1
.c_tparams
) begin fun (_, (p, s), _) ->
6623 (Reason.Rwitness
p, Tgeneric
s)
6625 let r = Reason.Rwitness
(fst c1
.c_name
) in
6626 let ctype1 = r, Tapply
(c1
.c_name
, params) in
6627 Typing_extends.check_implements
env ctype2
ctype1;
6630 (* Type-check a property declaration, with optional initializer *)
6631 and class_var_def
env ~is_static
c cv
=
6632 (* First pick up and localize the hint if it exists *)
6634 match cv
.cv_type
with
6637 | Some
(p, _ as cty
) ->
6639 (* If this is an XHP attribute and we're in strict mode,
6640 relax to partial mode to allow the use of the "array"
6641 annotation without specifying type parameters. Until
6642 recently HHVM did not allow "array" with type parameters
6643 in XHP attribute declarations, so this is a temporary
6644 hack to support existing code for now. *)
6645 (* Task #5815945: Get rid of this Hack *)
6646 if cv
.cv_is_xhp
&& (Env.is_strict
env)
6647 then Env.set_mode
env FileInfo.Mpartial
6649 let cty = TI.instantiable_hint
env cty in
6650 let env, cty = Phase.localize_with_self
env cty in
6651 env, Some
(p, Reason.URhint
, cty) in
6652 (* Next check the expression, passing in expected type if present *)
6653 let env, typed_cv_expr
, ty =
6654 match cv
.cv_expr
with
6655 | None
-> env, None
, Env.fresh_type
()
6657 let env, te, ty = expr ?
expected env e in
6658 (* Check that the inferred type is a subtype of the expected type.
6659 * Eventually this will be the responsibility of `expr`
6664 | Some
(p, ur
, cty) -> Type.sub_type
p ur
env ty cty in
6668 then Typing_attributes.check_def
env new_object
6669 SN.AttributeKinds.staticProperty cv
.cv_user_attributes
6670 else Typing_attributes.check_def
env new_object
6671 SN.AttributeKinds.instProperty cv
.cv_user_attributes
in
6673 if Option.is_none cv
.cv_type
6675 if Env.is_strict
env
6676 then Errors.add_a_typehint
(fst cv
.cv_id
)
6678 let pos, name = cv
.cv_id
in
6679 let name = if is_static
then "$" ^
name else name in
6680 let var_type = Reason.Rwitness
pos, Typing_utils.tany env in
6681 if Option.is_none cv
.cv_expr
6682 then Typing_suggest.uninitialized_member
(snd
c.c_name
) name env var_type ty
6683 else Typing_suggest.save_member
name env var_type ty
6686 T.cv_final
= cv
.cv_final
;
6687 T.cv_is_xhp
= cv
.cv_is_xhp
;
6688 T.cv_visibility
= cv
.cv_visibility
;
6689 T.cv_type
= cv
.cv_type
;
6691 T.cv_expr
= typed_cv_expr
;
6692 T.cv_user_attributes
= List.map cv
.cv_user_attributes
(user_attribute
env);
6696 and localize_where_constraints
6697 ~
ety_env (env:Env.env) (where_constraints
:Nast.where_constraint list
) =
6698 let add_constraint env (h1
, ck
, h2
) =
6700 Phase.localize
env (Decl_hint.hint
env.Env.decl_env h1
) ~
ety_env in
6702 Phase.localize
env (Decl_hint.hint
env.Env.decl_env h2
) ~
ety_env in
6703 SubType.add_constraint (fst h1
) env ck
ty1 ty2
6705 List.fold_left where_constraints ~
f:add_constraint ~init
:env
6707 and add_constraints
p env constraints
=
6708 let add_constraint env (ty1, ck
, ty2) =
6709 SubType.add_constraint p env ck
ty1 ty2 in
6710 List.fold_left constraints ~
f:add_constraint ~init
: env
6712 and user_attribute
env ua
=
6713 let typed_ua_params =
6714 List.map ua
.ua_params
(fun e -> let _env, te, _ty
= expr env e in te) in
6716 T.ua_name
= ua
.ua_name
;
6717 T.ua_params
= typed_ua_params;
6720 and method_def
env m
=
6721 (* reset the expression dependent display ids for each method body *)
6722 Reason.expr_display_id_map
:= IMap.empty
;
6723 Typing_hooks.dispatch_enter_method_def_hook m
;
6724 let pos = fst m
.m_name
in
6725 let env = Env.reinitialize_locals
env in
6726 let env = Env.set_env_function_pos
env pos in
6727 let env = Typing_attributes.check_def
env new_object
6728 SN.AttributeKinds.mthd m
.m_user_attributes
in
6729 let reactive = fun_reactivity env.Env.decl_env m
.m_user_attributes
in
6731 TUtils.fun_mutable m
.m_user_attributes
||
6732 (* <<__Mutable>> is implicit on constructors *)
6733 snd m
.m_name
= SN.Members.__construct
in
6734 let env = Env.set_env_reactive
env reactive in
6735 let env = Env.set_fun_mutable
env mut in
6737 { (Phase.env_with_self
env) with from_class
= Some CIstatic
; } in
6738 let env, constraints
=
6739 Phase.localize_generic_parameters_with_bounds
env m
.m_tparams
6741 TI.check_tparams_instantiable
env m
.m_tparams
;
6742 let env = add_constraints
pos env constraints
in
6744 localize_where_constraints ~
ety_env env m
.m_where_constraints
in
6746 if Env.is_static
env then env
6747 else Env.set_local
env this
(Env.get_self
env) in
6749 match Env.get_class
env (Env.get_self_id
env) with
6752 (* Mark $this as a using variable if it has a disposable type *)
6753 if c.tc_is_disposable
6754 then Env.set_using_var
env this
6756 let env = Env.clear_params
env in
6757 let env, ty = match m
.m_ret
with
6759 env, Typing_return.make_default_return
env m
.m_name
6761 let ret = TI.instantiable_hint
env ret in
6762 (* If a 'this' type appears it needs to be compatible with the
6766 { (Phase.env_with_self
env) with
6767 from_class
= Some CIstatic
} in
6768 Phase.localize ~
ety_env env ret in
6769 let return = Typing_return.make_info m
.m_fun_kind m
.m_user_attributes
env
6770 ~is_explicit
:(Option.is_some m
.m_ret
) ~is_by_ref
:m
.m_ret_by_ref
ty in
6771 TI.check_params_instantiable
env m
.m_params
;
6772 let env, param_tys
=
6773 List.map_env
env m
.m_params
(make_param_local_ty m
.m_user_attributes
) in
6774 if Env.is_strict
env then begin
6775 List.iter2_exn ~
f:(check_param
env) m
.m_params param_tys
;
6777 Typing_memoize.check_method
env m
;
6778 let env, typed_params
=
6779 List.map_env
env (List.zip_exn param_tys m
.m_params
) bind_param in
6780 let env, t_variadic
= match m
.m_variadic
with
6781 | FVvariadicArg vparam
->
6782 TI.check_param_instantiable
env vparam
;
6783 let env, ty = make_param_local_ty m
.m_user_attributes
env vparam
in
6784 if Env.is_strict
env then
6785 check_param
env vparam
ty;
6786 let env, t_variadic
= bind_param env (ty, vparam
) in
6787 env, (T.FVvariadicArg t_variadic
)
6788 | FVellipsis
p -> env, T.FVellipsis
p
6789 | FVnonVariadic
-> env, T.FVnonVariadic
in
6790 let nb = Nast.assert_named_body m
.m_body
in
6791 let local_tpenv = env.Env.lenv.Env.tpenv
in
6793 fun_ ~abstract
:m
.m_abstract
env return pos nb m
.m_fun_kind
in
6795 Env.check_todo
env in
6799 snd m
.m_name
= SN.Members.__destruct
6800 || snd m
.m_name
= SN.Members.__construct
->
6801 Some
(pos, Happly
((pos, "void"), []))
6802 | None
when Env.is_strict
env ->
6803 Typing_return.suggest_return
env pos return.Typing_env_return_info.return_type
; None
6804 | None
-> let (pos, id) = m
.m_name
in
6805 let id = (Env.get_self_id
env) ^
"::" ^
id in
6806 Typing_suggest.save_fun_or_method
(pos, id);
6809 Typing_return.async_suggest_return
(m
.m_fun_kind
) hint
(fst m
.m_name
); m
.m_ret in
6810 Env.log_anonymous
env;
6811 let m = { m with m_ret = m_ret; } in
6812 Typing_hooks.dispatch_exit_method_def_hook
m;
6814 T.m_annotation
= Env.save
local_tpenv env;
6815 T.m_final
= m.m_final
;
6816 T.m_abstract
= m.m_abstract
;
6817 T.m_visibility
= m.m_visibility
;
6818 T.m_name
= m.m_name
;
6819 T.m_tparams
= m.m_tparams
;
6820 T.m_where_constraints
= m.m_where_constraints
;
6821 T.m_variadic
= t_variadic
;
6822 T.m_params
= typed_params
;
6823 T.m_fun_kind
= m.m_fun_kind
;
6824 T.m_user_attributes
= List.map
m.m_user_attributes
(user_attribute
env);
6826 T.m_body
= T.NamedBody
{
6828 T.fnb_unsafe
= nb.fnb_unsafe
;
6830 T.m_ret_by_ref
= m.m_ret_by_ref
;
6833 and typedef_def tcopt typedef
=
6834 let env = EnvFromDef.typedef_env tcopt typedef
in
6835 let tdecl = Env.get_typedef
env (snd typedef
.t_name
) in
6836 add_decl_errors
(Option.(map
tdecl (fun tdecl -> value_exn
tdecl.td_decl_errors
)));
6837 let env, constraints
=
6838 Phase.localize_generic_parameters_with_bounds
env typedef
.t_tparams
6839 ~
ety_env:(Phase.env_with_self
env) in
6840 let env = add_constraints
(fst typedef
.t_name
) env constraints
in
6841 NastCheck.typedef
env typedef
;
6846 t_constraint
= tcstr
;
6848 t_user_attributes
= _;
6852 let ty = TI.instantiable_hint
env hint
in
6853 let env, ty = Phase.localize_with_self
env ty in
6854 let env = begin match tcstr
with
6856 let cstr = TI.instantiable_hint
env tcstr
in
6857 let env, cstr = Phase.localize_with_self
env cstr in
6858 Typing_ops.sub_type t_pos
Reason.URnewtype_cstr
env ty cstr
6861 let env = begin match hint
with
6862 | pos, Hshape
{ nsi_allows_unknown_fields
=_; nsi_field_map
} ->
6863 check_shape_keys_validity
env pos (ShapeMap.keys nsi_field_map
)
6866 let env = Typing_attributes.check_def
env new_object
6867 SN.AttributeKinds.typealias typedef
.t_user_attributes
in
6869 T.t_annotation
= Env.save
env.Env.lenv.Env.tpenv
env;
6870 T.t_name
= typedef
.t_name
;
6871 T.t_mode
= typedef
.t_mode
;
6872 T.t_vis
= typedef
.t_vis
;
6873 T.t_user_attributes
= List.map typedef
.t_user_attributes
(user_attribute
env);
6874 T.t_constraint
= typedef
.t_constraint
;
6875 T.t_kind
= typedef
.t_kind
;
6876 T.t_tparams
= typedef
.t_tparams
;
6879 and gconst_def tcopt cst
=
6880 let env = EnvFromDef.gconst_env tcopt cst
in
6881 add_decl_errors
(Option.map
(Env.get_gconst
env (snd cst
.cst_name
)) ~
f:snd
);
6883 let typed_cst_value, env =
6884 match cst
.cst_value
with
6887 match cst
.cst_type
with
6889 let ty = TI.instantiable_hint
env hint
in
6890 let env, dty
= Phase.localize_with_self
env ty in
6891 let env, te, value_type
=
6892 expr ~
expected:(fst hint
, Reason.URhint
, dty
) env value in
6893 let env = Typing_utils.sub_type
env value_type dty
in
6896 let env, te, _value_type
= expr env value in
6899 { T.cst_annotation
= Env.save
env.Env.lenv.Env.tpenv
env;
6900 T.cst_mode
= cst
.cst_mode
;
6901 T.cst_name
= cst
.cst_name
;
6902 T.cst_type
= cst
.cst_type
;
6903 T.cst_value
= typed_cst_value;
6904 T.cst_is_define
= cst
.cst_is_define
;
6907 (* Calls the method of a class, but allows the f callback to override the
6908 * return value type *)
6909 and overload_function make_call fpos p env ((), class_id
) method_id
el uel
f =
6910 let env, tcid
, ty = static_class_id ~check_constraints
:false p env class_id
in
6911 let env, _tel
, _ = exprs ~is_func_arg
:true env el in
6913 class_get ~
is_method:true ~is_const
:false env ty method_id class_id
in
6914 (* call the function as declared to validate arity and input types,
6915 but ignore the result and overwrite with custom one *)
6916 let (env, tel, tuel
, res
), has_error
= Errors.try_with_error
6917 (* TODO: Should we be passing hints here *)
6918 (fun () -> (call ~
expected:None
p env fty el uel
), false)
6919 (fun () -> (env, [], [], (Reason.Rwitness
p, Typing_utils.tany env)), true) in
6920 (* if there are errors already stop here - going forward would
6921 * report them twice *)
6923 then env, T.make_typed_expr
p res
T.Any
, res
6925 let env, ty = f env fty res
el in
6928 | r, Tfun
ft -> r, Tfun
{ft with ft_ret
= ty}
6930 let te = T.make_typed_expr
fpos fty (T.Class_const
(tcid
, method_id
)) in
6931 make_call env te [] tel tuel
ty
6933 and update_array_type ?lhs_of_null_coalesce
p env e1 e2
valkind =
6934 let access_type = Typing_arrays.static_array_access
env e2
in
6936 Typing_arrays.update_array_type
p access_type in
6938 | `lvalue
| `lvalue_subexpr
->
6940 raw_expr ~
valkind:`lvalue_subexpr ~in_cond
:false env e1
in
6941 let env, ty1 = type_mapper env ty1 in
6943 | (_, Lvar
(_, x)) ->
6944 (* type_mapper has updated the type in ty1 typevars, but we
6945 need to update the local variable type too *)
6946 let env, ty1 = set_valid_rvalue
p env x ty1 in
6948 | _ -> env, te1, ty1
6951 raw_expr ~in_cond
:false ?lhs_of_null_coalesce
env e1
6953 let nast_to_tast opts nast
=
6954 let convert_def = function
6955 | Nast.Fun
f -> T.Fun
(fun_def opts
f)
6956 | Nast.Constant gc
-> T.Constant
(gconst_def opts gc
)
6957 | Nast.Typedef td
-> T.Typedef
(typedef_def opts td
)
6959 match class_def opts
c with
6960 | Some
c -> T.Class
c
6961 | None
-> failwith
@@ Printf.sprintf
6962 "Error in declaration of class: %s" (snd
c.c_name
)
6964 let tast = List.map nast
convert_def in
6965 Tast_check.program
tast;