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