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 SN
= Naming_special_names
34 module TVis
= Typing_visibility
35 module TNBody
= Typing_naming_body
37 module Phase
= Typing_phase
38 module Subst
= Decl_subst
39 module ExprDepTy
= Typing_dependent_type.ExprDepTy
40 module TCO
= TypecheckerOptions
41 module EnvFromDef
= Typing_env_from_def.EnvFromDef
(Nast.Annotations
)
42 module TySet
= Typing_set
43 module C
= Typing_continuations
45 module Try
= Typing_try
46 module TR
= Typing_reactivity
47 module FL
= FeatureLogging
48 module MakeType
= Typing_make_type
49 module Cls
= Decl_provider.Class
51 (* Maps a Nast to a Tast where every type is Tany.
52 Used to produce a Tast for unsafe code without inferring types for it. *)
53 module NastTanyMapper
=
54 Aast_mapper.MapAnnotatedAST
(Nast.Annotations
)(Tast.Annotations
)
56 let map_funcbody_annotation an
=
58 | Nast.Annotations.FuncBodyAnnotation.NamedWithUnsafeBlocks
->
59 Tast.Annotations.FuncBodyAnnotation.HasUnsafeBlocks
60 | Nast.Annotations.FuncBodyAnnotation.Named
->
61 Tast.Annotations.FuncBodyAnnotation.NoUnsafeBlocks
62 | Nast.Annotations.FuncBodyAnnotation.Unnamed _
->
63 failwith
"Should not map over unnamed body"
67 map_env_annotation
= (fun () -> Tast.empty_saved_env tcopt
);
68 map_expr_annotation
= (fun p
-> p
, (Reason.Rnone
, Tany
));
69 map_funcbody_annotation = map_funcbody_annotation;
72 (*****************************************************************************)
74 (*****************************************************************************)
76 (* A guess as to the last position we were typechecking, for use in debugging,
77 * such as figuring out what a runaway hh_server thread is doing. Updated
78 * only best-effort -- it's an approximation to point debugging in the right
79 * direction, nothing more. *)
80 let debug_last_pos = ref Pos.none
81 let debug_print_last_pos _
= print_endline
(Pos.string (Pos.to_absolute
84 (****************************************************************************)
86 (****************************************************************************)
88 let expr_hook = ref None
90 let with_expr_hook hook f
= with_context
91 ~enter
: (fun () -> expr_hook := Some hook
)
92 ~exit
: (fun () -> expr_hook := None
)
95 (*****************************************************************************)
97 (*****************************************************************************)
99 let suggest env p ty
=
100 let ty = Typing_expand.fully_expand env
ty in
101 (match Typing_print.suggest ty with
102 | "..." -> Errors.expecting_type_hint p
103 | ty -> Errors.expecting_type_hint_suggest p
ty
106 let err_witness env p
= Reason.Rwitness p
, Typing_utils.terr env
108 (* When typing an expression, we optionally pass in the type
109 * that we *expect* the expression to have.
112 Pos.t
* Reason.ureason
* locl
ty
114 let expr_error env p r
=
115 let ty = (r
, Typing_utils.terr env
) in
116 env
, T.make_typed_expr p
ty T.Any
, ty
118 let expr_any env p r
=
119 let ty = (r
, Typing_utils.tany env
) in
120 env
, T.make_typed_expr p
ty T.Any
, ty
122 let compare_field_kinds x y
=
124 | Nast.AFvalue
(p1
, _
), Nast.AFkvalue
((p2
, _
), _
)
125 | Nast.AFkvalue
((p2
, _
), _
), Nast.AFvalue
(p1
, _
) ->
126 Errors.field_kinds p1 p2
;
131 let check_consistent_fields x l
=
132 List.for_all l
(compare_field_kinds x
)
134 let unbound_name env
(pos
, name
) =
135 match Env.get_mode env
with
136 | FileInfo.Mstrict
| FileInfo.Mexperimental
->
137 (Errors.unbound_name_typing pos name
;
138 expr_error env pos
(Reason.Rwitness pos
))
140 | FileInfo.Mdecl
| FileInfo.Mpartial
| FileInfo.Mphp
->
141 expr_any env pos
(Reason.Rwitness pos
)
143 (* Is this type Traversable<vty> or Container<vty> for some vty? *)
144 let get_value_collection_inst ty =
146 | (_
, Tclass
((_
, c
), _
, [vty
])) when
147 c
= SN.Collections.cTraversable
||
148 c
= SN.Collections.cContainer
->
150 (* If we're expecting a mixed or a nonnull then we can just assume
151 * that the element type is mixed *)
153 Some
(MakeType.mixed
Reason.Rnone
)
159 (* Is this type KeyedTraversable<kty,vty>
160 * or KeyedContainer<kty,vty>
163 let get_key_value_collection_inst ty =
165 | (_
, Tclass
((_
, c
), _
, [kty
; vty
])) when
166 c
= SN.Collections.cKeyedTraversable
||
167 c
= SN.Collections.cKeyedContainer
->
169 (* If we're expecting a mixed or a nonnull then we can just assume
170 * that the value and key types are mixed *)
172 let mixed = MakeType.mixed Reason.Rnone
in
179 (* Is this type varray<vty> or a supertype for some vty? *)
180 let get_varray_inst ty =
182 (* It's varray<vty> *)
183 | (_
, Tarraykind
(AKvarray vty
)) -> Some vty
184 | _
-> get_value_collection_inst ty
186 (* Is this type one of the value collection types with element type vty? *)
187 let get_vc_inst vc_kind
ty =
189 | (_
, Tclass
((_
, c
), _
, [vty
]))
190 when c
= vc_kind_to_name vc_kind
-> Some vty
191 | _
-> get_value_collection_inst ty
193 (* Is this type array<vty> or a supertype for some vty? *)
194 let get_akvec_inst ty =
196 | (_
, Tarraykind
(AKvec vty
)) -> Some vty
197 | _
-> get_value_collection_inst ty
199 (* Is this type array<kty,vty> or a supertype for some kty and vty? *)
200 let get_akmap_inst ty =
202 | (_
, Tarraykind
(AKmap
(kty
, vty
))) -> Some
(kty
, vty
)
203 | _
-> get_key_value_collection_inst ty
205 (* Is this type one of the three key-value collection types
206 * e.g. dict<kty,vty> or a supertype for some kty and vty? *)
207 let get_kvc_inst kvc_kind
ty =
209 | (_
, Tclass
((_
, c
), _
, [kty
; vty
]))
210 when c
= kvc_kind_to_name kvc_kind
-> Some
(kty
, vty
)
211 | _
-> get_key_value_collection_inst ty
213 (* Is this type darray<kty, vty> or a supertype for some kty and vty? *)
214 let get_darray_inst ty =
216 (* It's darray<kty, vty> *)
217 | (_
, Tarraykind
(AKdarray
(kty
, vty
))) -> Some
(kty
, vty
)
218 | _
-> get_key_value_collection_inst ty
220 let with_timeout env fun_name ~
(do_
: Env.env
-> 'b
): 'b
option =
221 let timeout = (Env.get_tcopt env
).GlobalOptions.tco_timeout
in
222 if timeout = 0 then Some
(do_ env
)
224 let big_envs = ref [] in
225 let env = { env with Env.big_envs } in
226 Timeout.with_timeout ~
timeout
227 ~on_timeout
:(fun _
->
228 List.iter
!big_envs (fun (p
, env) ->
229 Typing_log.log_key
"WARN: environment is too big."; Typing_log.hh_show_env p
env);
230 Errors.typechecker_timeout fun_name
timeout;
232 ~do_
:(fun _
-> Some
(do_
env))
234 (*****************************************************************************)
235 (* Handling function/method arguments *)
236 (*****************************************************************************)
237 let param_has_attribute param attr
=
238 List.exists param
.param_user_attributes
239 (fun { ua_name
; _
} -> attr
= snd ua_name
)
241 let has_accept_disposable_attribute param
=
242 param_has_attribute param
SN.UserAttributes.uaAcceptDisposable
244 let get_param_mutability param
=
245 if param_has_attribute param
SN.UserAttributes.uaMutable
246 then Some Param_borrowed_mutable
247 else if param_has_attribute param
SN.UserAttributes.uaMaybeMutable
248 then Some Param_maybe_mutable
249 else if param_has_attribute param
SN.UserAttributes.uaOwnedMutable
250 then Some Param_owned_mutable
253 (* Check whether this is a function type that (a) either returns a disposable
254 * or (b) has the <<__ReturnDisposable>> attribute
256 let is_return_disposable_fun_type env ty =
257 match Env.expand_type
env ty with
258 | _env
, (_
, Tfun ft
) ->
259 ft
.ft_return_disposable
|| Option.is_some
(Typing_disposable.is_disposable_type
env ft
.ft_ret
)
262 let enforce_param_not_disposable env param
ty =
263 if has_accept_disposable_attribute param
then ()
265 let p = param
.param_pos
in
266 match Typing_disposable.is_disposable_type
env ty with
268 Errors.invalid_disposable_hint
p (strip_ns class_name
)
272 let param_has_at_most_rx_as_func p =
273 let module UA
= SN.UserAttributes
in
274 Attributes.mem
UA.uaAtMostRxAsFunc
p.param_user_attributes
276 let fun_reactivity env attrs params
=
277 let r = Decl_fun_utils.fun_reactivity env attrs
in
278 let module UA
= Naming_special_names.UserAttributes
in
281 (* if at least one of parameters has <<__AtMostRxAsFunc>> attribute -
282 treat function reactivity as generic that is determined from the reactivity
283 of arguments annotated with __AtMostRxAsFunc. Declared reactivity is used as a
284 upper boundary of the reactivity function can have. *)
285 if List.exists params ~f
:param_has_at_most_rx_as_func
290 (* if at least one of arguments have <<__OnlyRxIfImpl>> attribute -
291 treat function reactivity as conditional that is determined at the callsite *)
292 if List.exists params
293 ~f
:(fun { param_user_attributes
= p; _
} ->
294 Attributes.mem
UA.uaOnlyRxIfImpl
p)
299 type array_ctx
= NoArray
| ElementAssignment
| ElementAccess
301 (* This function is used to determine the type of an argument.
302 * When we want to type-check the body of a function, we need to
303 * introduce the type of the arguments of the function in the environment
304 * Let's take an example, we want to check the code of foo:
306 * function foo(int $x): int {
307 * // CALL TO make_param_type on (int $x)
308 * // Now we know that the type of $x is int
310 * return $x; // in the environment $x is an int, the code is correct
313 * When we localize, we want to resolve to "static" or "$this" depending on
314 * the context. Even though we are passing in CIstatic, resolve_with_class_id
315 * is smart enough to know what to do. Why do this? Consider the following
318 * abstract const type T;
320 * private this::T $val;
322 * final public function __construct(this::T $x) {
326 * public static function create(this::T $x): this {
327 * return new static($x);
331 * class D extends C { const type T = int; }
333 * In __construct() we want to be able to assign $x to $this->val. The type of
334 * $this->val will expand to '$this::T', so we need $x to also be '$this::T'.
335 * We can do this soundly because when we construct a new class such as,
336 * 'new D(0)' we can determine the late static bound type (D) and resolve
337 * 'this::T' to 'D::T' which is int.
339 * A similar line of reasoning is applied for the static method create.
341 let make_param_local_ty env param
=
343 { (Phase.env_with_self
env) with from_class
= Some CIstatic
; } in
345 match param
.param_hint
with
347 let r = Reason.Rwitness param
.param_pos
in
348 env, (r, TUtils.tany
env)
350 let ty = Decl_hint.hint
env.Env.decl_env x
in
352 Decl_fun_utils.condition_type_from_attributes
env.Env.decl_env param
.param_user_attributes
in
353 begin match condition_type with
354 | Some
condition_type ->
355 let env, ty = Phase.localize ~
ety_env env ty in
356 begin match TR.try_substitute_type_with_condition
env condition_type ty with
360 | _
when Attributes.mem
SN.UserAttributes.uaAtMostRxAsFunc param
.param_user_attributes
->
361 let env, ty = Phase.localize ~
ety_env env ty in
362 (* expand type to track aliased function types *)
363 let env, expanded_ty
= Env.expand_type
env ty in
364 let adjusted_ty = make_function_type_rxvar expanded_ty
in
365 env, if phys_equal
adjusted_ty expanded_ty
then ty else adjusted_ty
367 Phase.localize ~
ety_env env ty
370 let ty = match ty with
371 | _
, t
when param
.param_is_variadic
->
372 (* when checking the body of a function with a variadic
373 * argument, "f(C ...$args)", $args is a varray<C> *)
374 let r = Reason.Rvar_param param
.param_pos
in
375 let arr_values = r, t
in
376 r, Tarraykind
(AKvarray
arr_values)
379 Typing_reactivity.disallow_atmost_rx_as_rxfunc_on_non_functions
env param
ty;
382 (* Given a localized parameter type and parameter information, infer
383 * a type for the parameter default expression (if present) and check that
384 * it is a subtype of the parameter type (if present). If no parameter type
385 * is specified, then union with Tany. (So it's as though we did a conditional
386 * assignment of the default expression to the parameter).
387 * Set the type of the parameter in the locals environment *)
388 let rec bind_param env (ty1
, param
) =
389 let env, param_te
, ty1
=
390 match param
.param_expr
with
392 Typing_suggest.save_param
(param
.param_name
) env ty1
(Reason.none
, Tany
);
395 let env, te
, ty2
= expr ~expected
:(param
.param_pos
, Reason.URparam
, ty1
) env e
in
396 Typing_sequencing.sequence_check_expr e
;
397 Typing_suggest.save_param
(param
.param_name
) env ty1 ty2
;
399 if Option.is_none param
.param_hint
400 (* In this case ty1 must be Tany, so just union it with the type of
401 * the default expression *)
402 then Union.union
env ty1 ty2
403 (* Otherwise we have an explicit type, and the default expression type
404 * must be a subtype *)
406 let env = Type.sub_type param
.param_pos
Reason.URhint
env ty2 ty1
in
411 T.param_annotation
= T.make_expr_annotation param
.param_pos ty1
;
412 T.param_hint
= param
.param_hint
;
413 T.param_is_reference
= param
.param_is_reference
;
414 T.param_is_variadic
= param
.param_is_variadic
;
415 T.param_pos
= param
.param_pos
;
416 T.param_name
= param
.param_name
;
417 T.param_expr
= param_te
;
418 T.param_callconv
= param
.param_callconv
;
419 T.param_user_attributes
= List.map param
.param_user_attributes
(user_attribute
env);
421 let mode = get_param_mode param
.param_is_reference param
.param_callconv
in
422 let id = Local_id.make_unscoped param
.param_name
in
423 let env = Env.set_local
env id ty1
in
424 let env = Env.set_param
env id (ty1
, mode) in
425 let env = if has_accept_disposable_attribute param
426 then Env.set_using_var
env id else env in
428 match get_param_mutability param
with
429 | Some Param_borrowed_mutable
->
430 Env.add_mutable_var
env id (param
.param_pos
, Typing_mutability_env.Borrowed
)
431 | Some Param_owned_mutable
->
432 Env.add_mutable_var
env id (param
.param_pos
, Typing_mutability_env.Mutable
)
433 | Some Param_maybe_mutable
->
434 Env.add_mutable_var
env id (param
.param_pos
, Typing_mutability_env.MaybeMutable
)
436 Env.add_mutable_var
env id (param
.param_pos
, Typing_mutability_env.Immutable
)
440 (* In strict mode, we force you to give a type declaration on a parameter *)
441 (* But the type checker is nice: it makes a suggestion :-) *)
442 and check_param
env param
ty =
443 let env = Typing_attributes.check_def
env new_object
444 SN.AttributeKinds.parameter param
.param_user_attributes
in
445 match param
.param_hint
with
446 | None
-> suggest env param
.param_pos
ty
448 (* We do not permit hints to implement IDisposable or IAsyncDisposable *)
449 enforce_param_not_disposable env param
ty
451 and check_inout_return
env =
452 let params = Local_id.Map.elements
(Env.get_params
env) in
453 List.fold
params ~init
:env ~f
:begin fun env (id, ((r, ty), mode)) ->
456 (* Whenever the function exits normally, we require that each local
457 * corresponding to an inout parameter be compatible with the original
458 * type for the parameter (under subtyping rules). *)
459 let local_ty = Env.get_local
env id in
460 let env, ety
= Env.expand_type
env local_ty in
461 let pos = Reason.to_pos
(fst ety
) in
462 let param_ty = Reason.Rinout_param
(Reason.to_pos
r), ty in
463 Type.sub_type
pos Reason.URassign_inout
env ety
param_ty
467 and add_decl_errors
= function
469 | Some errors
-> Errors.merge_into_current errors
471 (*****************************************************************************)
472 (* Now we are actually checking stuff! *)
473 (*****************************************************************************)
474 and fun_def tcopt f
: Tast.fun_def
option =
475 let env = EnvFromDef.fun_env tcopt f
in
476 with_timeout env f
.f_name ~do_
:begin fun env ->
477 (* reset the expression dependent display ids for each function body *)
478 Reason.expr_display_id_map
:= IMap.empty
;
479 let pos = fst f
.f_name
in
480 let nb = TNBody.func_body f
in
481 add_decl_errors
(Option.map
482 (Env.get_fun
env (snd f
.f_name
))
483 ~f
:(fun x
-> Option.value_exn x
.ft_decl_errors
)
485 let env = Env.set_env_function_pos
env pos in
486 let env = Typing_attributes.check_def
env new_object
SN.AttributeKinds.fn f
.f_user_attributes
in
487 let reactive = fun_reactivity env.Env.decl_env f
.f_user_attributes f
.f_params
in
488 let mut = TUtils.fun_mutable f
.f_user_attributes
in
489 let env = Env.set_env_reactive
env reactive in
490 let env = Env.set_fun_mutable
env mut in
491 NastCheck.fun_
env f
nb;
492 (* Fresh type environment is actually unnecessary, but I prefer to
493 * have a guarantee that we are using a clean typing environment. *)
494 let tfun_def = Env.fresh_tenv
env (
496 let ety_env = Phase.env_with_self
env in
497 let env, constraints
=
498 Phase.localize_generic_parameters_with_bounds
env f
.f_tparams
500 let env = add_constraints
pos env constraints
in
502 Phase.localize_where_constraints ~
ety_env env f
.f_where_constraints
in
506 env, (Reason.Rwitness
pos, Typing_utils.tany
env)
508 let ty = Decl_hint.hint
env.Env.decl_env ret
in
509 Phase.localize_with_self
env ty in
510 let return = Typing_return.make_info f
.f_fun_kind f
.f_user_attributes
env
511 ~is_explicit
:(Option.is_some f
.f_ret
) ty in
513 List.map_env
env f
.f_params
make_param_local_ty in
514 if Env.is_strict
env then
515 List.iter2_exn ~f
:(check_param
env) f
.f_params param_tys
;
516 Typing_memoize.check_function
env f
;
517 let env, typed_params
= List.map_env
env (List.zip_exn param_tys f
.f_params
)
519 let env, t_variadic
= match f
.f_variadic
with
520 | FVvariadicArg vparam
->
521 let env, ty = make_param_local_ty env vparam
in
522 if Env.is_strict
env then
523 check_param
env vparam
ty;
524 let env, t_vparam
= bind_param env (ty, vparam
) in
525 env, T.FVvariadicArg t_vparam
527 if Env.is_strict
env then
528 Errors.ellipsis_strict_mode ~require
:`Type_and_param_name
pos;
530 | FVnonVariadic
-> env, T.FVnonVariadic
in
531 let local_tpenv = env.Env.lenv
.Env.tpenv
in
532 let env, tb
= fun_
env return pos nb f
.f_fun_kind
in
533 let env = SubType.solve_all_unsolved_tyvars
env in
534 Typing_subtype.log_prop
env;
535 let env = Env.check_todo
env in
536 (* restore original reactivity *)
537 let env = Env.set_env_reactive
env reactive in
538 begin match f
.f_ret
with
539 | None
when Env.is_strict
env ->
540 Typing_return.suggest_return
env pos return.Typing_env_return_info.return_type
541 | None
-> Typing_suggest.save_fun_or_method f
.f_name
543 Typing_return.async_suggest_return
(f
.f_fun_kind
) hint
pos
545 let filename = Pos.filename (fst f
.f_name
) in
546 let droot = env.Env.decl_env
.Decl_env.droot in
547 let file_attrs = file_attributes tcopt
filename f
.f_mode
droot f
.f_file_attributes
in
549 T.f_annotation
= Env.save
local_tpenv env;
554 T.f_tparams
= List.map f
.f_tparams
(type_param
env);
555 T.f_where_constraints
= f
.f_where_constraints
;
556 T.f_variadic
= t_variadic
;
557 T.f_params
= typed_params
;
558 T.f_fun_kind
= f
.f_fun_kind
;
559 T.f_file_attributes
= file_attrs;
560 T.f_user_attributes
= List.map f
.f_user_attributes
(user_attribute
env);
561 T.f_body
= { T.fb_ast
= tb
; fb_annotation
= map_funcbody_annotation nb.fb_annotation
};
562 T.f_external
= f
.f_external
;
563 T.f_namespace
= f
.f_namespace
;
564 T.f_doc_comment
= f
.f_doc_comment
;
565 T.f_static
= f
.f_static
;
567 Typing_lambda_ambiguous.suggest_fun_def
env fundef
570 end (* with_timeout *)
572 (*****************************************************************************)
573 (* function used to type closures, functions and methods *)
574 (*****************************************************************************)
576 and fun_ ?
(abstract
=false) env return pos named_body f_kind
=
577 Env.with_env
env begin fun env ->
578 debug_last_pos := pos;
579 let env = Env.set_return
env return in
580 let env = Env.set_fn_kind
env f_kind
in
581 let env, tb
= block
env named_body
.fb_ast
in
582 Typing_sequencing.sequence_check_block named_body
.fb_ast
;
583 let { Typing_env_return_info.return_type
= ret
; _
} = Env.get_return
env in
585 if not
@@ LEnv.has_next
env ||
587 Nast.named_body_is_unsafe named_body
589 else fun_implicit_return
env pos ret f_kind
in
590 debug_last_pos := Pos.none
;
594 and fun_implicit_return
env pos ret
= function
595 | Ast.FGenerator
| Ast.FAsyncGenerator
-> env
598 (* A function without a terminal block has an implicit return; the
600 let env = check_inout_return
env in
601 let r = Reason.Rno_return
pos in
602 let rty = MakeType.void
r in
603 Typing_return.implicit_return
env pos ~expected
:ret ~actual
:rty
605 (* An async function without a terminal block has an implicit return;
606 * the Awaitable<void> type *)
607 let r = Reason.Rno_return_async
pos in
608 let rty = MakeType.awaitable
r (MakeType.void
r) in
609 Typing_return.implicit_return
env pos ~expected
:ret ~actual
:rty
611 and block
env (stl
: block
) = List.map_env
env stl ~f
:stmt
613 (* Set a local; must not be already assigned if it is a using variable *)
614 and set_local ?
(is_using_clause
= false) env (pos,x
) ty =
615 if Env.is_using_var
env x
618 then Errors.duplicate_using_var
pos
619 else Errors.illegal_disposable
pos "assigned";
620 let env = Env.set_local
env x
ty in
621 if is_using_clause
then Env.set_using_var
env x
else env
623 (* Check an individual component in the expression `e` in the
624 * `using (e) { ... }` statement.
625 * This consists of either
626 * a simple assignment `$x = e`, in which `$x` is the using variable, or
627 * an arbitrary expression `e`, in which case a temporary is the using
628 * variable, inaccessible in the source.
629 * Return the typed expression and its type, and any variables that must
630 * be designated as "using variables" for avoiding escapes.
632 and check_using_expr has_await
env ((pos, content
) as using_clause
) =
634 (* Simple assignment to local of form `$lvar = e` *)
635 | Binop
(Ast.Eq None
, (lvar_pos
, Lvar lvar
), e
) ->
636 let env, te
, ty = expr ~is_using_clause
:true env e
in
637 let env = Typing_disposable.enforce_is_disposable_type
env has_await
(fst e
) ty in
638 let env = set_local ~is_using_clause
:true env lvar
ty in
639 (* We are assigning a new value to the local variable, so we need to
640 * generate a new expression id
642 let env = Env.set_local_expr_id
env (snd lvar
) (Ident.tmp
()) in
643 env, (T.make_typed_expr
pos ty (T.Binop
(Ast.Eq None
,
644 T.make_typed_expr lvar_pos
ty (T.Lvar lvar
), te
)), [snd lvar
])
646 (* Arbitrary expression. This will be assigned to a temporary *)
648 let env, typed_using_clause
, ty = expr ~is_using_clause
:true env using_clause
in
649 let env = Typing_disposable.enforce_is_disposable_type
env has_await
pos ty in
650 env, (typed_using_clause
, [])
652 (* Check the using clause e in
653 * `using (e) { ... }` statement (`has_await = false`) or
654 * `await using (e) { ... }` statement (`has_await = true`).
655 * The expression consists of a comma-separated list of expressions (Expr_list)
656 * or a single expression.
657 * Return the typed expression, and any variables that must
658 * be designated as "using variables" for avoiding escapes.
660 and check_using_clause
env has_await
((pos, content
) as using_clause
) =
662 | Expr_list using_clauses
->
663 let env, pairs
= List.map_env
env using_clauses
(check_using_expr has_await
) in
664 let typed_using_clauses, vars_list
= List.unzip pairs
in
665 let ty_ = Ttuple
(List.map
typed_using_clauses T.get_type
) in
666 let ty = (Reason.Rnone
, ty_) in
667 env, T.make_typed_expr
pos ty (T.Expr_list
typed_using_clauses),
668 List.concat vars_list
670 let env, (typed_using_clause
, vars
) = check_using_expr has_await
env using_clause
in
671 env, typed_using_clause
, vars
673 (* Require a new construct with disposable *)
674 and enforce_return_disposable _env e
=
678 | _
, Await
(_
, Call _
) -> ()
680 Errors.invalid_return_disposable
p
682 (* Wrappers around the function with the same name in Typing_lenv, which only
683 * performs the move/save and merge operation if we are in a try block or in a
684 * function with return type 'noreturn'.
685 * This enables significant perf improvement, because this is called at every
686 * function of method call, when most calls are outside of a try block. *)
687 and move_and_merge_next_in_catch
env =
688 if env.Env.in_try
|| (TFTerm.is_noreturn
env)
689 then LEnv.move_and_merge_next_in_cont
env C.Catch
690 else LEnv.drop_cont
env C.Next
692 and save_and_merge_next_in_catch
env =
693 if env.Env.in_try
|| (TFTerm.is_noreturn
env)
694 then LEnv.save_and_merge_next_in_cont
env C.Catch
697 and might_throw
env = save_and_merge_next_in_catch
env
699 and gather_defined_in_block
env b
=
700 let locals = Typing_gather_defined.block
env b
in
701 Env.env_with_locals
env locals
703 and gather_defined_in_expr
env e
=
704 let locals = Typing_gather_defined.expr
env e
in
705 Env.env_with_locals
env locals
707 and stmt
env (pos, st
) =
708 let env, st
= stmt_
env pos st
in
709 Typing_debug.log_env_if_too_big
pos env;
712 and stmt_
env pos st
=
713 let env = Env.open_tyvars
env pos in
714 (fun (env, tb
) -> SubType.close_tyvars_and_solve
env, tb
) @@
717 (* Do not run inference on the block, since unsafe is sometimes used to work
718 around inference performance problems. *)
719 let env = gather_defined_in_block
env b
in
720 let tcopt = Env.get_tcopt
env in
721 let tb = NastTanyMapper.map_block
(ntm_env tcopt) b
in
722 env, T.Unsafe_block
tb
724 let env = if env.Env.in_case
725 then LEnv.move_and_merge_next_in_cont
env C.Fallthrough
730 let env = move_and_merge_next_in_catch
env in
735 let env, te
, _
= expr
env e
in
736 let env = if TFTerm.expression_exits
env e
737 then LEnv.move_and_merge_next_in_cont
env C.Exit
741 let env, te
, _
= expr
env e
in
743 (* We stash away the locals environment because condition updates it
744 * locally for checking b1. For example, we might have condition
745 * $x === null, or $x instanceof C, which changes the type of $x in
747 let parent_lenv = env.Env.lenv
in
749 let env = condition
env true te
in
750 let env, tb1
= block
env b1
in
751 let lenv1 = env.Env.lenv
in
753 let env = { env with Env.lenv
= parent_lenv } in
754 let env = condition
env false te
in
755 let env, tb2
= block
env b2
in
756 let lenv2 = env.Env.lenv
in
758 let env = LEnv.union_lenvs
env parent_lenv lenv1 lenv2 in
759 (* TODO TAST: annotate with joined types *)
760 env, T.If
(te
, tb1
, tb2
)
762 let env = check_inout_return
env in
763 let rty = Typing_return.wrap_awaitable
env pos (MakeType.void
(Reason.Rwitness
pos)) in
764 let { Typing_env_return_info.return_type
= expected_return
; _
} = Env.get_return
env in
765 let env = Typing_return.implicit_return
env pos ~expected
:expected_return ~actual
:rty in
766 let env = LEnv.move_and_merge_next_in_cont
env C.Exit
in
769 let env = check_inout_return
env in
770 let expr_pos = fst e
in
771 let Typing_env_return_info.{
772 return_type
; return_disposable
; return_mutable
; return_explicit
;
773 return_void_to_rx
} = Env.get_return
env in
776 then Some
(expr_pos, Reason.URreturn
,
777 Typing_return.strip_awaitable
(Env.get_fn_kind
env) env return_type
)
779 if return_disposable
then enforce_return_disposable
env e
;
780 let env, te
, rty = expr ~is_using_clause
:return_disposable ?
expected:expected env e
in
782 if Env.env_reactivity
env <> Nonreactive
784 Typing_mutability.handle_value_in_return
785 ~function_returns_mutable
:return_mutable
786 ~function_returns_void_for_rx
: return_void_to_rx
792 let return_type = TR.strip_condition_type_in_return
env return_type in
793 let env, rty = Env.unbind
env rty in
794 let rty = Typing_return.wrap_awaitable
env pos rty in
795 Typing_suggest.save_return
env return_type rty;
796 let env = Type.coerce_type
expr_pos Reason.URreturn
env rty return_type in
797 let env = LEnv.move_and_merge_next_in_cont
env C.Exit
in
798 env, T.Return
(Some te
)
800 (* NOTE: leaks scope as currently implemented; this matches
801 the behavior in naming (cf. `do_stmt` in naming/naming.ml).
803 let env, (tb, te
) = LEnv.stash_and_do
env [C.Continue
; C.Break
; C.Do
]
805 let env = LEnv.save_and_merge_next_in_cont
env C.Do
in
806 let env, _
= block
env b
in
807 (* saving the locals in continue here even if there is no continue
808 * statement because they must be merged at the end of the loop, in
809 * case there is no iteration *)
810 let env = LEnv.save_and_merge_next_in_cont
env C.Continue
in
812 if env.Env.in_loop
then 1 else Typing_alias.get_depth
(pos, st
) in
813 let env, tb = Env.in_loop
env begin
814 iter_n_acc
alias_depth begin fun env ->
815 let env = LEnv.update_next_from_conts
env [C.Continue
; C.Next
] in
816 (* The following is necessary in case there is an assignment in the
818 let env, te
, _
= expr
env e
in
819 let env = condition
env true te
in
820 let env = LEnv.update_next_from_conts
env [C.Do
; C.Next
] in
821 let env, tb = block
env b
in
824 let env = LEnv.update_next_from_conts
env [C.Continue
; C.Next
] in
825 let env, te
, _
= expr
env e
in
826 let env = condition
env false te
in
827 let env = LEnv.update_next_from_conts
env [C.Break
; C.Next
] in
830 | While
(e
, b
) as st
->
831 let env, (te
, tb) = LEnv.stash_and_do
env [C.Continue
; C.Break
] (fun env ->
832 let env = LEnv.save_and_merge_next_in_cont
env C.Continue
in
834 if env.Env.in_loop
then 1 else Typing_alias.get_depth
(pos, st
) in
835 let env, tb = Env.in_loop
env begin
836 iter_n_acc
alias_depth begin fun env ->
837 let env = LEnv.update_next_from_conts
env [C.Continue
; C.Next
] in
838 (* The following is necessary in case there is an assignment in the
840 let env, te
, _
= expr
env e
in
841 let env = condition
env true te
in
842 (* TODO TAST: avoid repeated generation of block *)
843 let env, tb = block
env b
in
847 let env = LEnv.update_next_from_conts
env [C.Continue
; C.Next
] in
848 let env, te
, _
= expr
env e
in
849 let env = condition
env false te
in
850 let env = LEnv.update_next_from_conts
env [C.Break
; C.Next
] in
852 env, T.While
(te
, tb)
854 us_has_await
= has_await
;
855 us_expr
= using_clause
;
856 us_block
= using_block
;
859 let env, typed_using_clause
, using_vars
= check_using_clause
env has_await using_clause
in
860 let env, typed_using_block
= block
env using_block
in
861 (* Remove any using variables from the environment, as they should not
862 * be in scope outside the block *)
863 let env = List.fold_left using_vars ~init
:env ~f
:Env.unset_local
in
865 us_has_await
= has_await
;
866 us_expr
= typed_using_clause
;
867 us_block
= typed_using_block
;
870 | For
(e1
, e2
, e3
, b
) as st
->
871 let env, (te1
, te2
, te3
, tb) = LEnv.stash_and_do
env [C.Continue
; C.Break
]
873 (* For loops leak their initalizer, but nothing that's defined in the
876 let (env, te1
, _
) = expr
env e1
in (* initializer *)
877 let env = LEnv.save_and_merge_next_in_cont
env C.Continue
in
879 if env.Env.in_loop
then 1 else Typing_alias.get_depth
(pos, st
) in
880 let env, (tb, te3
) = Env.in_loop
env begin
881 iter_n_acc
alias_depth begin fun env ->
882 (* The following is necessary in case there is an assignment in the
884 let env, te2
, _
= expr
env e2
in
885 let env = condition
env true te2
in
886 let env, tb = block
env b
in
887 let env = LEnv.update_next_from_conts
env [C.Continue
; C.Next
] in
888 let (env, te3
, _
) = expr
env e3
in
892 let env = LEnv.update_next_from_conts
env [C.Continue
; C.Next
] in
893 let (env, te2
, _
) = expr
env e2
in
894 let env = condition
env false te2
in
895 let env = LEnv.update_next_from_conts
env [C.Break
; C.Next
] in
896 env, (te1
, te2
, te3
, tb)) in
897 env, T.For
(te1
, te2
, te3
, tb)
898 | Switch
((pos, _
) as e
, cl
) ->
899 let env, te
, ty = expr
env e
in
900 (* Exhaustiveness etc is sensitive to unions, so normalize to avoid
901 * most of the problems. TODO: make it insensitive *)
902 let env, ty = Union.simplify_unions
env ty in
903 (* NB: A 'continue' inside a 'switch' block is equivalent to a 'break'.
905 * http://php.net/manual/en/control-structures.continue.php *)
906 let env, (te
, tcl
) = LEnv.stash_and_do
env [C.Continue
; C.Break
]
908 let parent_locals = LEnv.get_all_locals
env in
909 let case_list env = case_list parent_locals ty env pos cl
in
910 let env, tcl
= Env.in_case
env case_list in
911 let env = LEnv.update_next_from_conts
env
912 [C.Continue
; C.Break
; C.Next
] in
914 env, T.Switch
(te
, tcl
)
915 | Foreach
(e1
, e2
, b
) as st
->
916 (* It's safe to do foreach over a disposable, as no leaking is possible *)
917 let env, te1
, ty1
= expr ~accept_using_var
:true env e1
in
918 let env, (te1
, te2
, tb) = LEnv.stash_and_do
env [C.Continue
; C.Break
]
920 let env = LEnv.save_and_merge_next_in_cont
env C.Continue
in
921 let env, tk
, tv
= as_expr
env ty1
(fst e1
) e2
in
923 if env.Env.in_loop
then 1 else Typing_alias.get_depth
(pos, st
) in
924 let env, (te2
, tb) = Env.in_loop
env begin
925 iter_n_acc
alias_depth begin fun env ->
926 let env = LEnv.update_next_from_conts
env [C.Continue
; C.Next
] in
927 let env, te2
= bind_as_expr
env ty1
(fst e1
) tk tv e2
in
928 let env, tb = block
env b
in
932 let env = LEnv.update_next_from_conts
env
933 [C.Continue
; C.Break
; C.Next
] in
934 env, (te1
, te2
, tb)) in
935 env, T.Foreach
(te1
, te2
, tb)
936 | Try
(tb, cl
, fb
) ->
937 let env, ttb
, tcl
, tfb
= try_catch
env tb cl fb
in
938 env, T.Try
(ttb
, tcl
, tfb
)
940 (* Do nothing, this doesn't occur in Hack code. *)
941 failwith
"Should never typecheck nested definitions"
942 | Awaitall
(el
, b
) ->
943 let env = might_throw
env in
944 let env, el
= List.fold_left el ~init
:(env, []) ~f
:(fun (env, tel
) (e1
, e2
) ->
945 let env, te2
, ty2
= expr
env e2
in
946 let env, ty2
= Async.overload_extract_from_awaitable
env (fst e2
) ty2
in
949 let env, _
, _
= assign
(fst e1
) env (fst e1
, Lvar e1
) ty2
in
950 (env, (Some e1
, te2
) :: tel
)
951 | None
-> (env, (None
, te2
) :: tel
)
954 let env, b
= block
env b
in
955 env, T.Awaitall
(el
, b
)
958 let env, te
, ty = expr
env e
in
959 let env = exception_ty
p env ty in
960 let env = move_and_merge_next_in_catch
env in
963 let env = LEnv.move_and_merge_next_in_cont
env C.Continue
in
965 (* TempContinue is a naming error caught in naming.ml *)
967 let env = LEnv.move_and_merge_next_in_cont
env C.Continue
in
970 let env = LEnv.move_and_merge_next_in_cont
env C.Break
in
972 (* TempBreak is a naming error caught in naming.ml *)
974 let env = LEnv.move_and_merge_next_in_cont
env C.Break
in
976 | Let
((p, x
) as id, h
, rhs
) ->
977 let env, hint_ty
, expected = match h
with
980 { (Phase.env_with_self
env) with from_class
= Some CIstatic
; } in
981 let hint_ty = Decl_hint.hint
env.Env.decl_env
(p, h
) in
982 let env, hint_ty = Phase.localize ~
ety_env env hint_ty in
983 env, Some
hint_ty, Some
(p, Reason.URhint
, hint_ty)
984 | None
-> env, None
, None
986 let env, t_rhs
, rhs_ty
= expr
env rhs
in
987 let env, _
= match hint_ty with
989 let env = check_expected_ty
"Let" env rhs_ty
expected in
990 set_valid_rvalue
p env x
ty
991 | None
-> set_valid_rvalue
p env x rhs_ty
993 (* Transfer expression ID with RHS to let varible if RHS is another variable *)
994 let env = match rhs
with
995 | _
, ImmutableVar
(_
, x_rhs
) | _
, Lvar
(_
, x_rhs
) ->
996 let eid_rhs = Env.get_local_expr_id
env x_rhs
in
999 ~f
:(Env.set_local_expr_id
env x
)
1002 env, T.Let
(id, h
, t_rhs
)
1006 failwith
"Unexpected nodes in AST. These nodes should have been removed in naming."
1008 and finally_cont fb
env ctx
=
1009 let env = LEnv.replace_cont
env C.Next
(Some ctx
) in
1010 let env, _tfb
= block
env fb
in
1011 env, LEnv.get_all_locals
env
1013 and finally
env fb
=
1016 let env = LEnv.update_next_from_conts
env [C.Next
; C.Finally
] in
1019 let parent_locals = LEnv.get_all_locals
env in
1020 (* First typecheck the finally block against all continuations merged
1022 * During this phase, record errors found in the finally block, but discard
1023 * the resulting environment. *)
1024 let env'
= LEnv.update_next_from_conts
env C.all
in
1025 let _, tfb
= block
env' fb
in
1026 (* Second, typecheck the finally block once against each continuation. This
1027 * helps be more clever about what each continuation will be after the
1029 * We don't want to record errors during this phase, because certain types
1030 * of errors will fire wrongly. For example, if $x is nullable in some
1031 * continuations but not in others, then we must use `?->` on $x, but an
1032 * error will fire when typechecking the finally block againts continuations
1033 * where $x is non-null. *)
1034 let finally_cont env _key
= finally_cont fb
env in
1035 let env, locals_map
= Errors.ignore_
(fun () ->
1036 CMap.map_env
finally_cont env parent_locals) in
1037 let env, locals = Try.finally_merge
env locals_map
in
1038 (Env.env_with_locals
env locals), tfb
1040 and try_catch
env tb cl fb
=
1041 let parent_locals = LEnv.get_all_locals
env in
1042 let env = LEnv.drop_conts
env
1043 [C.Break
; C.Continue
; C.Exit
; C.Catch
; C.Finally
] in
1044 let env, (ttb
, tcb
) = Env.in_try
env (fun env ->
1045 let env, ttb
= block
env tb in
1046 let env = LEnv.move_and_merge_next_in_cont
env C.Finally
in
1047 let catchctx = LEnv.get_cont_option
env C.Catch
in
1048 let env, lenvtcblist
= List.map_env
env ~f
:(catch
catchctx) cl
in
1049 let lenvl, tcb
= List.unzip lenvtcblist
in
1050 let env = LEnv.union_lenv_list
env env.Env.lenv
lenvl in
1051 let env = LEnv.move_and_merge_next_in_cont
env C.Finally
in
1053 let env, tfb
= finally
env fb
in
1054 let env = LEnv.drop_cont
env C.Finally
in
1055 let env = LEnv.restore_and_merge_conts_from
1056 env parent_locals [C.Break
; C.Continue
; C.Exit
; C.Catch
; C.Finally
] in
1059 and case_list parent_locals ty env switch_pos cl
=
1060 let initialize_next_cont env =
1061 let env = LEnv.restore_conts_from
env parent_locals [C.Next
] in
1062 let env = LEnv.update_next_from_conts
env [C.Next
; C.Fallthrough
] in
1063 LEnv.drop_cont
env C.Fallthrough
in
1065 let check_fallthrough env switch_pos case_pos block rest_of_list ~is_default
=
1066 if not
@@ List.is_empty block
then
1067 begin match rest_of_list
with
1068 | [] | [Default
[]] -> ()
1070 begin match LEnv.get_cont_option
env C.Next
with
1072 if is_default
then Errors.default_fallthrough switch_pos
1073 else Errors.case_fallthrough switch_pos case_pos
1079 let make_exhaustive_equivalent_case_list env cl
=
1080 let has_default = List.exists cl ~f
:(function Default
_ -> true | _ -> false) in
1082 (* If it hasn't got a default clause then we need to solve type variables
1083 * in order to check for an enum *)
1085 then Env.expand_type
env ty
1086 else SubType.expand_type_and_solve
env ~description_of_expected
:"a value" switch_pos
ty in
1087 let is_enum = match snd
ty with
1088 | Tabstract
(AKenum
_, _) -> true
1090 (* If there is no default case and this is not a switch on enum (since
1091 * exhaustiveness is garanteed elsewhere on enums),
1092 * then add a default case for control flow correctness
1094 if has_default || is_enum then env, cl
, false else env, cl
@ [Default
[]], true in
1096 let rec case_list env = function
1098 | Default b
:: rl
->
1099 let env = initialize_next_cont env in
1100 let env, tb = block
env b
in
1101 check_fallthrough env switch_pos
Pos.none b rl ~is_default
:true;
1102 let env, tcl
= case_list env rl
in
1103 env, T.Default
tb::tcl
1104 | (Case
((pos, _) as e
, b
)) :: rl
->
1105 let env = initialize_next_cont env in
1106 let env, te
, _ = expr
env e
in
1107 let env, tb = block
env b
in
1108 check_fallthrough env switch_pos
pos b rl ~is_default
:false;
1109 let env, tcl
= case_list env rl
in
1110 env, T.Case
(te
, tb)::tcl
in
1112 let env, cl
, added_empty_default
= make_exhaustive_equivalent_case_list env cl
in
1113 let env, tcl
= case_list env cl
in
1114 let tcl = if added_empty_default
then List.take
tcl (List.length
tcl - 1) else tcl in
1117 and catch
catchctx env (sid
, exn
, b
) =
1118 let env = LEnv.replace_cont
env C.Next
catchctx in
1120 let ety_p = (fst sid
) in
1121 let env, _, _ = instantiable_cid
ety_p env cid [] in
1122 let env, _te
, ety
= static_class_id ~check_constraints
:false ety_p env [] cid in
1123 let env = exception_ty
ety_p env ety
in
1124 let env = set_local
env exn ety
in
1125 let env, tb = block
env b
in
1126 env, (env.Env.lenv
, (sid
, exn
, tb))
1128 and as_expr
env ty1 pe e
=
1129 let env = Env.open_tyvars
env pe
in
1130 (fun (env, ty, tk
, tv
) ->
1132 if TUtils.is_dynamic
env ty1
1134 else Type.sub_type pe
Reason.URforeach
env ty1
ty in
1135 let env = Env.set_tyvar_variance
env ty in
1136 SubType.close_tyvars_and_solve
env, tk
, tv
) @@
1137 let env, tv
= Env.fresh_unresolved_type
env pe
in
1140 let tk = MakeType.mixed Reason.Rnone
in
1141 env, MakeType.traversable
(Reason.Rforeach pe
) tv
, tk, tv
1143 let env, tk = Env.fresh_unresolved_type
env pe
in
1144 env, MakeType.keyed_traversable
(Reason.Rforeach pe
) tk tv
, tk, tv
1146 let tk = MakeType.mixed Reason.Rnone
in
1147 env, MakeType.async_iterator
(Reason.Rasyncforeach pe
) tv
, tk, tv
1149 let env, tk = Env.fresh_unresolved_type
env pe
in
1150 env, MakeType.async_keyed_iterator
(Reason.Rasyncforeach pe
) tk tv
, tk, tv
1152 and bind_as_expr
env loop_ty
p ty1 ty2 aexpr
=
1153 (* Set id as dynamic if the foreach loop was dynamic *)
1154 let env, eloop_ty
= Env.expand_type
env loop_ty
in
1155 let ty1, ty2
= if TUtils.is_dynamic
env eloop_ty
then
1156 MakeType.dynamic
(fst
ty1), MakeType.dynamic
(fst ty2
) else ty1, ty2
in
1157 let check_reassigned_mutable env te
=
1158 if Env.env_local_reactive
env
1159 then Typing_mutability.handle_assignment_mutability
env te None
1163 let env, te
, _ = assign
p env ev ty2
in
1164 let env = check_reassigned_mutable env te
in
1166 | Await_as_v
(p, ev
) ->
1167 let env, te
, _ = assign
p env ev ty2
in
1168 let env = check_reassigned_mutable env te
in
1169 env, T.Await_as_v
(p, te
)
1170 | As_kv
((p, ImmutableVar
((_, k
) as id)), ev
)
1171 | As_kv
((p, Lvar
((_, k
) as id)), ev
) ->
1172 let env, ty1'
= set_valid_rvalue
p env k
ty1 in
1173 let env, te
, _ = assign
p env ev ty2
in
1174 let tk = T.make_typed_expr
p ty1'
(T.Lvar
id) in
1175 let env = check_reassigned_mutable env tk in
1176 let env = check_reassigned_mutable env te
in
1177 env, T.As_kv
(tk, te
)
1178 | Await_as_kv
(p, (p1
, ImmutableVar
((_, k
) as id)), ev
)
1179 | Await_as_kv
(p, (p1
, Lvar
((_, k
) as id)), ev
) ->
1180 let env, ty1'
= set_valid_rvalue
p env k
ty1 in
1181 let env, te
, _ = assign
p env ev ty2
in
1182 let tk = T.make_typed_expr p1
ty1'
(T.Lvar
id) in
1183 let env = check_reassigned_mutable env tk in
1184 let env = check_reassigned_mutable env te
in
1185 env, T.Await_as_kv
(p, tk, te
)
1186 | _ -> (* TODO Probably impossible, should check that *)
1191 ?
(accept_using_var
= false)
1192 ?
(is_using_clause
= false)
1196 ?
(check_defined
= true)
1199 begin match expected with
1201 | Some
(_, r, ty) ->
1202 Typing_log.(log_with_level
env "typing" 1 (fun () ->
1204 [Log_head
("Typing.expr " ^
Typing_reason.string_of_ureason
r,
1205 [Log_type
("expected_ty", ty)])])) end;
1206 raw_expr ~accept_using_var ~is_using_clause
1207 ~valkind ~check_defined
1208 ?is_func_arg ?array_ref_ctx ?
expected env e
1210 let stack = Caml.Printexc.get_raw_backtrace
() in
1211 let pos = Pos.string (Pos.to_absolute
p) in
1212 prerr_endline
(Printf.sprintf
"Exception while typechecking expression at position %s" pos);
1213 Caml.Printexc.raise_with_backtrace e
stack
1216 ?
(accept_using_var
= false)
1217 ?
(is_using_clause
= false)
1219 ?lhs_of_null_coalesce
1222 ?valkind
:(valkind
=`other
)
1223 ?
(check_defined
= true)
1225 debug_last_pos := fst e
;
1227 expr_ ~accept_using_var ~is_using_clause ?
expected
1228 ?lhs_of_null_coalesce ?is_func_arg ?array_ref_ctx
1229 ~valkind ~check_defined
env e
in
1230 let () = match !expr_hook with
1231 | Some f
-> f e
(Typing_expand.fully_expand
env ty)
1236 let valkind = `lvalue
in
1237 expr_ ~
valkind ~check_defined
:false env e
1239 and lvalues
env el
=
1243 let env, te
, ty = lvalue
env e
in
1244 let env, tel
, tyl
= lvalues
env el
in
1245 env, te
::tel
, ty::tyl
1247 and is_pseudo_function s
=
1248 s
= SN.PseudoFunctions.hh_show
||
1249 s
= SN.PseudoFunctions.hh_show_env
||
1250 s
= SN.PseudoFunctions.hh_log_level
||
1251 s
= SN.PseudoFunctions.hh_force_solve
||
1252 s
= SN.PseudoFunctions.hh_loop_forever
1254 and loop_forever
env =
1255 (* forever = up to 10 minutes, to avoid accidentally stuck processes *)
1257 (* Look up things in shared memory occasionally to have a chance to be
1259 match Env.get_class
env "FOR_TEST_ONLY" with
1260 | None
-> Unix.sleep
1;
1263 Utils.assert_false_log_backtrace
1264 (Some
"hh_loop_forever was looping for more than 10 minutes")
1266 (* $x ?? 0 is handled similarly to $x ?: 0, except that the latter will also
1267 * look for sketchy null checks in the condition. *)
1268 (* TODO TAST: type refinement should be made explicit in the typed AST *)
1269 and eif
env ~
expected p c e1 e2
=
1270 let condition = condition ~lhs_of_null_coalesce
:false in
1271 let env, tc
, tyc
= raw_expr ~lhs_of_null_coalesce
:false env c
in
1272 let parent_lenv = env.Env.lenv
in
1274 let env = condition env true tc
in
1275 let env, te1
, ty1 = match e1
with
1277 let env, ty = TUtils.non_null
env p tyc
in
1280 let env, te1
, ty1 = expr ?
expected env e1
in
1283 let lenv1 = env.Env.lenv
in
1284 let env = { env with Env.lenv
= parent_lenv } in
1285 let env = condition env false tc
in
1286 let env, te2
, ty2
= expr ?
expected env e2
in
1287 let lenv2 = env.Env.lenv
in
1288 let fake_members = LEnv.intersect_fake
lenv1 lenv2 in
1289 (* we restore the locals to their parent state so as not to leak the
1290 * effects of the `condition` calls above *)
1291 let env = { env with Env.lenv
=
1292 { parent_lenv with Env.fake_members = fake_members } } in
1293 (* This is a shortened form of what we do in Typing_lenv.union_lenvs. The
1294 * latter takes local environments as arguments, but our types here
1295 * aren't assigned to local variables in an environment *)
1296 (* TODO: Omit if expected type is present and checked in calls to expr *)
1297 let env, ty = Union.union
env ty1 ty2
in
1298 make_result
env p (T.Eif
(tc
, te1
, te2
)) ty
1300 and is_parameter
env x
= Local_id.Map.mem x
(Env.get_params
env)
1301 and check_escaping_var
env (pos, x
) =
1302 if Env.is_using_var
env x
1305 then Errors.escaping_this
pos
1307 if is_parameter
env x
1308 then Errors.escaping_disposable_parameter
pos
1309 else Errors.escaping_disposable
pos
1313 ?
(accept_using_var
= false)
1317 ?
(check_defined
= true)
1324 let env, te
, ty = expr ~accept_using_var
1325 ?is_func_arg ?
expected ~
valkind ~check_defined
env e
in
1326 let env, tel
, tyl
= exprs ~accept_using_var
1327 ?is_func_arg ?
expected ~
valkind ~check_defined
env el
in
1328 env, te
::tel
, ty::tyl
1330 and exprs_expected
(pos, ur
, expected_tyl
) env el
=
1331 match el
, expected_tyl
with
1334 | e
::el
, expected_ty
::expected_tyl
->
1335 let env, te
, ty = expr ~
expected:(pos, ur
, expected_ty
) env e
in
1336 let env, tel
, tyl
= exprs_expected
(pos, ur
, expected_tyl
) env el
in
1337 env, te
::tel
, ty::tyl
1341 and make_result
env p te
ty =
1342 (* Set the variance of any type variables that were generated according
1343 * to how they appear in the expression type *)
1344 let env = Env.set_tyvar_variance
env ty in
1345 env, T.make_typed_expr
p ty te
, ty
1349 ?
(accept_using_var
= false)
1350 ?
(is_using_clause
= false)
1351 ?lhs_of_null_coalesce
1352 ?
(is_func_arg
= false)
1353 ?
(array_ref_ctx
= NoArray
)
1354 ~
(valkind: [> `lvalue
| `lvalue_subexpr
| `other
])
1357 let env = Env.open_tyvars
env p in
1358 (fun (env, te
, ty) -> SubType.close_tyvars_and_solve
env, te
, ty) @@
1359 let expr = expr ~check_defined
in
1360 let exprs = exprs ~check_defined
in
1361 let raw_expr = raw_expr ~check_defined
in
1364 * Given a list of types, computes their supertype. If any of the types are
1365 * unknown (e.g., comes from PHP), the supertype will be Typing_utils.tany env.
1367 let compute_supertype ~
expected ~reason
r env tys
=
1368 let p = Reason.to_pos
r in
1369 let env, supertype
=
1372 Env.fresh_unresolved_type_reason
env r
1373 | Some
(_, _, ty) -> env, ty in
1374 match supertype
with
1375 (* No need to check individual subtypes if expected type is mixed or any! *)
1376 | (_, Tany
) -> env, supertype
1378 let subtype_value env ty = Type.sub_type
p reason
env ty supertype
in
1379 let env = List.fold_left tys ~init
:env ~f
:subtype_value in
1380 if List.exists tys
(fun (_, ty) -> ty = Typing_utils.tany
env) then
1381 (* If one of the values comes from PHP land, we have to be conservative
1382 * and consider that we don't know what the type of the values are. *)
1383 env, (Reason.Rwitness
p, Typing_utils.tany
env)
1388 * Given a 'a list and a method to extract an expr and its ty from a 'a, this
1389 * function extracts a list of exprs from the list, and computes the supertype
1390 * of all of the expressions' tys.
1392 let compute_exprs_and_supertype ~
expected ?
(reason
= Reason.URarray_value
)
1393 r env l extract_expr_and_ty
=
1394 let env, exprs_and_tys
= List.map_env
env l
(extract_expr_and_ty ~
expected) in
1395 let exprs, tys
= List.unzip exprs_and_tys
in
1396 let env, supertype
= compute_supertype ~
expected ~reason
r env tys
in
1397 env, exprs, supertype
in
1399 let forget_fake_members env p callexpr
=
1400 (* Some functions are well known to not change the types of members, e.g.
1402 * There are a lot of usages like
1403 * if (!is_null($x->a) && !is_null($x->a->b))
1404 * where the second is_null call invalidates the first condition.
1405 * This function is a bit best effort. Add stuff here when you want
1406 * To avoid adding too many undue HH_FIXMEs. *)
1408 | _, Id
(_, func
) when (
1409 func
= SN.StdlibFunctions.is_null
||
1410 func
= SN.PseudoFunctions.isset
) -> env
1411 | _ -> Env.forget_members
env p in
1414 ~is_using_clause ~
expected env p call_type e hl el uel ~in_suspend
=
1415 let env, te
, result
=
1417 ~is_using_clause ~
expected p env call_type e hl el uel ~in_suspend
in
1418 let env = forget_fake_members env p e
in
1427 | ParenthesizedExpr
_ -> failwith
"AST should not contain these nodes"
1428 | Any
-> expr_error env p (Reason.Rwitness
p)
1430 (* TODO: use expected type to determine expected element type *)
1431 make_result
env p (T.Array
[]) (Reason.Rwitness
p, Tarraykind AKempty
)
1432 | Array
(x
:: rl
as l
) ->
1433 (* True if all fields are values, or all fields are key => value *)
1434 let fields_consistent = check_consistent_fields x rl
in
1435 let is_vec = match x
with
1436 | Nast.AFvalue
_ -> true
1437 | Nast.AFkvalue
_ -> false in
1438 if fields_consistent && is_vec then
1439 (* Use expected type to determine expected element type *)
1440 let env, elem_expected
=
1441 match expand_expected
env expected with
1442 | env, Some
(pos, ur
, ety
) ->
1443 begin match get_akvec_inst ety
with
1444 | Some vty
-> env, Some
(pos, ur
, vty
)
1449 let env, tel
, arraykind
=
1450 let env, tel
, value_ty
=
1451 compute_exprs_and_supertype ~
expected:elem_expected
1452 (Reason.Rtype_variable_generics
(p, "T", "array")) env l array_field_value
in
1453 env, tel
, AKvec value_ty
in
1455 (T.Array
(List.map tel
(fun e
-> T.AFvalue e
)))
1456 (Reason.Rwitness
p, Tarraykind arraykind
)
1459 (* TODO TAST: produce a typed expression here *)
1462 (* Use expected type to determine expected element type *)
1463 let env, vexpected
=
1464 match expand_expected
env expected with
1465 | env, Some
(pos, ur
, ety
) ->
1466 begin match get_akvec_inst ety
with
1467 | Some vty
-> env, Some
(pos, ur
, vty
)
1472 let env, _value_exprs
, value_ty
=
1473 compute_exprs_and_supertype ~
expected:vexpected
1474 (Reason.Rtype_variable_generics
(p, "T", "array")) env l array_field_value
in
1475 make_result
env p T.Any
(Reason.Rwitness
p, Tarraykind
(AKvec value_ty
))
1477 (* Use expected type to determine expected element type *)
1478 let env, kexpected
, vexpected
=
1479 match expand_expected
env expected with
1480 | env, Some
(pos, ur
, ety
) ->
1481 begin match get_akmap_inst ety
with
1482 | Some
(kty
, vty
) -> env, Some
(pos, ur
, kty
), Some
(pos, ur
, vty
)
1483 | None
-> env, None
, None
1487 let env, key_exprs
, key_ty
=
1488 compute_exprs_and_supertype ~
expected:kexpected
1489 (Reason.Rtype_variable_generics
(p, "Tk", "array")) env l array_field_key
in
1490 let env, value_exprs
, value_ty
=
1491 compute_exprs_and_supertype ~
expected:vexpected
1492 (Reason.Rtype_variable_generics
(p, "Tv", "array")) env l array_field_value
in
1494 (T.Array
(List.map
(List.zip_exn key_exprs value_exprs
)
1495 (fun (tek
, tev
) -> T.AFkvalue
(tek
, tev
))))
1496 (Reason.Rwitness
p, Tarraykind
(AKmap
(key_ty
, value_ty
)))
1499 (* Use expected type to determine expected key and value types *)
1500 let env, kexpected
, vexpected
=
1505 ) when not
(TCO.ignore_collection_expr_type_arguments
(Env.get_tcopt
env)) ->
1506 let env, localtk
= resolve_type_argument
env tk in
1507 let env, localtv
= resolve_type_argument
env tv
in
1508 env, Some
(pk
, Reason.URhint
, localtk
), Some
(pv
, Reason.URhint
, localtv
)
1509 | _ -> (* no explicit typehint, fallback to supplied expect *)
1510 begin match expand_expected
env expected with
1511 | env, Some
(pos, ur
, ety
) ->
1512 begin match get_darray_inst ety
with
1513 | Some
(kty
, vty
) ->
1514 env, Some
(pos, ur
, kty
), Some
(pos, ur
, vty
)
1521 let keys, values
= List.unzip l
in
1522 let env, value_exprs
, value_ty
=
1523 compute_exprs_and_supertype ~
expected:vexpected
1524 (Reason.Rtype_variable_generics
(p, "Tv", "darray")) env values array_value
in
1525 let env, key_exprs
, key_ty
=
1526 compute_exprs_and_supertype ~
expected:kexpected
1527 (Reason.Rtype_variable_generics
(p, "Tk", "darray")) env keys
1528 (arraykey_value
p "darray") in
1529 let field_exprs = List.zip_exn key_exprs value_exprs
in
1531 (T.Darray
(th
, field_exprs))
1532 (Reason.Rwitness
p, Tarraykind
(AKdarray
(key_ty
, value_ty
)))
1534 | Varray
(th
, values
) ->
1535 (* Use expected type to determine expected element type *)
1536 let env, elem_expected
=
1538 | Some
((pv
, _) as tv
)
1539 when not
(TCO.ignore_collection_expr_type_arguments
(Env.get_tcopt
env)) ->
1540 let env, localtv
= resolve_type_argument
env tv
in
1541 env, Some
(pv
, Reason.URhint
, localtv
)
1542 | _ -> (* no explicit typehint, fallback to supplied expect *)
1543 begin match expand_expected
env expected with
1544 | env, Some
(pos, ur
, ety
) ->
1545 begin match get_varray_inst ety
with
1546 | Some vty
-> env, Some
(pos, ur
, vty
)
1552 let env, value_exprs
, value_ty
=
1553 compute_exprs_and_supertype ~
expected:elem_expected
1554 (Reason.Rtype_variable_generics
(p, "T", "varray")) env values array_value
in
1556 (T.Varray
(th
, value_exprs
))
1557 (Reason.Rwitness
p, Tarraykind
(AKvarray value_ty
))
1559 | ValCollection
(kind
, th
, el
) ->
1560 (* Use expected type to determine expected element type *)
1561 let env, elem_expected
=
1563 | Some
((pv
, _) as tv
)
1564 when not
(TCO.ignore_collection_expr_type_arguments
(Env.get_tcopt
env)) ->
1565 let env, localtv
= resolve_type_argument
env tv
in
1566 env, Some
(pv
, Reason.URhint
, localtv
)
1568 begin match expand_expected
env expected with
1569 | env, Some
(pos, ur
, ety
) ->
1570 begin match get_vc_inst kind ety
with
1571 | Some vty
-> env, Some
(pos, ur
, vty
)
1576 let class_name = vc_kind_to_name kind
in
1579 | `Set
| `ImmSet
| `Keyset
->
1580 arraykey_value
p class_name
1581 | `Vector
| `ImmVector
| `Vec
| `Pair
->
1584 let env, tel
, elem_ty
=
1585 compute_exprs_and_supertype ~
expected:elem_expected ~reason
:Reason.URvector
1586 (Reason.Rtype_variable_generics
(p, "T", strip_ns
class_name)) env el
subtype_val in
1587 let ty = MakeType.class_type
(Reason.Rwitness
p) class_name [elem_ty
] in
1588 make_result
env p (T.ValCollection
(kind
, th
, tel
)) ty
1589 | KeyValCollection
(kind
, th
, l
) ->
1590 (* Use expected type to determine expected key and value types *)
1591 let env, kexpected
, vexpected
=
1596 ) when not
(TCO.ignore_collection_expr_type_arguments
(Env.get_tcopt
env)) ->
1597 let env, localtk
= resolve_type_argument
env tk in
1598 let env, localtv
= resolve_type_argument
env tv
in
1599 env, Some
(pk
, Reason.URhint
, localtk
), Some
(pv
, Reason.URhint
, localtv
)
1600 | _ -> (* no explicit typehint, fallback to supplied expect *)
1601 begin match expand_expected
env expected with
1602 | env, Some
(pos, ur
, ety
) ->
1603 begin match get_kvc_inst kind ety
with
1604 | Some
(kty
, vty
) ->
1605 env, Some
(pos, ur
, kty
), Some
(pos, ur
, vty
)
1606 | None
-> env, None
, None
1608 | _ -> env, None
, None
1610 let kl, vl
= List.unzip l
in
1611 let class_name = kvc_kind_to_name kind
in
1613 compute_exprs_and_supertype ~
expected:kexpected ~reason
:Reason.URkey
1614 (Reason.Rtype_variable_generics
(p, "Tk", strip_ns
class_name))
1615 env kl (arraykey_value
p class_name) in
1617 compute_exprs_and_supertype ~
expected:vexpected ~reason
:Reason.URvalue
1618 (Reason.Rtype_variable_generics
(p, "Tv", strip_ns
class_name))
1619 env vl array_value
in
1620 let ty = MakeType.class_type
(Reason.Rwitness
p) class_name [k
; v
] in
1621 make_result
env p (T.KeyValCollection
(kind
, th
, List.zip_exn tkl tvl
)) ty
1623 let env, te
, ty = expr env e
in
1624 (* Clone only works on objects; anything else fatals at runtime *)
1625 let tobj = (Reason.Rwitness
p, Tobject
) in
1626 let env = Type.sub_type
p Reason.URclone
env ty tobj in
1627 make_result
env p (T.Clone te
) ty
1629 let r, _ = Env.get_self
env in
1631 then Errors.this_var_outside_class
p;
1632 if not accept_using_var
1633 then check_escaping_var
env (p,this
);
1634 let (_, ty) = Env.get_local
env this
in
1635 let r = Reason.Rwitness
p in
1636 let ty = r, TUtils.this_of
(r, ty) in
1637 make_result
env p T.This
ty
1638 | Assert
(AE_assert e
) ->
1639 let env, te
, _ = expr env e
in
1640 let env = LEnv.save_and_merge_next_in_cont
env C.Exit
in
1641 let env = condition env true te
in
1642 make_result
env p (T.Assert
(T.AE_assert te
))
1643 (MakeType.void
(Reason.Rwitness
p))
1645 make_result
env p T.True
(MakeType.bool (Reason.Rwitness
p))
1647 make_result
env p T.False
(MakeType.bool (Reason.Rwitness
p))
1648 (* TODO TAST: consider checking that the integer is in range. Right now
1649 * it's possible for HHVM to fail on well-typed Hack code
1652 make_result
env p (T.Int s
) (MakeType.int (Reason.Rwitness
p))
1654 make_result
env p (T.Float s
) (MakeType.float (Reason.Rwitness
p))
1655 (* TODO TAST: consider introducing a "null" type, and defining ?t to
1659 make_result
env p T.Null
(MakeType.null
(Reason.Rwitness
p))
1661 make_result
env p (T.String s
) (MakeType.string (Reason.Rwitness
p))
1663 let env, tel
= string2
env idl
in
1664 make_result
env p (T.String2 tel
) (MakeType.string (Reason.Rwitness
p))
1665 | PrefixedString
(n
, e
) ->
1668 Errors.experimental_feature
p
1669 "String prefixes other than `re` are not yet supported.";
1670 expr_error env p (Reason.Rnone
)
1672 let env, te
, ty = expr env e
in
1674 let env = SubType.sub_string
pe env ty in
1677 begin try make_result
env p (T.PrefixedString
(n
, te
))
1678 (Typing_regex.type_pattern e
)
1680 | Pcre.Error
(Pcre.BadPattern
(s
, i
)) ->
1681 let s = s ^
" [" ^
(string_of_int i
) ^
"]" in
1682 Errors.bad_regex_pattern
pe s;
1683 expr_error env pe (Reason.Rregex
pe)
1684 | Typing_regex.Empty_regex_pattern
->
1685 Errors.bad_regex_pattern
pe "This pattern is empty";
1686 expr_error env pe (Reason.Rregex
pe)
1687 | Typing_regex.Missing_delimiter
->
1688 Errors.bad_regex_pattern
pe "Missing delimiter(s)";
1689 expr_error env pe (Reason.Rregex
pe)
1690 | Typing_regex.Invalid_global_option
->
1691 Errors.bad_regex_pattern
pe "Invalid global option(s)";
1692 expr_error env pe (Reason.Rregex
pe)
1695 Errors.re_prefixed_non_string
pe "Strings with embedded expressions";
1696 expr_error env pe (Reason.Rregex
pe)
1698 Errors.re_prefixed_non_string
pe "Non-strings";
1699 expr_error env pe (Reason.Rregex
pe))
1701 let env, fty
= fun_type_of_id
env x
[] in
1702 begin match fty
with
1703 | _, Tfun fty
-> check_deprecated
(fst x
) fty
;
1706 make_result
env p (T.Fun_id x
) fty
1707 | Id
((cst_pos
, cst_name
) as id) ->
1708 (match Env.get_gconst
env cst_name
with
1709 | None
when Env.is_strict
env ->
1710 Errors.unbound_global cst_pos
;
1711 let ty = (Reason.Rwitness cst_pos
, Typing_utils.terr
env) in
1712 make_result
env cst_pos
(T.Id
id) ty
1714 make_result
env p (T.Id
id) (Reason.Rwitness cst_pos
, Typing_utils.tany
env)
1717 Phase.localize_with_self
env ty in
1718 make_result
env p (T.Id
id) ty
1720 | Method_id
(instance
, meth
) ->
1721 (* Method_id is used when creating a "method pointer" using the magic
1722 * inst_meth function.
1724 * Typing this is pretty simple, we just need to check that instance->meth
1725 * is public+not static and then return its type.
1727 let env, te
, ty1 = expr env instance
in
1728 let env, result
, vis
=
1729 obj_get_with_visibility ~obj_pos
:p ~is_method
:true ~nullsafe
:None
1730 ~
valkind:`other ~pos_params
:None
env ty1 (CIexpr instance
) meth
(fun x
-> x
) in
1731 let has_lost_info = Env.FakeMembers.is_invalid
env instance
(snd meth
) in
1734 let name = "the method "^snd meth
in
1735 let env, result
= Env.lost_info
name env result
in
1736 make_result
env p (T.Method_id
(te
, meth
)) result
1740 | _, Tfun fty
-> check_deprecated
p fty
1743 | Some
(method_pos
, Vprivate
_) ->
1744 Errors.private_inst_meth method_pos
p
1745 | Some
(method_pos
, Vprotected
_) ->
1746 Errors.protected_inst_meth method_pos
p
1749 make_result
env p (T.Method_id
(te
, meth
)) result
1751 | Method_caller
((pos, class_name) as pos_cname
, meth_name
) ->
1752 (* meth_caller('X', 'foo') desugars to:
1755 let class_ = Env.get_class
env class_name in
1757 | None
-> unbound_name env pos_cname
1759 (* Create a class type for the given object instantiated with unresolved
1760 * types for its type parameters.
1763 List.map_env
env (Cls.tparams
class_) (fun env _ ->
1764 TUtils.unresolved_tparam ~reason
:(Reason.Rtype_variable
p) env) in
1765 let params = List.map
(Cls.tparams
class_) begin fun { tp_name
= (p,n
); _ } ->
1766 Reason.Rwitness
p, Tgeneric n
1768 let obj_type = Reason.Rwitness
p, Tapply
(pos_cname
, params) in
1770 (Phase.env_with_self
env) with
1771 substs
= Subst.make
(Cls.tparams
class_) tvarl
;
1773 let env, local_obj_ty
= Phase.localize ~
ety_env env obj_type in
1775 obj_get ~obj_pos
:pos ~is_method
:true ~nullsafe
:None
env local_obj_ty
1776 (CI
(pos, class_name)) meth_name
(fun x
-> x
) in
1778 | reason
, Tfun fty
->
1779 check_deprecated
p fty
;
1780 (* We are creating a fake closure:
1781 * function(Class $x, arg_types_of(Class::meth_name))
1782 : return_type_of(Class::meth_name)
1785 ety_env with substs
= Subst.make
(Cls.tparams
class_) tvarl
1788 Phase.check_tparams_constraints ~use_pos
:p ~
ety_env env (Cls.tparams
class_) in
1789 let env, local_obj_ty
= Phase.localize ~
ety_env env obj_type in
1790 let local_obj_fp = TUtils.default_fun_param local_obj_ty
in
1791 let fty = { fty with
1792 ft_params
= local_obj_fp :: fty.ft_params
} in
1793 let fun_arity = match fty.ft_arity
with
1794 | Fstandard
(min
, max
) -> Fstandard
(min
+ 1, max
+ 1)
1795 | Fvariadic
(min
, x
) -> Fvariadic
(min
+ 1, x
)
1796 | Fellipsis
(min
, p) -> Fellipsis
(min
+ 1, p) in
1799 ft_deprecated
= None
;
1800 ft_abstract
= false;
1801 (* propagate 'is_coroutine' from the method being called*)
1802 ft_is_coroutine
= fty.ft_is_coroutine
;
1803 ft_arity
= fun_arity;
1804 ft_tparams
= fty.ft_tparams
;
1805 ft_where_constraints
= fty.ft_where_constraints
;
1806 ft_params
= fty.ft_params
;
1807 ft_ret
= fty.ft_ret
;
1808 ft_reactive
= fty.ft_reactive
;
1809 ft_mutability
= fty.ft_mutability
;
1810 ft_returns_mutable
= fty.ft_returns_mutable
;
1811 ft_return_disposable
= fty.ft_return_disposable
;
1812 ft_decl_errors
= None
;
1813 ft_returns_void_to_rx
= fty.ft_returns_void_to_rx
;
1815 make_result
env p (T.Method_caller
(pos_cname
, meth_name
))
1816 (reason
, Tfun
caller)
1818 (* This can happen if the method lives in PHP *)
1819 make_result
env p (T.Method_caller
(pos_cname
, meth_name
))
1820 (Reason.Rwitness
pos, Typing_utils.tany
env)
1823 | Smethod_id
(c
, meth
) ->
1824 (* Smethod_id is used when creating a "method pointer" using the magic
1825 * class_meth function.
1827 * Typing this is pretty simple, we just need to check that c::meth is
1828 * public+static and then return its type.
1830 let class_ = Env.get_class
env (snd c
) in
1833 (* The class given as a static string was not found. *)
1836 let smethod = Env.get_static_member
true env class_ (snd meth
) in
1838 | None
-> (* The static method wasn't found. *)
1839 smember_not_found
p ~is_const
:false ~is_method
:true class_ (snd meth
);
1840 expr_error env p Reason.Rnone
1841 | Some
{ ce_type
= lazy ty; ce_visibility
; _ } ->
1843 let env, _te
, cid_ty
=
1844 static_class_id ~check_constraints
:true (fst c
) env [] cid in
1847 | (_, Tclass
(_, _, tyargs)) -> tyargs
1850 type_expansions
= [];
1851 substs
= Subst.make
(Cls.tparams
class_) tyargs;
1853 from_class
= Some
cid;
1854 validate_dty
= None
;
1859 let env, ft
= Phase.(localize_ft
1860 ~instantiation
:Phase.{ use_name
= strip_ns
(snd meth
); use_pos
= p; explicit_tparams
= [] }
1862 let ty = r, Tfun ft
in
1863 check_deprecated
p ft
;
1864 match ce_visibility
with
1866 make_result
env p (T.Smethod_id
(c
, meth
)) ty
1868 Errors.private_class_meth
(Reason.to_pos
r) p;
1871 Errors.protected_class_meth
(Reason.to_pos
r) p;
1875 Errors.internal_error
p "We have a method which isn't callable";
1880 let r = Reason.Rplaceholder
p in
1881 let ty = MakeType.void
r in
1882 make_result
env p (T.Lplaceholder
p) ty
1883 | Dollardollar
_ when valkind = `lvalue
->
1884 Errors.dollardollar_lvalue
p;
1885 expr_error env p (Reason.Rwitness
p)
1886 | Dollardollar
id ->
1887 let ty = Env.get_local_check_defined
env id in
1888 let env = might_throw
env in
1889 make_result
env p (T.Dollardollar
id) ty
1890 | Lvar
((_, x
) as id) ->
1891 if not accept_using_var
1892 then check_escaping_var
env id;
1893 let ty = if check_defined
1894 then Env.get_local_check_defined
env id
1895 else Env.get_local
env x
in
1896 make_result
env p (T.Lvar
id) ty
1897 | ImmutableVar
((_, x
) as id) ->
1898 let ty = Env.get_local
env x
in
1899 make_result
env p (T.ImmutableVar
id) ty
1901 let env, tel
, tyl
= match valkind with
1902 | `lvalue
| `lvalue_subexpr
-> lvalues
env el
1904 let env, expected = expand_expected
env expected in
1906 | Some
(pos, ur
, (_, Ttuple expected_tyl
)) ->
1907 exprs_expected
(pos, ur
, expected_tyl
) env el
1911 (* TODO TAST: figure out role of unbind here *)
1912 let env, tyl
= List.map_env
env tyl
Typing_env.unbind
in
1913 let env, tyl
= List.map_env
env tyl
TUtils.unresolved
in
1914 let ty = Reason.Rwitness
p, Ttuple tyl
in
1915 make_result
env p (T.List tel
) ty
1917 (* Use expected type to determine expected element types *)
1918 let env, expected1
, expected2
=
1919 match expand_expected
env expected with
1920 | env, Some
(pos, ur
, (_, Tclass
((_, k
), _, [ty1; ty2
]))) when k
= SN.Collections.cPair
->
1921 env, Some
(pos, ur
, ty1), Some
(pos, ur
, ty2
)
1922 | _ -> env, None
, None
in
1923 let env, te1
, ty1 = expr ?
expected:expected1
env e1
in
1924 let env, ty1 = Typing_env.unbind
env ty1 in
1925 let env, ty1 = TUtils.unresolved
env ty1 in
1926 let env, te2
, ty2
= expr ?
expected:expected2
env e2
in
1927 let env, ty2
= Typing_env.unbind
env ty2
in
1928 let env, ty2
= TUtils.unresolved
env ty2
in
1929 let ty = MakeType.pair
(Reason.Rwitness
p) ty1 ty2
in
1930 make_result
env p (T.Pair
(te1
, te2
)) ty
1932 (* TODO: use expected type to determine tuple component types *)
1933 let env, tel
, tyl
= exprs env el
in
1934 let ty = Reason.Rwitness
p, Ttuple tyl
in
1935 make_result
env p (T.Expr_list tel
) ty
1936 | Array_get
(e
, None
) ->
1937 let env, te
, _ = update_array_type
p env e None
valkind in
1938 let env = might_throw
env in
1939 (* NAST check reports an error if [] is used for reading in an
1941 let ty = (Reason.Rwitness
p, Typing_utils.terr
env) in
1942 make_result
env p (T.Array_get
(te
, None
)) ty
1943 | Array_get
(e1
, Some e2
) ->
1945 update_array_type ?lhs_of_null_coalesce
p env e1
(Some e2
) valkind in
1946 let env, ty1 = TUtils.fold_unresolved
env ty1 in
1947 let env, te2
, ty2
= expr env e2
in
1948 let env = might_throw
env in
1949 let is_lvalue = phys_equal
valkind `lvalue
in
1951 Typing_array_access.array_get ~array_pos
:(fst e1
) ~
expr_pos:p ?lhs_of_null_coalesce
1952 is_lvalue env ty1 e2 ty2
in
1953 make_result
env p (T.Array_get
(te1
, Some te2
)) ty
1954 | Call
(Cnormal
, (pos_id
, Id
((_, s) as id)), hl
, el
, [])
1955 when is_pseudo_function
s ->
1956 let env, tel
, tys
= exprs ~accept_using_var
:true env el
in
1958 if s = SN.PseudoFunctions.hh_show
1959 then (List.iter tys
(Typing_log.hh_show
p env); env)
1961 if s = SN.PseudoFunctions.hh_show_env
1962 then (Typing_log.hh_show_env
p env; env)
1964 if s = SN.PseudoFunctions.hh_log_level
1966 | [(_, String key_str
); (_, Int level_str
)] ->
1967 Env.set_log_level
env key_str
(int_of_string level_str
)
1970 if s = SN.PseudoFunctions.hh_force_solve
1971 then SubType.solve_all_unsolved_tyvars
env
1973 if s = SN.PseudoFunctions.hh_loop_forever
then (loop_forever
env; env)
1975 let env, ty = Env.fresh_type
env p in
1979 T.make_typed_expr pos_id
(Reason.Rnone
, TUtils.tany
env) (T.Id
id),
1983 | Call
(call_type
, e
, hl
, el
, uel
) ->
1984 let env = might_throw
env in
1985 let env, te
, ty = check_call ~is_using_clause ~
expected
1986 env p call_type e hl el uel ~in_suspend
:false in
1988 | Binop
(Ast.QuestionQuestion
, e1
, e2
) ->
1989 let env, te1
, ty1 = raw_expr ~lhs_of_null_coalesce
:true env e1
in
1990 let env, te2
, ty2
= expr ?
expected env e2
in
1991 let env, ty1'
= Env.fresh_unresolved_type
env (fst e1
) in
1992 let env = SubType.sub_type
env ty1 (MakeType.nullable
Reason.Rnone
ty1'
) in
1993 let env, ty_result
=
1994 if TypecheckerOptions.new_inference
(Env.get_tcopt
env)
1996 (* Essentially mimic a call to
1997 * function coalesce<Tr, Ta as Tr, Tb as Tr>(?Ta, Tb): Tr
1998 * That way we let the constraint solver take care of the union logic.
2000 let env, ty_result
= Env.fresh_unresolved_type
env (fst e2
) in
2001 let env = SubType.sub_type
env ty1' ty_result
in
2002 let env = SubType.sub_type
env ty2 ty_result
in
2005 Union.union
env ty1' ty2
in
2006 make_result
env p (T.Binop
(Ast.QuestionQuestion
, te1
, te2
)) ty_result
2007 (* For example, e1 += e2. This is typed and translated as if
2008 * written e1 = e1 + e2.
2009 * TODO TAST: is this right? e1 will get evaluated more than once
2011 | Binop
(Ast.Eq
(Some op
), e1
, e2
) ->
2012 begin match op
, snd e1
with
2013 | Ast.QuestionQuestion
, Class_get
_ ->
2014 Errors.experimental_feature
p
2015 "null coalesce assignment operator with static properties";
2016 expr_error env p (Reason.Rnone
)
2018 let e_fake = (p, Binop
(Ast.Eq None
, e1
, (p, Binop
(op
, e1
, e2
)))) in
2019 let env, te_fake
, ty = raw_expr env e_fake in
2020 begin match snd te_fake
with
2021 | T.Binop
(_, te1
, (_, T.Binop
(_, _, te2
))) ->
2022 let te = T.Binop
(Ast.Eq
(Some op
), te1
, te2
) in
2023 make_result
env p te ty
2027 | Binop
(Ast.Eq None
, e1
, e2
) ->
2028 let array_ref_ctx = match e1
, e2
with
2029 | (_, Array_get
_), (_, Unop
(Ast.Uref
, _)) -> ElementAssignment
2030 | _, (_, Unop
(Ast.Uref
, (_, Array_get
_))) -> ElementAccess
2033 | _, ImmutableVar
(p, x
) ->
2034 Errors.let_var_immutability_violation
p (Local_id.get_name x
)
2037 let env, te2
, ty2
= raw_expr ~
array_ref_ctx env e2
in
2038 let env, te1
, ty = assign
p env e1 ty2
in
2040 if Env.env_local_reactive
env then
2041 Typing_mutability.handle_assignment_mutability
env te1
(Some
(snd te2
))
2044 (* If we are assigning a local variable to another local variable then
2045 * the expression ID associated with e2 is transferred to e1
2048 | (_, Lvar
(_, x1
)), (_, ImmutableVar
(_, x2
))
2049 | (_, Lvar
(_, x1
)), (_, Lvar
(_, x2
)) ->
2050 let eid2 = Env.get_local_expr_id
env x2
in
2054 ~f
:(Env.set_local_expr_id
env x1
) in
2055 make_result
env p (T.Binop
(Ast.Eq None
, te1
, te2
)) ty
2057 make_result
env p (T.Binop
(Ast.Eq None
, te1
, te2
)) ty
2059 | Binop
((Ast.Ampamp
| Ast.Barbar
as bop
), e1
, e2
) ->
2060 let c = bop
= Ast.Ampamp
in
2061 let env, te1
, _ = expr env e1
in
2062 let lenv = env.Env.lenv in
2063 let env = condition env c te1
in
2064 let env, te2
, _ = expr env e2
in
2065 let env = { env with Env.lenv = lenv } in
2066 make_result
env p (T.Binop
(bop
, te1
, te2
)) (MakeType.bool (Reason.Rlogic_ret
p))
2067 | Binop
(bop
, e1
, e2
) when Env.is_strict
env
2068 && (snd e1
= Nast.Null
|| snd e2
= Nast.Null
)
2069 && (bop
= Ast.Eqeqeq
|| bop
= Ast.Diff2
) ->
2070 let e, ne
= if snd e2
= Nast.Null
then e1
, e2
else e2
, e1
in
2071 let env, te, ty = raw_expr env e in
2072 let tne = T.make_typed_expr
(fst ne
) ty T.Null
in
2073 let te1, te2
= if snd e2
= Nast.Null
then te, tne else tne, te in
2074 make_result
env p (T.Binop
(bop
, te1, te2
)) (MakeType.bool (Reason.Rcomp
p))
2075 | Binop
(bop
, e1
, e2
) ->
2076 let env, te1, ty1 = raw_expr env e1
in
2077 let env, te2
, ty2
= raw_expr env e2
in
2078 let env = might_throw
env in
2080 binop
p env bop
(fst e1
) te1 ty1 (fst e2
) te2 ty2
in
2082 | Pipe
(e0
, e1
, e2
) ->
2083 let env, te1, ty = expr env e1
in
2084 (** id is the ID of the $$ that is implicitly declared by the pipe.
2085 * Set the local type for the $$ in the RHS. *)
2086 let env = set_local
env e0
ty in
2087 let env, te2
, ty2
= expr env e2
in
2089 * Return ty2 since the type of the pipe expression is the type of the
2092 * Note: env does have the type of this Pipe's $$, but it doesn't
2093 * override the outer one since they have different ID's.
2096 * a() |> ( inner1($$) |> inner2($$) ) + $$
2098 * The rightmost $$ refers to the result of a()
2100 make_result
env p (T.Pipe
(e0
, te1, te2
)) ty2
2102 let env, te, ty = raw_expr env e in
2103 let env = might_throw
env in
2104 unop ~is_func_arg ~
array_ref_ctx p env uop
te ty
2105 | Eif
(c, e1
, e2
) -> eif
env ~
expected p c e1 e2
2107 begin match Env.get_typedef
env (snd sid
) with
2108 | Some
{td_tparams
= tparaml
; _} ->
2109 (* Typedef type parameters cannot have constraints *)
2110 let params = List.map ~f
:begin fun { tp_name
= (p,x
); _ } ->
2111 Reason.Rwitness
p, Tgeneric x
2113 let tdef = Reason.Rwitness
(fst sid
), Tapply
(sid
, params) in
2115 Reason.Rwitness
p, Tapply
((p, SN.Classes.cTypename
), [tdef]) in
2116 let env, tparams
= List.map_env
env tparaml
begin fun env tp
->
2117 Env.fresh_unresolved_type
env (fst tp
.tp_name
)
2119 let ety_env = { (Phase.env_with_self
env) with
2120 substs
= Subst.make tparaml tparams
} in
2121 let env = Phase.check_tparams_constraints ~use_pos
:p ~
ety_env env tparaml
in
2122 let env, ty = Phase.localize ~
ety_env env typename in
2123 make_result
env p (T.Typename sid
) ty
2125 (* Should never hit this case since we only construct this AST node
2126 * if in the expression Foo::class, Foo is a type def.
2128 expr_error env p (Reason.Rwitness
p)
2130 | Class_const
(cid, mid
) -> class_const
env p (cid, mid
)
2131 | Class_get
((px
, x
), CGstring
(py
, y
))
2132 when Env.FakeMembers.get_static
env x y
<> None
->
2133 let env, local
= Env.FakeMembers.make_static
p env x y
in
2134 let local = p, Lvar
(p, local) in
2135 let env, _, ty = expr env local in
2136 let env, te, _ = static_class_id ~check_constraints
:false px
env [] x
in
2137 make_result
env p (T.Class_get
(te, T.CGstring
(py
, y
))) ty
2138 | Class_get
((cpos
, cid), CGstring mid
) ->
2139 let env, te, cty
= static_class_id ~check_constraints
:false cpos
env [] cid in
2140 let env = might_throw
env in
2142 class_get ~is_method
:false ~is_const
:false env cty mid
cid in
2143 if Env.FakeMembers.is_static_invalid
env cid (snd mid
)
2145 let fake_name = Env.FakeMembers.make_static_id
cid (snd mid
) in
2146 let env, ty = Env.lost_info
fake_name env ty in
2147 make_result
env p (T.Class_get
(te, T.CGstring mid
)) ty
2149 make_result
env p (T.Class_get
(te, T.CGstring mid
)) ty
2150 (* Fake member property access. For example:
2151 * if ($x->f !== null) { ...$x->f... }
2153 | Class_get
(_, CGexpr
_) -> failwith
"AST should not have any CGexprs after naming"
2154 | Obj_get
(e, (pid
, Id
(py
, y
)), nf
)
2155 when Env.FakeMembers.get
env e y
<> None
->
2156 let env = might_throw
env in
2157 let env, local = Env.FakeMembers.make
p env e y
in
2158 let local = p, Lvar
(p, local) in
2159 let env, _, ty = expr env local in
2160 let env, t_lhs
, _ = expr ~accept_using_var
:true env e in
2161 let t_rhs = T.make_typed_expr pid
ty (T.Id
(py
, y
)) in
2162 make_result
env p (T.Obj_get
(t_lhs
, t_rhs, nf
)) ty
2163 (* Statically-known instance property access e.g. $x->f *)
2164 | Obj_get
(e1
, (pm
, Id m
), nullflavor
) ->
2166 (match nullflavor
with
2167 | OG_nullthrows
-> None
2168 | OG_nullsafe
-> Some
p
2170 let env, te1, ty1 = expr ~accept_using_var
:true env e1
in
2171 let env = might_throw
env in
2173 obj_get ~obj_pos
:(fst e1
) ~is_method
:false ~
nullsafe ~
valkind
2174 env ty1 (CIexpr e1
) m
(fun x
-> x
) in
2175 let has_lost_info = Env.FakeMembers.is_invalid
env e1
(snd m
) in
2179 let name = "the member " ^ snd m
in
2180 Env.lost_info
name env result
2184 make_result
env p (T.Obj_get
(te1,
2185 T.make_typed_expr pm result
(T.Id m
), nullflavor
)) result
2186 (* Dynamic instance property access e.g. $x->$f *)
2187 | Obj_get
(e1
, e2
, nullflavor
) ->
2188 let env, te1, ty1 = expr ~accept_using_var
:true env e1
in
2189 let env, te2
, _ = expr env e2
in
2190 let ty = if TUtils.is_dynamic
env ty1 then
2191 MakeType.dynamic
(Reason.Rwitness
p) else
2192 (Reason.Rwitness
p, Typing_utils.tany
env)
2194 let (pos, _), te2
= te2
in
2195 let env = might_throw
env in
2196 let te2 = T.make_typed_expr
pos ty te2 in
2197 make_result
env p (T.Obj_get
(te1, te2, nullflavor
)) ty
2199 make_result
env p T.Yield_break
(Reason.Rwitness
p, Typing_utils.tany
env)
2201 let env, (taf
, opt_key
, value) = array_field
env af
in
2202 let env, send
= Env.fresh_type
env p in
2203 let env, key
= match af
, opt_key
with
2204 | Nast.AFvalue
(p, _), None
->
2205 begin match Env.get_fn_kind
env with
2209 Errors.internal_error
p "yield found in non-generator";
2210 env, (Reason.Rwitness
p, Typing_utils.tany
env)
2212 env, MakeType.int (Reason.Rwitness
p)
2213 | Ast.FAsyncGenerator
->
2214 let env, ty = Env.fresh_type
env p in
2215 env, MakeType.nullable
(Reason.Ryield_asyncnull
p) ty
2219 | _, _ -> assert false in
2220 let rty = match Env.get_fn_kind
env with
2222 (* yield in coroutine is already reported as error in NastCheck *)
2223 let _, _, ty = expr_error env p (Reason.Rwitness
p) in
2226 MakeType.generator
(Reason.Ryield_gen
p) key
value send
2227 | Ast.FAsyncGenerator
->
2228 MakeType.async_generator
(Reason.Ryield_asyncgen
p) key
value send
2229 | Ast.FSync
| Ast.FAsync
->
2230 failwith
"Parsing should never allow this" in
2231 let Typing_env_return_info.{ return_type = expected_return
; _ } = Env.get_return
env in
2233 Type.coerce_type
p (Reason.URyield
) env rty expected_return
in
2234 let env = Env.forget_members
env p in
2235 let env = LEnv.save_and_merge_next_in_cont
env C.Exit
in
2236 make_result
env p (T.Yield taf
) (MakeType.nullable
(Reason.Ryield_send
p) send
)
2238 let env, key
= Env.fresh_type
env p in
2239 let env, value = Env.fresh_type
env p in
2240 let env, te, yield_from_ty
=
2241 expr ~is_using_clause
env e in
2242 (* Expected type of `e` in `yield from e` is KeyedTraversable<Tk,Tv> (but might be dynamic)*)
2243 let expected_yield_from_ty = MakeType.keyed_traversable
(Reason.Ryield_gen
p) key
value in
2244 let from_dynamic = SubType.is_sub_type
env yield_from_ty
(MakeType.dynamic
(fst yield_from_ty
)) in
2247 then env (* all set if dynamic, otherwise need to check against KeyedTraversable *)
2248 else Type.coerce_type
p Reason.URyield_from
env yield_from_ty
expected_yield_from_ty in
2249 let rty = match Env.get_fn_kind
env with
2251 (* yield in coroutine is already reported as error in NastCheck *)
2252 let _, _, ty = expr_error env p (Reason.Rwitness
p) in
2256 then MakeType.dynamic
(Reason.Ryield_gen
p) (*TODO: give better reason*)
2257 else MakeType.generator
(Reason.Ryield_gen
p) key
value (MakeType.void
(Reason.Rwitness
p))
2258 | Ast.FSync
| Ast.FAsync
| Ast.FAsyncGenerator
->
2259 failwith
"Parsing should never allow this" in
2260 let Typing_env_return_info.{ return_type = expected_return
; _ } = Env.get_return
env in
2262 Type.coerce_type
p (Reason.URyield_from
) env rty expected_return
in
2263 let env = Env.forget_members
env p in
2264 make_result
env p (T.Yield_from
te) (MakeType.void
(Reason.Rwitness
p))
2266 let env = might_throw
env in
2267 (* Await is permitted in a using clause e.g. using (await make_handle()) *)
2269 expr ~is_using_clause
env e in
2270 let env, ty = Async.overload_extract_from_awaitable
env p rty in
2271 make_result
env p (T.Await
te) ty
2275 | _, Call
(call_type
, e, hl
, el
, uel
) ->
2276 let env = Env.open_tyvars
env p in
2277 (fun (env, te, ty) -> SubType.close_tyvars_and_solve
env, te, ty) @@
2278 check_call ~is_using_clause ~
expected
2279 env p call_type
e hl el uel ~in_suspend
:true
2281 let env, te, ty = expr env e in
2282 (* not a call - report an error *)
2283 Errors.non_call_argument_in_suspend
2285 (Reason.to_string
("This is " ^
Typing_print.error
env ty) (fst
ty));
2287 make_result
env p (T.Suspend
te) ty
2289 | Special_func func
-> special_func
env p func
2290 | New
((pos, c), tal
, el
, uel
, p1
) ->
2291 let env = might_throw
env in
2292 let env, tc
, tel
, tuel
, ty, ctor_fty
=
2293 new_object ~
expected ~is_using_clause ~check_parent
:false ~check_not_abstract
:true
2294 pos env c tal el uel
in
2295 let env = Env.forget_members
env p in
2296 make_result
env p (T.New
(tc
, tal
, tel
, tuel
, (p1
, ctor_fty
))) ty
2298 expr_error env p (Reason.Rwitness
p)
2299 | Cast
((_, Harray
(None
, None
)), _)
2300 when Env.is_strict
env
2301 || TCO.migration_flag_enabled
(Env.get_tcopt
env) "array_cast" ->
2302 Errors.array_cast
p;
2303 expr_error env p (Reason.Rwitness
p)
2305 let env, te, ty2
= expr env e in
2306 let env = might_throw
env in
2307 if (TypecheckerOptions.experimental_feature_enabled
2309 TypecheckerOptions.experimental_forbid_nullable_cast
)
2310 && TUtils.is_option_non_mixed
env ty2
2311 then Errors.nullable_cast
p (Typing_print.error
env ty2
) (Reason.to_pos
(fst ty2
));
2312 let env, ty = Phase.localize_hint_with_self
env hint
in
2313 make_result
env p (T.Cast
(hint
, te)) ty
2314 | InstanceOf
(e, (pos, cid)) ->
2315 let env, te, _ = expr env e in
2316 let env, te2, _class
= instantiable_cid
pos env cid [] in
2317 make_result
env p (T.InstanceOf
(te, te2)) (MakeType.bool (Reason.Rwitness
p))
2319 let env, te, _ = expr env e in
2320 make_result
env p (T.Is
(te, hint
)) (MakeType.bool (Reason.Rwitness
p))
2321 | As
(e, hint
, is_nullable
) ->
2322 let refine_type env lpos lty
rty =
2323 let reason = Reason.Ras lpos
in
2324 let env, rty = Env.expand_type
env rty in
2325 if snd
rty <> Tdynamic
&& SubType.is_sub_type
env lty
rty
2327 else safely_refine_type
env p reason lpos lty
rty
2329 let env, te, expr_ty
= expr env e in
2330 let env = might_throw
env in
2331 let ety_env = { (Phase.env_with_self
env) with from_class
= Some CIstatic
; } in
2332 let env, hint_ty = Phase.localize_hint ~
ety_env env hint
in
2335 let env, hint_ty = refine_type env (fst
e) expr_ty
hint_ty in
2336 env, MakeType.nullable
(Reason.Rwitness
p) hint_ty
2337 else if is_instance_var
e then
2338 let env, _, ivar_ty
= raw_expr env e in
2339 let env, ((ivar_pos
, _) as ivar
) = get_instance_var
env e in
2340 let env, hint_ty = refine_type env ivar_pos ivar_ty
hint_ty in
2341 let env = set_local
env ivar
hint_ty in
2344 refine_type env (fst
e) expr_ty
hint_ty
2346 make_result
env p (T.As
(te, hint
, is_nullable
)) hint_ty
2349 let is_anon = match e with Efun
_ -> true | Lfun
_ -> false | _ -> assert false in
2350 (* This is the function type as declared on the lambda itself.
2351 * If type hints are absent then use Tany instead. *)
2352 let declared_ft = Decl.fun_decl_in_env
env.Env.decl_env f
in
2353 (* When creating a closure, the 'this' type will mean the late bound type
2354 * of the current enclosing class
2357 { (Phase.env_with_self
env) with from_class
= Some CIstatic
} in
2358 let env, declared_ft =
2359 Phase.(localize_ft ~instantiation
: {use_name
="lambda"; use_pos
=p; explicit_tparams
=[]}
2360 ~
ety_env env declared_ft) in
2361 List.iter idl
(check_escaping_var
env);
2362 (* Ensure lambda arity is not Fellipsis in strict mode *)
2363 begin match declared_ft.ft_arity
with
2364 | Fellipsis
_ when Env.is_strict
env ->
2365 Errors.ellipsis_strict_mode ~require
:`Param_name
p
2368 (* Is the return type declared? *)
2369 let is_explicit_ret = Option.is_some f
.f_ret
in
2371 Decl_fun_utils.fun_reactivity_opt
env.Env.decl_env f
.f_user_attributes
2372 |> Option.value ~default
:(TR.strip_conditional_reactivity
(Env.env_reactivity
env)) in
2373 let check_body_under_known_params env ?ret_ty ft
=
2374 let old_reactivity = Env.env_reactivity
env in
2375 let env = Env.set_env_reactive
env reactivity in
2376 let old_inside_ppl_class = env.Typing_env.inside_ppl_class
in
2377 let env = { env with Typing_env.inside_ppl_class
= false } in
2378 let ft = { ft with ft_reactive
= reactivity } in
2379 let (is_coroutine
, _counter
, _, anon
) = anon_make
env p f
ft idl
is_anon in
2380 let ft = { ft with ft_is_coroutine
= is_coroutine
} in
2381 let env, tefun
, ty = anon ?ret_ty
env ft.ft_params
ft.ft_arity
in
2382 let env = Env.set_env_reactive
env old_reactivity in
2383 let env = { env with
2384 Typing_env.inside_ppl_class
= old_inside_ppl_class; } in
2387 then (Reason.Rwitness
p, Tfun
{ ft with ft_ret
= declared_ft.ft_ret
})
2388 else (Reason.Rwitness
p, Tfun
{ ft with ft_ret
= ty }) in
2389 env, tefun
, inferred_ty in
2390 let env, eexpected
= expand_expected
env expected in
2391 begin match eexpected
with
2392 | Some
(_pos
, _ur
, (_, Tfun expected_ft
)) ->
2393 (* First check that arities match up *)
2394 check_lambda_arity
p expected_ft
.ft_pos
declared_ft.ft_arity expected_ft
.ft_arity
;
2395 (* Use declared types for parameters in preference to those determined
2396 * by the context: they might be more general. *)
2397 let rec replace_non_declared_types params declared_ft_params expected_ft_params
=
2398 match params, declared_ft_params
, expected_ft_params
with
2399 | param
::params, declared_ft_param
::declared_ft_params
,
2400 expected_ft_param
::expected_ft_params
->
2401 let rest = replace_non_declared_types params declared_ft_params expected_ft_params
in
2402 let resolved_ft_param = if Option.is_some param
.param_hint
2403 then declared_ft_param
2404 else { declared_ft_param
with fp_type
= expected_ft_param
.fp_type
} in
2405 resolved_ft_param :: rest
2407 (* This means the expected_ft params list can have more parameters
2408 * than declared parameters in the lambda. For variadics, this is OK,
2409 * for non-variadics, this will be caught elsewhere in arity checks.
2413 let replace_non_declared_arity variadic declared_arity expected_arity
=
2415 | FVvariadicArg
{param_hint
= Some
(_); _} -> declared_arity
2416 | FVvariadicArg
_ ->
2418 match declared_arity
, expected_arity
with
2419 | Fvariadic
(min_arity
, declared
), Fvariadic
(_, expected) ->
2420 Fvariadic
(min_arity
, { declared
with fp_type
= expected.fp_type
})
2421 | _, _ -> declared_arity
2423 | _ -> declared_arity
2425 let expected_ft = { expected_ft with ft_arity
=
2426 replace_non_declared_arity
2427 f
.f_variadic
declared_ft.ft_arity
expected_ft.ft_arity
} in
2428 let expected_ft = { expected_ft with ft_params
=
2429 replace_non_declared_types f
.f_params
declared_ft.ft_params
expected_ft.ft_params
} in
2430 (* Don't bother passing in `void` if there is no explicit return *)
2432 match expected_ft.ft_ret
with
2433 | _, Tprim Tvoid
when not
is_explicit_ret -> None
2434 | _ -> Some
expected_ft.ft_ret
in
2435 Typing_log.increment_feature_count
env FL.Lambda.contextual_params
;
2436 check_body_under_known_params env ?
ret_ty expected_ft
2438 let explicit_variadic_param_or_non_variadic =
2439 begin match f
.f_variadic
with
2440 | FVvariadicArg
{param_hint
; _} -> Option.is_some param_hint
2441 | FVellipsis
_ -> false
2445 (* If all parameters are annotated with explicit types, then type-check
2446 * the body under those assumptions and pick up the result type *)
2447 let all_explicit_params =
2448 List.for_all f
.f_params
(fun param
-> Option.is_some param
.param_hint
) in
2449 if all_explicit_params && explicit_variadic_param_or_non_variadic
2451 Typing_log.increment_feature_count
env
2452 (if List.is_empty f
.f_params
then FL.Lambda.no_params
else FL.Lambda.explicit_params
);
2453 check_body_under_known_params env declared_ft
2457 | Some
(_, _, (_, Tany
)) ->
2458 (* If the expected type is Tany env then we're passing a lambda to an untyped
2459 * function and we just assume every parameter has type Tany env *)
2460 Typing_log.increment_feature_count
env FL.Lambda.untyped_context
;
2461 check_body_under_known_params env declared_ft
2463 (* If the expected type is something concrete but not a function
2464 * then we should reject in strict mode. Check body anyway *)
2465 if Env.is_strict
env
2466 then Errors.untyped_lambda_strict_mode
p;
2467 Typing_log.increment_feature_count
env FL.Lambda.non_function_typed_context
;
2468 check_body_under_known_params env declared_ft
2470 (* If we're in partial mode then type-check definition anyway,
2471 * so treating parameters without type hints as "untyped"
2473 if TypecheckerOptions.new_inference
(Env.get_tcopt
env) && not
(Env.is_strict
env)
2475 Typing_log.increment_feature_count
env FL.Lambda.non_strict_unknown_params
;
2476 check_body_under_known_params env declared_ft
2479 (* If new_inference and new_inference_lambda are enabled,
2480 * check lambda using constraints *)
2481 if TypecheckerOptions.new_inference
(Env.get_tcopt
env)
2482 && TypecheckerOptions.new_inference_lambda
(Env.get_tcopt
env)
2484 Typing_log.increment_feature_count
env FL.Lambda.fresh_tyvar_params
;
2485 let freshen_untyped_param env ft_param
=
2486 match snd ft_param
.fp_type
with
2488 let env, ty = Env.fresh_invariant_type_var
env ft_param
.fp_pos
in
2489 env, { ft_param
with fp_type
= ty }
2492 let env, ft_params
= List.map_env
env declared_ft.ft_params
freshen_untyped_param in
2493 let declared_ft = { declared_ft with ft_params
} in
2494 check_body_under_known_params env declared_ft
2496 (* Legacy lambda inference *)
2498 Typing_log.increment_feature_count
env FL.Lambda.unknown_params
;
2499 (* check for recursive function calls *)
2500 let reactivity = fun_reactivity env.Env.decl_env f
.f_user_attributes f
.f_params
in
2501 let old_reactivity = Env.env_reactivity
env in
2502 let env = Env.set_env_reactive
env reactivity in
2503 let is_coroutine, counter
, pos, anon
= anon_make
env p f
declared_ft idl
is_anon in
2504 let env, tefun
, _, anon_id
= Errors.try_with_error
2506 let (_, tefun
, ty) = anon
env declared_ft.ft_params
declared_ft.ft_arity
in
2507 let anon_fun = reactivity, is_coroutine, counter
, pos, anon
in
2508 let env, anon_id
= Env.add_anonymous
env anon_fun in
2509 env, tefun
, ty, anon_id
)
2511 (* If the anonymous function declaration has errors itself, silence
2512 them in any subsequent usages. *)
2513 let anon_ign ?el
:_ ?
ret_ty:_ env fun_params
=
2514 Errors.ignore_
(fun () -> (anon
env fun_params
)) in
2515 let (_, tefun
, ty) = anon_ign env declared_ft.ft_params
declared_ft.ft_arity
in
2516 let anon_fun = reactivity, is_coroutine, counter
, pos, anon
in
2517 let env, anon_id
= Env.add_anonymous
env anon_fun in
2518 env, tefun
, ty, anon_id
) in
2519 let env = Env.set_env_reactive
env old_reactivity in
2520 let anon_ty = (Reason.Rwitness
p, Tanon
(declared_ft.ft_arity
, anon_id
)) in
2521 let ((ep
,_efun_ty
),efun
) = tefun
in
2522 let tefun = ((ep
, anon_ty), efun
) in
2527 | Xml
(sid
, attrl
, el
) ->
2529 let env, _te
, classes
= class_id_for_new ~exact
:Nonexact
p env cid [] in
2530 let class_info = match classes
with
2532 (* OK to ignore rest of list; class_info only used for errors, and
2533 * cid = CI sid cannot produce a union of classes anyhow *)
2534 | (_, class_info, _)::_ -> Some
class_info
2536 let env, _te
, obj
= expr env (fst sid
, New
((fst sid
, cid), [], [], [], (fst sid
))) in
2537 let env, typed_attrs
, attr_types
= xhp_attribute_exprs
env class_info attrl
in
2538 let env, tel
= List.map_env
env el ~f
:(fun env e -> let env, te, _ = expr env e in env, te) in
2539 let txml = T.Xml
(sid
, typed_attrs
, List.rev tel
) in
2540 (match class_info with
2541 | None
-> make_result
env p txml (Reason.Runknown_class
p, Tobject
)
2542 | Some
class_info ->
2543 let env = List.fold_left attr_types ~f
:begin fun env attr
->
2544 let namepstr, valpty
= attr
in
2545 let valp, valty
= valpty
in
2547 obj_get ~obj_pos
:(fst sid
) ~is_method
:false ~
nullsafe:None
env obj
cid
2548 namepstr (fun x
-> x
) in
2549 let ureason = Reason.URxhp
((Cls.name class_info), snd
namepstr) in
2550 Type.coerce_type
valp ureason env valty declty
2552 make_result
env p txml obj
2555 (* Do not run inference on the expression, since unsafe is sometimes used to
2556 work around inference performance problems. *)
2557 let env = gather_defined_in_expr
env e in
2558 let tcopt = Env.get_tcopt
env in
2559 let te = NastTanyMapper.map_expr
(ntm_env tcopt) e in
2560 make_result
env p (T.Unsafe_expr
te) (Reason.Rnone
, Tany
)
2561 | Callconv
(kind
, e) ->
2562 let env, te, ty = expr env e in
2563 make_result
env p (T.Callconv
(kind
, te)) ty
2565 let env, fdm_with_expected
=
2566 match expand_expected
env expected with
2567 | env, Some
(pos, ur
, (_, Tshape
(_, expected_fdm
))) ->
2571 match ShapeMap.get k expected_fdm
with
2572 | None
-> (k
, (v
, None
))
2573 | Some sft
-> (k
, (v
, Some
(pos, ur
, sft
.sft_ty
)))) fdm
in
2576 env, List.map ~f
:(fun (k
, v
) -> (k
, (v
, None
))) fdm
in
2578 (* allow_inter adds a type-variable *)
2581 ~f
:(fun env (key
, (e, expected)) ->
2582 let env, te, ty = expr ?
expected env e in env, (key
, (te,ty)))
2583 env fdm_with_expected
in
2585 let convert_expr_and_type_to_shape_field_type env (key
, (_, ty)) =
2586 let env, sft_ty
= TUtils.unresolved
env ty in
2587 (* An expression evaluation always corresponds to a shape_field_type
2588 with sft_optional = false. *)
2589 env, (key
, { sft_optional
= false; sft_ty
}) in
2590 List.map_env ~f
:convert_expr_and_type_to_shape_field_type env tfdm
in
2591 let fdm = List.fold_left
2592 ~f
:(fun acc
(k
, v
) -> ShapeMap.add k v acc
)
2593 ~init
:ShapeMap.empty
2595 let env = check_shape_keys_validity
env p (ShapeMap.keys fdm) in
2596 (* Fields are fully known, because this shape is constructed
2597 * using shape keyword and we know exactly what fields are set. *)
2598 make_result
env p (T.Shape
(List.map ~f
:(fun (k
,(te,_)) -> (k
, te)) tfdm
))
2599 (Reason.Rwitness
p, Tshape
(FieldsFullyKnown
, fdm))
2601 | PU_atom
_ -> failwith
"TODO(T36532263): Pocket Universes"
2602 | PU_identifier
_ -> failwith
"TODO(T36532263): Pocket Universes"
2604 with Typing_lenv_cont.Continuation_not_found
_ ->
2605 expr_any env p (Reason.Rwitness
p)
2607 and class_const ?
(incl_tc
=false) env p ((cpos
, cid), mid
) =
2608 let env, ce
, cty
= static_class_id ~check_constraints
:false cpos
env [] cid in
2609 let env, const_ty
, cc_abstract_info
=
2610 class_get ~is_method
:false ~is_const
:true ~incl_tc
env cty mid
cid in
2611 match cc_abstract_info
with
2612 | Some
(cc_pos
, cc_name
) ->
2613 let () = match cid with
2614 | CIstatic
| CIexpr
_ -> ();
2615 | _ -> Errors.abstract_const_usage
p cc_pos cc_name
; ()
2616 in make_result
env p (T.Class_const
(ce
, mid
)) const_ty
2618 make_result
env p (T.Class_const
(ce
, mid
)) const_ty
2620 and anon_sub_type
pos ur
env ty_sub ty_super
=
2621 Errors.try_add_err
pos (Reason.string_of_ureason ur
)
2622 (fun () -> SubType.sub_type
env ty_sub ty_super
)
2625 and anon_coerce_type
pos ur
env ty_have ty_expect
=
2626 Typing_ops.coerce_type ~sub_fn
:anon_sub_type
pos ur
env ty_have ty_expect
2628 (*****************************************************************************)
2629 (* XHP attribute/body helpers. *)
2630 (*****************************************************************************)
2632 * Process a spread operator by computing the intersection of XHP attributes
2633 * between the spread expression and the XHP constructor onto which we're
2636 and xhp_spread_attribute
env c_onto valexpr
=
2637 let (p, _) = valexpr
in
2638 let env, te, valty
= expr env valexpr
in
2639 (* Build the typed attribute node *)
2640 let typed_attr = T.Xhp_spread
te in
2641 let env, attr_ptys
= match c_onto
with
2643 | Some
class_info -> Typing_xhp.get_spread_attributes
env p class_info valty
2644 in env, typed_attr, attr_ptys
2647 * Simple XHP attributes (attr={expr} form) are simply interpreted as a member
2648 * variable prefixed with a colon, the types of which will be validated later
2650 and xhp_simple_attribute
env id valexpr
=
2651 let (p, _) = valexpr
in
2652 let env, te, valty
= expr env valexpr
in
2653 (* This converts the attribute name to a member name. *)
2654 let name = ":"^
(snd
id) in
2655 let attr_pty = ((fst
id, name), (p, valty
)) in
2656 let typed_attr = T.Xhp_simple
(id, te) in
2657 env, typed_attr, [attr_pty]
2661 * Typecheck the attribute expressions - this just checks that the expressions are
2662 * valid, not that they match the declared type for the attribute and,
2663 * in case of spreads, makes sure they are XHP.
2665 and xhp_attribute_exprs
env cid attrl
=
2666 let handle_attr (env, typed_attrl
, attr_ptyl
) attr
=
2667 let env, typed_attr, attr_ptys
= match attr
with
2668 | Xhp_simple
(id, valexpr
) -> xhp_simple_attribute
env id valexpr
2669 | Xhp_spread valexpr
-> xhp_spread_attribute
env cid valexpr
2671 env, typed_attr::typed_attrl
, attr_ptys
@ attr_ptyl
2673 let env, typed_attrl
, attr_ptyl
= List.fold_left ~f
:handle_attr ~init
:(env, [], []) attrl
in
2674 env, List.rev typed_attrl
, List.rev attr_ptyl
2676 (*****************************************************************************)
2677 (* Anonymous functions. *)
2678 (*****************************************************************************)
2679 and anon_bind_param
params (env, t_params
) ty : Env.env * Tast.fun_param list
=
2682 (* This code cannot be executed normally, because the arity is wrong
2683 * and it will error later. Bind as many parameters as we can and carry
2686 | param
:: paraml
->
2688 match param
.param_hint
with
2691 let h = Decl_hint.hint
env.Env.decl_env
h in
2692 (* When creating a closure, the 'this' type will mean the
2693 * late bound type of the current enclosing class
2696 { (Phase.env_with_self
env) with from_class
= Some CIstatic
} in
2697 let env, h = Phase.localize ~
ety_env env h in
2698 let pos = Reason.to_pos
(fst
ty) in
2699 (* Don't use Type.coerce_type as it resets env.Env.pos unnecessarily *)
2700 let env = anon_coerce_type
pos Reason.URparam
env ty h in
2701 (* Closures are allowed to have explicit type-hints. When
2702 * that is the case we should check that the argument passed
2703 * is compatible with the type-hint.
2704 * The body of the function should be type-checked with the
2705 * hint and not the type of the argument passed.
2706 * Otherwise it leads to strange results where
2707 * foo(?string $x = null) is called with a string and fails to
2708 * type-check. If $x is a string instead of ?string, null is not
2709 * subtype of string ...
2711 let env, t_param
= bind_param env (h, param
) in
2712 env, t_params
@ [t_param
]
2714 let ty = (Reason.Rlambda_param
(param
.param_pos
, fst
ty), snd
ty) in
2715 let env, t_param
= bind_param env (ty, param
) in
2716 env, t_params
@ [t_param
]
2718 and anon_bind_variadic
env vparam variadic_ty
=
2720 match vparam
.param_hint
with
2722 (* if the hint is missing, use the type we expect *)
2723 env, variadic_ty
, Reason.to_pos
(fst variadic_ty
)
2725 let h = Decl_hint.hint
env.Env.decl_env hint
in
2727 { (Phase.env_with_self
env) with from_class
= Some CIstatic
; } in
2728 let env, h = Phase.localize ~
ety_env env h in
2729 let pos = Reason.to_pos
(fst variadic_ty
) in
2730 let env = anon_coerce_type
pos Reason.URparam
env variadic_ty
h in
2731 env, h, vparam
.param_pos
2733 let r = Reason.Rvar_param
pos in
2734 let arr_values = r, (snd
ty) in
2735 let ty = r, Tarraykind
(AKvarray
arr_values) in
2736 let env, t_variadic
= bind_param env (ty, vparam
) in
2740 and anon_bind_opt_param
env param
: Env.env =
2741 match param
.param_expr
with
2743 let ty = Reason.Rwitness param
.param_pos
, Typing_utils.tany
env in
2744 let env, _ = bind_param env (ty, param
) in
2747 let env, _te
, ty = expr env default
in
2748 Typing_sequencing.sequence_check_expr default
;
2749 let env, _ = bind_param env (ty, param
) in
2752 and anon_check_param
env param
=
2753 match param
.param_hint
with
2756 let env, hty
= Phase.localize_hint_with_self
env hty
in
2757 let paramty = Env.get_local
env (Local_id.make_unscoped param
.param_name
) in
2758 let hint_pos = Reason.to_pos
(fst hty
) in
2759 let env = Type.coerce_type
hint_pos Reason.URhint
env paramty hty
in
2762 and stash_conts_for_anon
env is_anon captured f
=
2763 let captured = if Env.is_local_defined
env this
then (Pos.none
, this
) :: captured else captured in
2764 let initial_locals = if is_anon
2765 then Env.get_locals
env captured
2766 else Env.next_cont_exn
env in
2767 let env, (tfun
, result
) = Typing_lenv.stash_and_do
env C.all
(
2769 let env = Env.reinitialize_locals
env in
2770 let env = Env.set_locals
env initial_locals in
2771 let env, tfun
, result
= f
env in
2772 env, (tfun
, result
)) in
2775 (* Make a type-checking function for an anonymous function. *)
2776 and anon_make tenv
p f
ft idl
is_anon =
2777 let anon_lenv = tenv
.Env.lenv in
2778 let is_typing_self = ref false in
2779 let nb = Nast.assert_named_body f
.f_body
in
2780 let is_coroutine = f
.f_fun_kind
= Ast.FCoroutine
in
2784 (* Here ret_ty should include Awaitable wrapper *)
2785 fun ?el ?
ret_ty env supplied_params supplied_arity
->
2788 Errors.anonymous_recursive
p;
2789 expr_error env p (Reason.Rwitness
p)
2792 is_typing_self := true;
2793 Env.anon
anon_lenv env begin fun env ->
2794 stash_conts_for_anon
env is_anon idl
begin fun env ->
2795 let env = Env.clear_params
env in
2796 let make_variadic_arg env varg tyl
=
2797 let remaining_types =
2798 (* It's possible the variadic arg will capture the variadic
2799 * parameter of the supplied arity (if arity is Fvariadic)
2800 * and additional supplied params.
2802 * For example in cases such as:
2803 * lambda1 = (int $a, string...$c) ==> {};
2804 * lambda1(1, "hello", ...$y); (where $y is a variadic string)
2805 * lambda1(1, "hello", "world");
2806 * then ...$c will contain "hello" and everything in $y in the first
2807 * example, and "hello" and "world" in the second example.
2809 * To account for a mismatch in arity, we take the remaining supplied
2810 * parameters and return a list of all their types. We'll use this
2811 * to create a union type when creating the typed variadic arg.
2813 let remaining_params = List.drop supplied_params
(List.length f
.f_params
) in
2814 List.map ~f
:(fun param
-> param
.fp_type
) remaining_params
2816 let r = Reason.Rvar_param
(varg
.param_pos
) in
2817 let union = Tunion
(tyl
@ remaining_types) in
2818 let env, t_param
= anon_bind_variadic
env varg
(r, union) in
2819 env, T.FVvariadicArg t_param
2821 let env, t_variadic
=
2822 begin match f
.f_variadic
, supplied_arity
with
2823 | FVvariadicArg arg
, Fvariadic
(_, variadic
) ->
2824 make_variadic_arg env arg
[variadic
.fp_type
]
2825 | FVvariadicArg arg
, Fstandard
_ ->
2826 make_variadic_arg env arg
[]
2827 | FVellipsis
pos, _ -> env, T.FVellipsis
pos
2828 | _, _ -> env, T.FVnonVariadic
2830 let params = ref f
.f_params
in
2831 let env, t_params
= List.fold_left ~f
:(anon_bind_param
params) ~init
:(env, [])
2832 (List.map supplied_params
(fun x
-> x
.fp_type
)) in
2833 let env = List.fold_left ~f
:anon_bind_opt_param ~init
:env !params in
2834 let env = List.fold_left ~f
:anon_check_param ~init
:env f
.f_params
in
2835 let env = match el
with
2837 iter2_shortest
Unify.unify_param_modes
ft.ft_params supplied_params
;
2840 let var_param = match f
.f_variadic
with
2842 let param = TUtils.default_fun_param ~
pos
2843 (Reason.Rvar_param
pos, Tany
) in
2846 let rec iter l1 l2
=
2847 match l1
, l2
, var_param with
2850 | [], x2
::rl2
, Some def1
->
2851 param_modes ~is_variadic
:true def1 x2
;
2853 | x1
::rl1
, x2
::rl2
, _ -> param_modes x1 x2
; iter rl1 rl2
2855 iter ft.ft_params x
;
2856 wfold_left2 inout_write_back
env ft.ft_params x
in
2857 let env = Env.set_fn_kind
env f
.f_fun_kind
in
2861 (* Do we have a contextual return type? *)
2862 begin match ret_ty with
2864 let env, ret_ty = Env.fresh_unresolved_type
env p in
2865 env, Typing_return.wrap_awaitable
env p ret_ty
2867 (* We might need to force it to be Awaitable if it is a type variable *)
2868 Typing_return.force_awaitable
env p ret_ty
2871 let ret = Decl_hint.hint
env.Env.decl_env x
in
2872 (* If a 'this' type appears it needs to be compatible with the
2876 { (Phase.env_with_self
env) with
2877 from_class
= Some CIstatic
} in
2878 Phase.localize ~
ety_env env ret in
2879 let env = Env.set_return
env
2880 (Typing_return.make_info f
.f_fun_kind
[] env
2881 ~is_explicit
:(Option.is_some
ret_ty)
2883 let local_tpenv = env.Env.lenv.Env.tpenv
in
2884 let env, tb = block
env nb.fb_ast
in
2885 let implicit_return = LEnv.has_next
env in
2887 if not
implicit_return || Nast.named_body_is_unsafe
nb
2889 else fun_implicit_return
env p hret f
.f_fun_kind
2891 (* We don't want the *uses* of the function to affect its return type *)
2892 let env, hret
= Env.unbind
env hret
in
2893 is_typing_self := false;
2895 if Nast.named_body_is_unsafe
nb
2896 then Tast.Annotations.FuncBodyAnnotation.HasUnsafeBlocks
2897 else Tast.Annotations.FuncBodyAnnotation.NoUnsafeBlocks
in
2899 T.f_annotation
= Env.save
local_tpenv env;
2900 T.f_span
= f
.f_span
;
2901 T.f_mode
= f
.f_mode
;
2903 T.f_name
= f
.f_name
;
2904 T.f_tparams
= List.map f
.f_tparams
(type_param
env);
2905 T.f_where_constraints
= f
.f_where_constraints
;
2906 T.f_fun_kind
= f
.f_fun_kind
;
2907 T.f_file_attributes
= [];
2908 T.f_user_attributes
= List.map f
.f_user_attributes
(user_attribute
env);
2909 T.f_body
= { T.fb_ast
= tb; fb_annotation
= annotation };
2910 T.f_params
= t_params
;
2911 T.f_variadic
= t_variadic
; (* TODO TAST: Variadic efuns *)
2912 T.f_external
= f
.f_external
;
2913 T.f_namespace
= f
.f_namespace
;
2914 T.f_doc_comment
= f
.f_doc_comment
;
2915 T.f_static
= f
.f_static
;
2917 let ty = (Reason.Rwitness
p, Tfun
ft) in
2919 then T.make_typed_expr
p ty (T.Efun
(tfun_, idl
))
2920 else T.make_typed_expr
p ty (T.Lfun
(tfun_, idl
)) in
2921 let env = Env.set_tyvar_variance
env ty in
2923 end (* stash_conts_for_anon *)
2927 (*****************************************************************************)
2928 (* End of anonymous functions. *)
2929 (*****************************************************************************)
2931 and special_func
env p func
=
2932 let env, tfunc
, ty = (match func
with
2934 let env, tel
, etyl
= exprs env el
in
2935 let env, ty = Async.genva
env p etyl
in
2936 env, T.Genva tel
, ty
2938 let result_ty = MakeType.awaitable
(Reason.Rwitness
p) ty in
2939 make_result
env p (T.Special_func tfunc
) result_ty
2941 and requires_consistent_construct
= function
2948 (* Caller will be looking for a particular form of expected type
2949 * e.g. a function type (when checking lambdas) or tuple type (when checking
2950 * tuples). First expand the expected type and elide single union; also
2951 * strip nullables, so ?t becomes t, as context will always accept a t if a ?t
2954 and expand_expected
env expected =
2958 | Some
(p, ur
, ty) ->
2959 let env, ty = Env.expand_type
env ty in
2961 | _, Tunion
[ty] -> env, Some
(p, ur
, ty)
2962 | _, Toption
ty -> env, Some
(p, ur
, ty)
2963 | _ -> env, Some
(p, ur
, ty)
2965 (* Do a subtype check of inferred type against expected type *)
2966 and check_expected_ty message
env inferred_ty expected =
2970 | Some
(p, ur
, expected_ty
) ->
2971 Typing_log.(log_with_level
env "typing" 1 (fun () ->
2973 [Log_head
(Printf.sprintf
"Typing.check_expected_ty %s" message
,
2974 [Log_type
("inferred_ty", inferred_ty);
2975 Log_type
("expected_ty", expected_ty
)])]));
2976 Type.coerce_type
p ur
env inferred_ty expected_ty
2978 and new_object ~
expected ~check_parent ~check_not_abstract ~is_using_clause
p env cid tal el uel
=
2979 (* Obtain class info from the cid expression. We get multiple
2980 * results with a CIexpr that has a union type *)
2981 let env, tcid
, classes
= instantiable_cid ~exact
:Exact
p env cid tal
in
2982 let allow_abstract_bound_generic = match tcid
with
2983 | (_, (_, Tabstract
(AKgeneric tt
, _))), T.CI
(_, tn
) -> tt
= tn
2985 let finish env tcid tel tuel
ty ctor_fty
=
2987 let (_, cid_ty
), _ = tcid
in
2989 | _, Tabstract
(AKgeneric
_, _) ->
2992 if check_parent
then env, ty
2993 else ExprDepTy.make
env cid ty in
2994 env, tcid
, tel
, tuel
, new_ty
, ctor_fty
in
2995 let rec gather env tel tuel res classes
=
3001 let env, tel
, _ = exprs env el
in
3002 let env, tuel
, _ = exprs env uel
in
3003 let r = Reason.Runknown_class
p in
3004 finish env tcid tel tuel
(r, Tobject
) (r, TUtils.terr
env)
3005 | [ty,ctor_fty
] -> finish env tcid tel tuel
ty ctor_fty
3007 let tyl, ctyl
= List.unzip l
in
3008 let r = Reason.Rwitness
p in
3009 finish env tcid tel tuel
(r, Tunion
tyl) (r, Tunion ctyl
)
3012 | (cname
, class_info, c_ty
)::classes
->
3013 if check_not_abstract
&& (Cls.abstract
class_info)
3014 && not
(requires_consistent_construct
cid)
3015 && not
allow_abstract_bound_generic then
3016 uninstantiable_error
env p cid (Cls.pos class_info) (Cls.name class_info) p c_ty
;
3017 let env, obj_ty_
, params =
3018 match cid, tal
, snd c_ty
with
3019 (* Explicit type arguments *)
3020 | CI
_, (_::_), Tclass
(_, _, tyl) -> env, (snd c_ty
), tyl
3022 let env, params = List.map_env
env (Cls.tparams
class_info)
3024 let env, tvar
= Env.fresh_unresolved_type_reason
env
3025 (Reason.Rtype_variable_generics
(p, snd
tparam.tp_name
, strip_ns
(snd cname
))) in
3026 Typing_log.log_new_tvar_for_new_object
env p tvar cname
tparam;
3028 begin match snd c_ty
with
3029 | Tclass
(_, Exact
, _) ->
3030 env, (Tclass
(cname
, Exact
, params)), params
3032 env, (Tclass
(cname
, Nonexact
, params)), params
3034 if not check_parent
&& not is_using_clause
&& (Cls.is_disposable
class_info)
3035 then Errors.invalid_new_disposable
p;
3036 let r_witness = Reason.Rwitness
p in
3037 let obj_ty = (r_witness, obj_ty_
) in
3040 | CIstatic
-> (r_witness, TUtils.this_of
obj_ty)
3041 | CIexpr
_ -> (r_witness, snd
c_ty)
3044 let (_, cid_ty
), _ = tcid
in
3046 | _, Tabstract
(AKgeneric
_, _) ->
3051 else ExprDepTy.make
env cid c_ty in
3052 (* Set variance according to type of `new` expression now. Lambda arguments
3053 * to the constructor might depend on it, and `call_construct` only uses
3054 * `ctor_fty` to set the variance which has void return type *)
3055 let env = Env.set_tyvar_variance
env new_ty
in
3056 let env, _tcid
, tel
, tuel
, ctor_fty
=
3057 let env = check_expected_ty
"New" env new_ty
expected in
3058 call_construct
p env class_info params el uel
cid in
3059 if (snd
(Cls.construct
class_info)) = Inconsistent
then
3061 | CIstatic
-> Errors.new_inconsistent_construct
p cname `static
3062 | CIexpr
_ -> Errors.new_inconsistent_construct
p cname `classname
3067 match (fst
(Cls.construct
class_info)) with
3068 | Some
{ce_type
= lazy ty; _ } ->
3070 type_expansions
= [];
3071 substs
= Subst.make
(Cls.tparams
class_info) params;
3074 validate_dty
= None
;
3076 let env, ctor_fty
= Phase.localize ~
ety_env env ty in
3077 env, check_abstract_parent_meth
SN.Members.__construct
p ctor_fty
3078 | None
-> env, ctor_fty
3080 gather env tel tuel
((obj_ty,ctor_fty
)::res
) classes
3081 | CIstatic
| CI
_ | CIself
-> gather env tel tuel
((c_ty,ctor_fty
)::res
) classes
3083 (* When constructing from a (classname) variable, the variable
3084 * dictates what the constructed object is going to be. This allows
3085 * for generic and dependent types to be correctly carried
3086 * through the 'new $foo()' iff the constructed obj_ty is a
3087 * supertype of the variable-dictated c_ty *)
3088 let env = SubType.sub_type
env c_ty obj_ty in
3089 gather env tel tuel
((c_ty,ctor_fty
)::res
) classes
3091 gather env [] [] [] classes
3093 (* FIXME: we need to separate our instantiability into two parts. Currently,
3094 * all this function is doing is checking if a given type is inhabited --
3095 * that is, whether there are runtime values of type T. However,
3096 * instantiability should be the stricter notion that T has a runtime
3097 * constructor; that is, `new T()` should be valid. In particular, interfaces
3098 * are inhabited, but not instantiable.
3099 * To make this work with classname, we likely need to add something like
3100 * concrete_classname<T>, where T cannot be an interface.
3102 and instantiable_cid ?
(exact
= Nonexact
) p env cid tal
=
3103 let env, te, classes
= class_id_for_new ~exact
p env cid tal
in
3105 List.iter classes
begin fun ((pos, name), class_info, c_ty) ->
3106 if (Cls.kind
class_info) = Ast.Ctrait
|| (Cls.kind
class_info) = Ast.Cenum
3109 | CIexpr
_ | CI
_ ->
3110 uninstantiable_error
env p cid (Cls.pos class_info) name pos c_ty
3111 | CIstatic
| CIparent
| CIself
-> ()
3112 else if (Cls.kind
class_info) = Ast.Cabstract
&& (Cls.final
class_info)
3114 uninstantiable_error
env p cid (Cls.pos class_info) name pos c_ty
3119 and uninstantiable_error
env reason_pos
cid c_tc_pos c_name c_usage_pos
c_ty =
3120 let reason_msgl = match cid with
3122 let ty_str = "This would be "^
Typing_print.error
env c_ty in
3123 [(reason_pos
, ty_str)]
3125 Errors.uninstantiable_class c_usage_pos c_tc_pos c_name
reason_msgl
3127 and exception_ty
pos env ty =
3128 let exn_ty = MakeType.throwable
(Reason.Rthrow
pos) in
3129 Type.sub_type
pos (Reason.URthrow
) env ty exn_ty
3131 and shape_field_pos
= function
3132 | Ast.SFlit_int
(p, _) | Ast.SFlit_str
(p, _) -> p
3133 | Ast.SFclass_const
((cls_pos
, _), (mem_pos
, _)) -> Pos.btw cls_pos mem_pos
3135 and check_shape_keys_validity
env pos keys =
3136 (* If the key is a class constant, get its class name and type. *)
3137 let get_field_info env key
=
3138 let key_pos = shape_field_pos key
in
3139 (* Empty strings or literals that start with numbers are not
3140 permitted as shape field names. *)
3142 | Ast.SFlit_int
_ ->
3144 | Ast.SFlit_str
(_, key_name
) ->
3145 if (String.length key_name
= 0) then
3146 (Errors.invalid_shape_field_name_empty
key_pos);
3148 | Ast.SFclass_const
(p, cls
as x
, y
) ->
3149 let env, _te
, ty = class_const
env pos ((p, CI x
), y
) in
3150 let env = Typing_enum.check_valid_array_key_type
3151 Errors.invalid_shape_field_type ~allow_any
:false
3153 env, key_pos, Some
(cls
, ty))
3156 let check_field witness_pos witness_info
env key
=
3157 let env, key_pos, key_info
= get_field_info env key
in
3158 match witness_info
, key_info
with
3160 Errors.invalid_shape_field_literal
key_pos witness_pos
; env
3162 Errors.invalid_shape_field_const
key_pos witness_pos
; env
3164 | Some
(cls1
, ty1), Some
(cls2
, ty2
) ->
3165 if cls1
<> cls2
then
3166 Errors.shape_field_class_mismatch
3167 key_pos witness_pos
(strip_ns cls2
) (strip_ns cls1
);
3168 if not
(SubType.is_sub_type
env ty1 ty2
&& SubType.is_sub_type
env ty2
ty1)
3170 Errors.shape_field_type_mismatch
3172 (Typing_print.error
env ty2
) (Typing_print.error
env ty1);
3176 (* Sort the keys by their positions since the error messages will make
3177 * more sense if we take the one that appears first as canonical and if
3178 * they are processed in source order. *)
3179 let cmp_keys x y
= Pos.compare
(shape_field_pos x
) (shape_field_pos y
) in
3180 let keys = List.sort
cmp_keys keys in
3184 | witness
:: rest_keys
->
3185 let env, pos, info
= get_field_info env witness
in
3186 List.fold_left ~f
:(check_field pos info
) ~init
:env rest_keys
3188 and set_valid_rvalue
p env x
ty =
3189 let env = set_local
env (p, x
) ty in
3190 (* We are assigning a new value to the local variable, so we need to
3191 * generate a new expression id
3193 let env = Env.set_local_expr_id
env x
(Ident.tmp
()) in
3196 (* Deal with assignment of a value of type ty2 to lvalue e1 *)
3197 and assign
p env e1 ty2
: _ * T.expr * T.ty =
3198 assign_
p Reason.URassign
env e1 ty2
3200 and assign_
p ur
env e1 ty2
=
3202 | (_, Lvar
((_, x
) as id)) ->
3203 let env, ty1 = set_valid_rvalue
p env x ty2
in
3204 make_result
env (fst e1
) (T.Lvar
id) ty1
3205 | (_, Lplaceholder
id) ->
3206 let placeholder_ty = MakeType.void
(Reason.Rplaceholder
p) in
3207 make_result
env (fst e1
) (T.Lplaceholder
id) placeholder_ty
3209 let env, folded_ty2
= TUtils.fold_unresolved
env ty2
in
3211 TUtils.try_over_concrete_supertypes
env folded_ty2
3212 begin fun env ty2
->
3213 let env, ty2
= SubType.expand_type_and_solve
3214 ~description_of_expected
:"assignable value" env p ty2
in
3216 (* Vector<t> or ImmVector<t> or ConstVector<t> or vec<T> *)
3217 | (_, Tclass
((_, x
), _, [elt_type
]))
3218 when x
= SN.Collections.cVector
3219 || x
= SN.Collections.cImmVector
3220 || x
= SN.Collections.cVec
3221 || x
= SN.Collections.cConstVector
->
3222 let env, tel
= List.map_env
env el
begin fun env e ->
3223 let env, te, _ = assign
(fst
e) env e elt_type
in
3226 make_result
env (fst e1
) (T.List tel
) ty2
3227 (* array<t> or varray<t> *)
3228 | (_, Tarraykind
(AKvec elt_type
))
3229 | (_, Tarraykind
(AKvarray elt_type
)) ->
3230 let env, tel
= List.map_env
env el
begin fun env e ->
3231 let env, te, _ = assign
(fst
e) env e elt_type
in
3234 make_result
env (fst e1
) (T.List tel
) ty2
3235 (* array or empty array or Tany *)
3236 | (r, (Tarraykind
(AKany
| AKempty
) | Tany
)) ->
3237 let env, tel
= List.map_env
env el
begin fun env e ->
3238 let env, te, _ = assign
(fst
e) env e (r, Typing_utils.tany
env) in
3241 make_result
env (fst e1
) (T.List tel
) ty2
3242 | (r, (Tdynamic
)) ->
3243 let env, tel
= List.map_env
env el
begin fun env e ->
3244 let env, te, _ = assign
(fst
e) env e (MakeType.dynamic
r) in
3247 make_result
env (fst e1
) (T.List tel
) ty2
3249 | ((r, Tclass
((_, coll
), _, [ty1; ty2
])) as folded_ety2
)
3250 when coll
= SN.Collections.cPair
->
3253 let env, te1, _ = assign
p env x1
ty1 in
3254 let env, te2, _ = assign
p env x2 ty2
in
3255 make_result
env (fst e1
) (T.List
[te1; te2]) folded_ety2
3257 Errors.pair_arity
p;
3258 make_result
env (fst e1
) T.Any
(r, Typing_utils.terr
env))
3259 (* Other, including tuples. Create a tuple type for the left hand
3260 * side and attempt subtype against it. In particular this deals with
3261 * types such as (string,int) | (int,bool) *)
3263 let env = Env.open_tyvars
env p in
3264 let env, tyl = List.map_env
env el
3265 ~f
:(fun env _ -> Env.fresh_unresolved_type
env (Reason.to_pos
r)) in
3266 let tuple_ty = (Reason.Rwitness
(fst e1
), Ttuple
tyl) in
3267 let env = Type.sub_type
p ur
env folded_ty2
tuple_ty in
3268 let env = Env.set_tyvar_variance
env tuple_ty in
3269 let env = SubType.close_tyvars_and_solve
env in
3270 let env, reversed_tel
=
3271 List.fold2_exn el
tyl ~init
:(env,[]) ~f
:(fun (env,tel
) lvalue ty2
->
3272 let env, te, _ = assign
p env lvalue ty2
in
3274 make_result
env (fst e1
) (T.List
(List.rev reversed_tel
)) ty2
3276 begin match resl with
3278 | _ -> assign_simple
p ur
env e1 ty2
3281 | pobj
, Obj_get
(obj
, (pm
, Id
(_, member_name
as m
)), nullflavor
) ->
3282 let lenv = env.Env.lenv in
3283 let no_fakes = LEnv.env_with_empty_fakes
env in
3284 (* In this section, we check that the assignment is compatible with
3285 * the real type of a member. Remember that members can change
3286 * type (cf fake_members). But when we assign a value to $this->x,
3287 * we want to make sure that the type assign to $this->x is compatible
3288 * with the actual type hint. In this portion of the code, type-check
3289 * the assignment in an environment without fakes, and therefore
3290 * check that the assignment is compatible with the type of
3293 let nullsafe = match nullflavor
with
3294 | OG_nullthrows
-> None
3295 | OG_nullsafe
-> Some pobj
in
3296 let env, tobj, obj_ty = expr ~accept_using_var
:true no_fakes obj
in
3297 let env = might_throw
env in
3298 let env, ty2'
= Env.unbind
env ty2
in
3299 let k (env, member_ty
, vis
) =
3300 let env = Type.coerce_type
p ur
env ty2' member_ty
in
3301 env, member_ty
, vis
in
3303 obj_get ~obj_pos
:(fst obj
) ~is_method
:false ~
nullsafe ~
valkind:`lvalue
3304 env obj_ty (CIexpr e1
) m
k in
3306 T.make_typed_expr pobj result
3308 (tobj, T.make_typed_expr pm result
(T.Id m
), nullflavor
)) in
3309 let env = { env with Env.lenv = lenv } in
3310 begin match obj
with
3312 let env, local = Env.FakeMembers.make
p env obj member_name
in
3313 let env, exp_real_type
= Env.expand_type
env result
in
3314 Typing_suggest.save_member member_name
env exp_real_type ty2
;
3315 let env, ty = set_valid_rvalue
p env local ty2
in
3318 let env, local = Env.FakeMembers.make
p env obj member_name
in
3319 let env, ty = set_valid_rvalue
p env local ty2
in
3321 | _ -> env, te1, ty2
3324 let lenv = env.Env.lenv in
3325 let no_fakes = LEnv.env_with_empty_fakes
env in
3326 let env, te1, real_type
= lvalue
no_fakes e1
in
3327 let env, exp_real_type
= Env.expand_type
env real_type
in
3328 let env = { env with Env.lenv = lenv } in
3329 let env, ty2'
= Env.unbind
env ty2
in
3330 let env = Type.coerce_type
p ur
env ty2' exp_real_type
in
3332 | _, Class_get
(_, CGexpr
_) -> failwith
"AST should not have any CGexprs after naming"
3333 | _, Class_get
((_, x
), CGstring
(_, y
)) ->
3334 let lenv = env.Env.lenv in
3335 let no_fakes = LEnv.env_with_empty_fakes
env in
3336 let env, te1, real_type
= lvalue
no_fakes e1
in
3337 let env, exp_real_type
= Env.expand_type
env real_type
in
3338 let env = { env with Env.lenv = lenv } in
3339 let env, ety2
= Env.expand_type
env ty2
in
3340 let real_type_list =
3341 match exp_real_type
with
3342 | _, Tunion
tyl -> tyl
3345 let env = List.fold_left
real_type_list ~f
:begin fun env real_type
->
3346 Type.coerce_type
p ur
env ety2 real_type
3348 let env, local = Env.FakeMembers.make_static
p env x y
in
3349 let env, ty3
= set_valid_rvalue
p env local ty2
in
3353 Typing_suggest.save_member y
env exp_real_type ty2
;
3356 | pos, Array_get
(e1
, None
) ->
3357 let env, te1, ty1 = update_array_type
pos env e1 None `lvalue
in
3358 let env, (ty1'
, _ty2'
) =
3359 Typing_array_access.assign_array_append ~array_pos
:(fst e1
) ~
expr_pos:p ur
env ty1 ty2
in
3361 if TUtils.is_hack_collection
env ty1
3363 else let env, te1, _ = assign_
p ur
env e1
ty1'
in env, te1 in
3364 make_result
env pos (T.Array_get
(te1, None
)) ty2
3365 | pos, Array_get
(e1
, Some
e) ->
3366 let env, te1, ty1 = update_array_type
pos env e1
(Some
e) `lvalue
in
3367 let env, te, ty = expr env e in
3368 let env, (ty1'
, ty2'
) =
3369 Typing_array_access.assign_array_get ~array_pos
:(fst e1
) ~
expr_pos:p ur
env ty1 e ty ty2
in
3371 if TUtils.is_hack_collection
env ty1
3373 else let env, te1, _ = assign_
p ur
env e1
ty1'
in env, te1 in
3374 env, ((pos, ty2'
), T.Array_get
(te1, Some
te)), ty2
3375 | pref
, Unop
(Ast.Uref
, e1'
) ->
3376 (* references can be "lvalues" in foreach bindings *)
3377 Errors.binding_ref_to_array pref
;
3378 let env, texpr
, ty = assign
p env e1' ty2
in
3379 make_result
env (fst e1
) (T.Unop
(Ast.Uref
, texpr
)) ty
3381 assign_simple
p ur
env e1 ty2
3383 and assign_simple
pos ur
env e1 ty2
=
3384 let env, te1, ty1 = lvalue
env e1
in
3385 let env, ty2
= TUtils.unresolved
env ty2
in
3386 let env = Type.coerce_type
pos ur
env ty2
ty1 in
3389 and array_field
env = function
3390 | Nast.AFvalue ve
->
3391 let env, tve
, tv
= expr env ve
in
3392 let env, tv
= Typing_env.unbind
env tv
in
3393 env, (T.AFvalue tve
, None
, tv
)
3394 | Nast.AFkvalue
(ke
, ve
) ->
3395 let env, tke
, tk = expr env ke
in
3396 let env, tve
, tv
= expr env ve
in
3397 let env, tv
= Typing_env.unbind
env tv
in
3398 env, (T.AFkvalue
(tke
, tve
), Some
tk, tv
)
3400 and array_value ~
expected env x
=
3401 let env, te, ty = expr ?
expected ~
array_ref_ctx:ElementAssignment
env x
in
3402 let env, ty = Typing_env.unbind
env ty in
3405 and array_field_value ~
expected env = function
3406 | Nast.AFvalue x
| Nast.AFkvalue
(_, x
) ->
3407 array_value ~
expected env x
3409 and arraykey_value
p class_name ~
expected env ((pos, _) as x
) =
3410 let env, (te, ty) = array_value ~
expected env x
in
3411 let ty_arraykey = MakeType.arraykey
(Reason.Ridx_dict
pos) in
3412 let env = Type.sub_type
p (Reason.index_class
class_name) env ty ty_arraykey in
3415 and array_field_key ~
expected env = function
3416 (* This shouldn't happen *)
3417 | Nast.AFvalue
(p, _) ->
3418 let ty = MakeType.int (Reason.Rwitness
p) in
3419 env, (T.make_typed_expr
p ty T.Any
, ty)
3420 | Nast.AFkvalue
(x
, _) ->
3421 array_value ~
expected env x
3423 and check_parent_construct
pos env el uel env_parent
=
3424 let check_not_abstract = false in
3425 let env, env_parent
= Phase.localize_with_self
env env_parent
in
3426 let env, _tcid
, tel
, tuel
, parent
, fty =
3427 new_object ~
expected:None ~check_parent
:true ~
check_not_abstract
3428 ~is_using_clause
:false
3429 pos env CIparent
[] el uel
in
3430 (* Not sure why we need to equate these types *)
3431 let env = Type.sub_type
pos (Reason.URnone
) env env_parent parent
in
3432 let env = Type.sub_type
pos (Reason.URnone
) env parent env_parent
in
3433 env, tel
, tuel
, MakeType.void
(Reason.Rwitness
pos), parent
, fty
3435 and call_parent_construct
pos env el uel
=
3436 let parent = Env.get_parent
env in
3439 check_parent_construct
pos env el uel
parent
3450 | Tvarray_or_darray
_
3461 ) -> (* continue here *)
3462 let ty = (Reason.Rwitness
pos, Typing_utils.tany
env) in
3463 let default = env, [], [], ty, ty, ty in
3464 match Env.get_self
env with
3465 | _, Tclass
((_, self
), _, _) ->
3466 (match Env.get_class
env self
with
3467 | Some trait
when Cls.kind trait
= Ast.Ctrait
->
3468 (match trait_most_concrete_req_class trait
env with
3469 | None
-> Errors.parent_in_trait
pos; default
3470 | Some
(_, parent_ty
) ->
3471 check_parent_construct
pos env el uel parent_ty
3474 if not
(Cls.members_fully_known self_tc
)
3475 then () (* Don't know the hierarchy, assume it's correct *)
3476 else Errors.undefined_parent
pos;
3478 | None
-> assert false)
3479 | _, (Terr
| Tany
| Tnonnull
| Tarraykind
_ | Toption
_
3480 | Tprim
_ | Tfun
_ | Ttuple
_ | Tshape
_ | Tvar
_ | Tdynamic
3481 | Tabstract
(_, _) | Tanon
(_, _) | Tunion
_ | Tobject
3483 Errors.parent_outside_class
pos;
3484 let ty = (Reason.Rwitness
pos, Typing_utils.terr
env) in
3485 env, [], [], ty, ty, ty
3487 (* parent::method() in a class definition invokes the specific parent
3488 * version of the method ... it better be callable *)
3489 and check_abstract_parent_meth mname
pos fty =
3490 if is_abstract_ft
fty
3491 then Errors.parent_abstract_call mname
pos (Reason.to_pos
(fst
fty));
3494 and is_abstract_ft
fty = match fty with
3495 | _r
, Tfun
{ ft_abstract
= true; _ } -> true
3496 | _r
, (Terr
| Tany
| Tnonnull
| Tarraykind
_ | Toption
_ | Tprim
_
3497 | Tvar
_ | Tfun
_ | Tclass
_ | Tabstract
_ | Ttuple
_
3498 | Tanon
_ | Tunion
_ | Tobject
| Tshape
_ | Tdynamic
3502 (* Depending on the kind of expression we are dealing with
3503 * The typing of call is different.
3506 and dispatch_call ~
expected ~is_using_clause
p env call_type
3507 (fpos
, fun_expr
as e) tal el uel ~in_suspend
=
3508 let make_call env te thl tel tuel
ty =
3509 make_result
env p (T.Call
(call_type
, te, thl
, tel
, tuel
)) ty in
3510 (* TODO: Avoid Tany annotations in TAST by eliminating `make_call_special` *)
3511 let make_call_special env id tel
ty =
3513 (T.make_typed_expr fpos
(Reason.Rnone
, TUtils.tany
env) (T.Id
id)) [] tel
[] ty in
3514 (* For special functions and pseudofunctions with a definition in hhi. *)
3515 let make_call_special_from_def env id tel
ty_ =
3516 let env, fty = fun_type_of_id
env id tal
in
3517 let ty = match fty with
3518 | _, Tfun
ft -> ft.ft_ret
3519 | _ -> (Reason.Rwitness
p, ty_) in
3520 make_call env (T.make_typed_expr fpos
fty (T.Id
id)) [] tel
[] ty in
3521 let overload_function = overload_function make_call fpos
in
3523 let check_coroutine_call env fty =
3524 let () = if is_return_disposable_fun_type env fty && not is_using_clause
3525 then Errors.invalid_new_disposable
p else () in
3527 - Some true if type is definitely a coroutine
3528 - Some false if type is definitely not a coroutine
3529 - None if type is Tunion that contains
3530 both coroutine and non-coroutine constituents *)
3531 (* TODO: replace the case analysis here with a subtyping check;
3532 * see T37483866 and the linked diff for discussion.
3534 let rec is_coroutine env ty =
3535 let env, ety
= SubType.expand_type_and_solve
env (Reason.to_pos
(fst
ty)) ty
3536 ~description_of_expected
:"a function value" in
3538 | Tfun
{ ft_is_coroutine
= true; _ } ->
3541 env, Some
(Option.value_map
(Env.get_anonymous
env id) ~
default:false ~f
:(fun (_,b
,_,_,_) -> b
))
3542 | Tunion ts
-> are_coroutines
env ts
3545 and are_coroutines
env ts
=
3546 let env, ts_are_coroutines
= List.map_env
env ts ~f
:is_coroutine in
3547 let ts_are_coroutines = match ts_are_coroutines with
3550 (*if rest of the list has the same value as the first element
3551 return value of the first element or None otherwise*)
3552 if List.for_all xs ~f
:(Option.value_map ~
default:false ~f
:((=)x
))
3555 | _ -> Some
false in
3556 env, ts_are_coroutines in
3557 let env, fty_is_coroutine
= is_coroutine env fty in
3558 let () = match in_suspend
, fty_is_coroutine
with
3560 | false, Some
false -> ()
3562 (* non-coroutine call in suspend *)
3563 Errors.non_coroutine_call_in_suspend
3565 (Reason.to_string
("This is " ^
Typing_print.error
env fty) (fst
fty));
3567 (*coroutine call outside of suspend *)
3568 Errors.coroutine_call_outside_of_suspend
p in
3571 let check_function_in_suspend name =
3573 then Errors.function_is_not_coroutine fpos
name in
3575 let check_class_function_in_suspend class_name function_name
=
3576 check_function_in_suspend (class_name ^
"::" ^ function_name
) in
3579 (* Special function `echo` *)
3580 | Id
((p, pseudo_func
) as id) when pseudo_func
= SN.SpecialFunctions.echo
->
3581 check_function_in_suspend SN.SpecialFunctions.echo
;
3582 let env, tel
, _ = exprs ~accept_using_var
:true env el
in
3583 make_call_special env id tel
(MakeType.void
(Reason.Rwitness
p))
3584 (* Special function `empty` *)
3585 | Id
((_, pseudo_func
) as id) when pseudo_func
= SN.PseudoFunctions.empty
->
3586 check_function_in_suspend SN.PseudoFunctions.empty
;
3587 let env, tel
, _ = exprs ~accept_using_var
:true env el
in
3589 Errors.unpacking_disallowed_builtin_function
p pseudo_func
;
3590 make_call_special_from_def env id tel
(Tprim Tbool
)
3591 (* Special function `isset` *)
3592 | Id
((_, pseudo_func
) as id) when pseudo_func
= SN.PseudoFunctions.isset
->
3593 check_function_in_suspend SN.PseudoFunctions.isset
;
3595 exprs ~accept_using_var
:true ~check_defined
:false env el
in
3597 Errors.unpacking_disallowed_builtin_function
p pseudo_func
;
3598 make_call_special_from_def env id tel
(Tprim Tbool
)
3599 (* Special function `unset` *)
3600 | Id
((_, pseudo_func
) as id) when pseudo_func
= SN.PseudoFunctions.unset
->
3601 check_function_in_suspend SN.PseudoFunctions.unset
;
3602 let env, tel
, _ = exprs env el
in
3604 Errors.unpacking_disallowed_builtin_function
p pseudo_func
;
3605 let disallow_varray =
3606 TypecheckerOptions.disallow_unset_on_varray
(Env.get_tcopt
env) in
3607 let unset_error = if disallow_varray then
3608 Errors.unset_nonidx_in_strict_no_varray
3610 Errors.unset_nonidx_in_strict
in
3611 let env = if Env.is_strict
env then
3613 | [(_, Array_get
((_, Class_const
_), Some
_))], [] ->
3614 Errors.const_mutation
p Pos.none
"";
3616 | [(_, Array_get
(ea
, Some
_))], [] ->
3617 let env, _te
, ty = expr env ea
in
3618 let tany = (Reason.Rnone
, Typing_utils.tany env) in
3619 if List.exists ~f
:(fun super
-> SubType.is_sub_type
env ty super
) [
3620 MakeType.dict
Reason.Rnone
tany tany;
3621 MakeType.keyset
Reason.Rnone
tany;
3622 if disallow_varray then
3623 (Reason.Rnone
, Tarraykind
(AKmap
(tany, tany)))
3624 else (Reason.Rnone
, Tarraykind AKany
);
3629 (Reason.to_string
("This is " ^
Typing_print.error
env ty) (fst
ty));
3632 | _ -> unset_error p []; env)
3635 | [(p, Obj_get
(_, _, OG_nullsafe
))] ->
3637 Errors.nullsafe_property_write_context
p;
3638 make_call_special_from_def env id tel
(TUtils.terr
env)
3641 make_call_special_from_def env id tel
(Tprim Tvoid
))
3642 (* Special function `array_filter` *)
3643 | Id
((_, array_filter
) as id)
3644 when array_filter
= SN.StdlibFunctions.array_filter
&& el
<> [] && uel
= [] ->
3645 check_function_in_suspend SN.StdlibFunctions.array_filter
;
3646 (* dispatch the call to typecheck the arguments *)
3647 let env, fty = fun_type_of_id
env id tal
in
3648 let env, tel
, tuel
, res
= call ~
expected p env fty el uel
in
3649 (* but ignore the result and overwrite it with custom return type *)
3650 let x = List.hd_exn el
in
3651 let env, _tx
, ty = expr env x in
3652 let explain_array_filter (r, t
) =
3653 (Reason.Rarray_filter
(p, r), t
) in
3654 let get_value_type env tv
=
3656 if List.length el
> 1
3658 else TUtils.non_null
env p tv
in
3659 env, explain_array_filter tv
in
3660 let rec get_array_filter_return_type env ty =
3661 let env, ety
= Env.expand_type
env ty in
3663 | (_, Tarraykind
(AKany
| AKempty
)) as array_type
->
3665 | (r, Tarraykind
(AKvec tv
| AKvarray tv
)) ->
3666 let env, tv
= get_value_type env tv
in
3667 env, (r, Tarraykind
(AKvec tv
))
3669 let acc, x = List.map_env
env x get_array_filter_return_type in
3672 env, (r, Typing_utils.tany env)
3674 env, (r, Typing_utils.terr
env)
3676 let env, tk = Env.fresh_unresolved_type
env p in
3677 let env, tv
= Env.fresh_unresolved_type
env p in
3680 let keyed_container_type = MakeType.keyed_container
Reason.Rnone
tk tv
in
3681 let env = SubType.sub_type
env ety
keyed_container_type in
3682 let env, tv
= get_value_type env tv
in
3683 env, (r, Tarraykind
(AKmap
(
3684 (explain_array_filter tk),
3687 (fun _ -> Errors.try_
3689 let container_type = MakeType.container
Reason.Rnone tv
in
3690 let env = SubType.sub_type
env ety
container_type in
3691 let env, tv
= get_value_type env tv
in
3692 env, (r, Tarraykind
(AKmap
(
3693 (explain_array_filter (MakeType.arraykey
r)),
3695 (fun _ -> env, res
)))
3696 in let env, rty = get_array_filter_return_type env ty in
3699 | r, Tfun
ft -> r, Tfun
{ft with ft_ret
= rty}
3701 make_call env (T.make_typed_expr fpos
fty (T.Id
id)) tal tel tuel
rty
3702 (* Special function `type_structure` *)
3703 | Id
(p, type_structure
)
3704 when type_structure
= SN.StdlibFunctions.type_structure
3705 && (List.length el
= 2) && uel
= [] ->
3706 check_function_in_suspend SN.StdlibFunctions.type_structure
;
3710 | p, Nast.String cst
->
3711 (* find the class constant implicitly defined by the typeconst *)
3712 let cid = (match e1
with
3713 | _, Class_const
(cid, (_, x))
3714 | _, Class_get
(cid, CGstring
(_, x)) when x = SN.Members.mClass
-> cid
3715 | _ -> (fst e1
, Nast.CIexpr e1
)) in
3716 class_const ~incl_tc
:true env p (cid, (p, cst
))
3718 Errors.illegal_type_structure
p "second argument is not a string";
3719 expr_error env p (Reason.Rwitness
p))
3720 | _ -> assert false)
3721 (* Special function `array_map` *)
3722 | Id
((_, array_map
) as x)
3723 when array_map
= SN.StdlibFunctions.array_map
&& el
<> [] && uel
= [] ->
3724 check_function_in_suspend SN.StdlibFunctions.array_map
;
3725 let env, fty = fun_type_of_id
env x [] in
3726 let env, fty = Env.expand_type
env fty in
3727 let env, fty = match fty, el
with
3728 | ((r_fty
, Tfun
fty), _::args
) when args
<> [] ->
3729 let arity = List.length args
in
3731 Builds a function with signature:
3733 function<T1, ..., Tn, Tr>(
3734 (function(T1, ..., Tn):Tr),
3740 where R is constructed by build_output_container applied to Tr
3742 let build_function env build_output_container
=
3744 (* If T1, ... Tn, Tr are provided explicitly, instantiate the function parameters with
3745 * those directly. *)
3746 if List.is_empty tal
3748 let env, tr
= Env.fresh_unresolved_type
env p in
3749 let env, vars
= List.map_env
env args
3750 ~f
:(fun env _ -> Env.fresh_unresolved_type
env p) in
3752 else if List.length tal
<> List.length args
+ 1 then begin
3753 let env, tr
= Env.fresh_unresolved_type
env p in
3754 Errors.expected_tparam ~use_pos
:fpos ~definition_pos
:fty.ft_pos
3755 (1 + (List.length args
));
3756 let env, vars
= List.map_env
env args
3757 ~f
:(fun env _ -> Env.fresh_unresolved_type
env p) in
3760 let env, vars_and_tr
= List.map_env
env tal
Phase.localize_hint_with_self
in
3761 let vars, trl
= List.split_n vars_and_tr
(List.length vars_and_tr
- 1) in
3762 (* Since we split the arguments and return type at the last index and the length is
3763 non-zero this is safe. *)
3764 let tr = List.hd_exn trl
in
3767 let f = TUtils.default_fun_param
(
3770 ft_pos
= fty.ft_pos
;
3771 ft_deprecated
= None
;
3772 ft_abstract
= false;
3773 ft_is_coroutine
= false;
3774 ft_arity
= Fstandard
(arity, arity);
3775 ft_tparams
= ([], FTKtparams
);
3776 ft_where_constraints
= [];
3777 ft_params
= List.map
vars TUtils.default_fun_param
;
3779 ft_reactive
= fty.ft_reactive
;
3780 ft_mutability
= fty.ft_mutability
;
3781 ft_returns_mutable
= fty.ft_returns_mutable
;
3782 ft_return_disposable
= fty.ft_return_disposable
;
3783 ft_decl_errors
= None
;
3784 ft_returns_void_to_rx
= fty.ft_returns_void_to_rx
;
3787 let containers = List.map
vars (fun var
->
3788 let tc = Tclass
((fty.ft_pos
, SN.Collections.cContainer
), Nonexact
, [var
]) in
3789 TUtils.default_fun_param
(r_fty
, tc)
3791 env, (r_fty
, Tfun
{fty with
3792 ft_arity
= Fstandard
(arity+1, arity+1);
3793 ft_params
= f::containers;
3794 ft_ret
= build_output_container
tr;
3799 Takes a Container type and returns a function that can "pack" a type
3800 into an array of appropriate shape, preserving the key type, i.e.:
3801 array -> f, where f R = array
3802 array<X> -> f, where f R = array<R>
3803 array<X, Y> -> f, where f R = array<X, R>
3804 Vector<X> -> f where f R = array<R>
3805 KeyedContainer<X, Y> -> f, where f R = array<X, R>
3806 Container<X> -> f, where f R = array<arraykey, R>
3807 X -> f, where f R = Y
3809 let rec build_output_container
3810 env (x:locl
ty) : (Env.env * (locl
ty -> locl
ty)) =
3811 let env, x = Env.expand_type
env x in
3813 | (_, Tarraykind
(AKany
| AKempty
)) as array_type
->
3814 env, (fun _ -> array_type
)
3815 | (r, Tarraykind
(AKvec
_ | AKvarray
_)) ->
3816 env, (fun tr -> (r, Tarraykind
(AKvec
(tr))) )
3818 env, (fun _ -> (r, Typing_utils.tany env))
3820 env, (fun _ -> (r, Typing_utils.terr
env))
3822 let env, x = List.map_env
env x build_output_container in
3823 env, (fun tr -> (r, Tunion
(List.map
x (fun f -> f tr))))
3825 let env, tk = Env.fresh_unresolved_type
env p in
3826 let env, tv
= Env.fresh_unresolved_type
env p in
3827 let try_vector env =
3828 let vector_type = MakeType.const_vector r_fty tv
in
3829 let env = SubType.sub_type
env x vector_type in
3830 env, (fun tr -> (r, Tarraykind
(
3833 let try_keyed_container env =
3834 let keyed_container_type = MakeType.keyed_container r_fty
tk tv
in
3835 let env = SubType.sub_type
env x keyed_container_type in
3836 env, (fun tr -> (r, Tarraykind
(AKmap
(
3840 let try_container env =
3841 let container_type = MakeType.container r_fty tv
in
3842 let env = SubType.sub_type
env x container_type in
3843 env, (fun tr -> (r, Tarraykind
(AKmap
(
3844 (MakeType.arraykey
r),
3850 (fun _ -> Errors.try_
3852 try_keyed_container env)
3853 (fun _ -> Errors.try_
3856 (fun _ -> env, (fun _ -> (Reason.Rwitness
p, Typing_utils.tany env))))) in
3859 Single argument calls preserve the key type, multi argument
3860 calls always return an array<Tr>
3864 let env, _tx
, x = expr env x in
3865 let env, output_container
= build_output_container env x in
3866 build_function env output_container
3868 build_function env (fun tr ->
3869 (r_fty
, Tarraykind
(AKvec
(tr)))))
3871 let env, tel
, tuel
, ty = call ~
expected p env fty el
[] in
3872 make_call env (T.make_typed_expr fpos
fty (T.Id
x)) tal tel tuel
ty
3873 (* Special function `idx` *)
3874 | Id
((_, idx
) as id) when idx
= SN.FB.idx
->
3875 check_function_in_suspend SN.FB.idx
;
3876 (* Directly call get_fun so that we can muck with the type before
3877 * instantiation -- much easier to work in terms of Tgeneric Tk/Tv than
3878 * trying to figure out which Tvar is which. *)
3879 (match Env.get_fun
env (snd
id) with
3881 let param1, param2
, param3
=
3882 match fty.ft_params
with
3883 | [param1; param2
; param3
] -> param1, param2
, param3
3884 | _ -> assert false in
3885 let { fp_type
= (r2
, _); _ } = param2
in
3886 let { fp_type
= (r3
, _); _ } = param3
in
3887 let params, ret = match List.length el
with
3889 let ty1 = match param1.fp_type
with
3890 | (r11
, Toption
(r12
, Tapply
(coll
, [tk; (r13
, _) as tv
]))) ->
3891 (r11
, Toption
(r12
, Tapply
(coll
, [tk; (r13
, Toption tv
)])))
3892 | _ -> assert false in
3893 let param1 = { param1 with fp_type
= ty1 } in
3894 let ty2 = MakeType.nullable r2
(r2
, Tgeneric
"Tk") in
3895 let param2 = { param2 with fp_type
= ty2 } in
3896 let rret = fst
fty.ft_ret
in
3897 let ret = MakeType.nullable
rret (rret, Tgeneric
"Tv") in
3898 [param1; param2], ret
3900 let param2 = { param2 with fp_type
= MakeType.nullable r2
(r2
, Tgeneric
"Tk") } in
3901 let param3 = { param3 with fp_type
= (r3
, Tgeneric
"Tv") } in
3902 let ret = (fst
fty.ft_ret
, Tgeneric
"Tv") in
3903 [param1; param2; param3], ret
3904 | _ -> fty.ft_params
, fty.ft_ret
in
3905 let fty = { fty with ft_params
= params; ft_ret
= ret } in
3906 let ety_env = Phase.env_with_self
env in
3907 let env, fty = Phase.(localize_ft
3908 ~instantiation
:{ use_pos
=p; use_name
="idx"; explicit_tparams
=[]}
3909 ~
ety_env env fty) in
3910 let tfun = Reason.Rwitness
fty.ft_pos
, Tfun
fty in
3911 let env, tel
, _tuel
, ty = call ~
expected p env tfun el
[] in
3912 (* Remove double nullables. This shouldn't be necessary, and currently
3913 * interferes with new_inference because it "solves" before we set variance
3915 let env, ty = match ty with
3916 | r, Toption
ty when not
(TypecheckerOptions.new_inference
(Env.get_tcopt
env)) ->
3917 let env, ty = TUtils.non_null
env p ty in
3918 env, (r, Toption
ty)
3920 make_call env (T.make_typed_expr fpos
tfun (T.Id
id)) [] tel
[] ty
3921 | None
-> unbound_name env id)
3923 (* Special function `Shapes::idx` *)
3924 | Class_const
((_, CI
(_, shapes
)) as class_id
, ((_, idx
) as method_id
))
3925 when shapes
= SN.Shapes.cShapes
&& idx
= SN.Shapes.idx
->
3926 check_class_function_in_suspend SN.Shapes.cShapes
SN.Shapes.idx
;
3927 overload_function p env class_id method_id el uel
3928 begin fun env fty res el
-> match el
with
3930 let env, _ts
, shape_ty
= expr env shape
in
3931 Typing_shapes.idx
env p (fst
fty) shape_ty field None
3932 | [shape
; field
; default] ->
3933 let env, _ts
, shape_ty
= expr env shape
in
3934 let env, _td
, default_ty
= expr env default in
3935 Typing_shapes.idx
env p (fst
fty) shape_ty field
3936 (Some
((fst
default), default_ty
))
3939 (* Special function `Shapes::at` *)
3940 | Class_const
((_, CI
(_, shapes
)) as class_id
, ((_, at
) as method_id
))
3941 when shapes
= SN.Shapes.cShapes
&& at
= SN.Shapes.at
->
3942 check_class_function_in_suspend SN.Shapes.cShapes
SN.Shapes.at
;
3943 overload_function p env class_id method_id el uel
3944 begin fun env _fty res el
-> match el
with
3946 let env, _te
, shape_ty
= expr env shape
in
3947 Typing_shapes.at
env p shape_ty field
3950 (* Special function `Shapes::keyExists` *)
3951 | Class_const
((_, CI
(_, shapes
)) as class_id
, ((_, key_exists
) as method_id
))
3952 when shapes
= SN.Shapes.cShapes
&& key_exists
= SN.Shapes.keyExists
->
3953 check_class_function_in_suspend SN.Shapes.cShapes
SN.Shapes.keyExists
;
3954 overload_function p env class_id method_id el uel
3955 begin fun env fty res el
-> match el
with
3957 let env, _te
, shape_ty
= expr env shape
in
3958 (* try accessing the field, to verify existence, but ignore
3959 * the returned type and keep the one coming from function
3960 * return type hint *)
3961 let env, _ = Typing_shapes.idx
env p (fst
fty) shape_ty field None
in
3965 (* Special function `Shapes::removeKey` *)
3966 | Class_const
((_, CI
(_, shapes
)) as class_id
, ((_, remove_key
) as method_id
))
3967 when shapes
= SN.Shapes.cShapes
&& remove_key
= SN.Shapes.removeKey
->
3968 check_class_function_in_suspend SN.Shapes.cShapes
SN.Shapes.removeKey
;
3969 overload_function p env class_id method_id el uel
3970 begin fun env _ res el
-> match el
with
3971 | [shape
; field
] -> begin match shape
with
3972 | (_, Lvar
(_, lvar
))
3973 | (_, Callconv
(Ast.Pinout
, (_, Lvar
(_, lvar
))))
3974 | (_, Unop
(Ast.Uref
, (_, Lvar
(_, lvar
)))) ->
3975 let env, _te
, shape_ty
= expr ~is_func_arg
:true env shape
in
3977 Typing_shapes.remove_key
p env shape_ty field
in
3978 let env, _ = set_valid_rvalue
p env lvar shape_ty
in
3981 Errors.invalid_shape_remove_key
(fst shape
);
3986 (* Special function `Shapes::toArray` *)
3987 | Class_const
((_, CI
(_, shapes
)) as class_id
, ((_, to_array
) as method_id
))
3988 when shapes
= SN.Shapes.cShapes
&& to_array
= SN.Shapes.toArray
->
3989 check_class_function_in_suspend SN.Shapes.cShapes
SN.Shapes.toArray
;
3990 overload_function p env class_id method_id el uel
3991 begin fun env _ res el
-> match el
with
3993 let env, _te
, shape_ty
= expr env shape
in
3994 Typing_shapes.to_array
env p shape_ty res
3998 (* Special function `Shapes::toDict` *)
3999 | Class_const
((_, CI
(_, shapes
)) as class_id
, ((_, to_array
) as method_id
))
4000 when shapes
= SN.Shapes.cShapes
&& to_array
= SN.Shapes.toDict
->
4001 check_class_function_in_suspend SN.Shapes.cShapes
SN.Shapes.toDict
;
4002 overload_function p env class_id method_id el uel
4003 begin fun env _ res el
-> match el
with
4005 let env, _te
, shape_ty
= expr env shape
in
4006 Typing_shapes.to_dict
env p shape_ty res
4010 (* Special function `parent::__construct` *)
4011 | Class_const
((pos, CIparent
), ((_, construct
) as id))
4012 when construct
= SN.Members.__construct
->
4013 check_class_function_in_suspend "parent" SN.Members.__construct
;
4014 let env, tel
, tuel
, ty, pty
, ctor_fty
=
4015 call_parent_construct
p env el uel
in
4016 make_call env (T.make_typed_expr fpos ctor_fty
4017 (T.Class_const
(((pos, pty
), T.CIparent
), id))) tal tel tuel
ty
4019 (* Calling parent method *)
4020 | Class_const
((pos, CIparent
), m
) ->
4021 let env, tcid
, ty1 = static_class_id ~check_constraints
:false pos env [] CIparent
in
4022 let this_ty = (Reason.Rwitness fpos
, TUtils.this_of
(Env.get_self
env)) in
4023 if Env.is_static
env
4025 (* in static context, you can only call parent::foo() on static
4028 class_get ~is_method
:true ~is_const
:false ~explicit_tparams
:tal
env ty1 m CIparent
in
4029 let fty = check_abstract_parent_meth
(snd m
) p fty in
4030 let env = check_coroutine_call env fty in
4031 let env, tel
, tuel
, ty =
4033 ~method_call_info
:(TR.make_call_info ~receiver_is_self
:false
4034 ~is_static
:true this_ty (snd m
))
4036 make_call env (T.make_typed_expr fpos
fty
4037 (T.Class_const
(tcid
, m
))) tal tel tuel
ty
4040 (* in instance context, you can call parent:foo() on static
4041 * methods as well as instance methods *)
4042 if not
(class_contains_smethod
env ty1 m
)
4044 (* parent::nonStaticFunc() is really weird. It's calling a method
4045 * defined on the parent class, but $this is still the child class.
4046 * We can deal with this by hijacking the continuation that
4047 * calculates the SN.Typehints.this type *)
4048 let k_lhs _ = this_ty in
4049 let ftys = ref [] in
4050 let env, method_
, _ =
4051 obj_get_ ~is_method
:true ~
nullsafe:None ~obj_pos
:pos
4052 ~pos_params
:(Some el
) ~
valkind:`other
env ty1 CIparent m
4053 begin fun (env, fty, _) ->
4054 let fty = check_abstract_parent_meth
(snd m
) p fty in
4055 let env = check_coroutine_call env fty in
4056 let env, _tel
, _tuel
, method_
= call ~
expected
4057 ~method_call_info
:(TR.make_call_info ~receiver_is_self
:false
4058 ~is_static
:false this_ty (snd m
))
4060 ftys := fty :: !ftys;
4068 | l
-> (Reason.none
, Tunion l
) in
4069 make_call env (T.make_typed_expr fpos
fty (T.Class_const
(tcid
, m
)))
4073 class_get ~is_method
:true ~is_const
:false ~explicit_tparams
:tal
env ty1 m CIparent
in
4074 let fty = check_abstract_parent_meth
(snd m
) p fty in
4075 let env = check_coroutine_call env fty in
4076 let env, tel
, tuel
, ty =
4077 call ~
expected ~method_call_info
:(TR.make_call_info ~receiver_is_self
:false
4078 ~is_static
:true this_ty (snd m
))
4080 make_call env (T.make_typed_expr fpos
fty
4081 (T.Class_const
(tcid
, m
))) tal tel tuel
ty
4083 (* Call class method *)
4084 | Class_const
((pid
, e1
), m
) ->
4085 let env, te1, ty1 = static_class_id ~check_constraints
:true pid
env [] e1
in
4087 class_get ~is_method
:true ~is_const
:false ~explicit_tparams
:tal
4088 ~pos_params
:el
env ty1 m e1
in
4089 let () = match e1
with
4090 | CIself
when is_abstract_ft
fty ->
4091 begin match Env.get_self
env with
4092 | _, Tclass
((_, self
), _, _) ->
4093 (* at runtime, self:: in a trait is a call to whatever
4094 * self:: is in the context of the non-trait "use"-ing
4095 * the trait's code *)
4096 begin match Env.get_class
env self
with
4097 | Some cls
when Cls.kind cls
= Ast.Ctrait
-> ()
4098 | _ -> Errors.self_abstract_call
(snd m
) p (Reason.to_pos
(fst
fty))
4102 | CI
c when is_abstract_ft
fty ->
4103 Errors.classname_abstract_call
(snd
c) (snd m
) p (Reason.to_pos
(fst
fty))
4104 | CI
(_, classname
) ->
4105 begin match Decl_provider.get_class classname
with
4107 let (_, method_name
) = m
in
4108 begin match Cls.get_smethod class_def method_name
with
4111 if elt
.ce_synthesized
then
4112 Errors.static_synthetic_method classname
(snd m
) p (Reason.to_pos
(fst
fty))
4115 (* This technically should be an error, but if we throw here we'll break a ton of our
4116 tests since they reference classes that only exist in www, and any missing classes will
4117 get caught elsewhere in the pipeline. *)
4121 let env = check_coroutine_call env fty in
4122 let env, tel
, tuel
, ty =
4124 ~method_call_info
:(TR.make_call_info ~receiver_is_self
:(e1
= CIself
)
4125 ~is_static
:true ty1 (snd m
))
4127 make_call env (T.make_typed_expr fpos
fty
4128 (T.Class_const
(te1, m
))) tal tel tuel
ty
4129 (* <<__PPL>>: sample, factor, observe, condition *)
4130 | Id
(pos, id) when env.Env.inside_ppl_class
&& SN.PPLFunctions.is_reserved
id ->
4131 let m = (pos, String_utils.lstrip
id "\\") in
4132 (* Mock these as type equivalent to \Infer -> sample... *)
4133 let infer_e = CI
(p, "\\Infer") in
4134 let env, _, ty1 = static_class_id ~check_constraints
:true p env [] infer_e in
4135 let nullsafe = None
in
4136 let tel = ref [] and tuel
= ref [] and tftyl
= ref [] in
4137 let fn = (fun (env, fty, _) ->
4138 let env, tel_
, tuel_
, method_
=
4141 ~method_call_info
:(TR.make_call_info ~receiver_is_self
:false
4142 ~is_static
:false ty1 (snd
m))
4144 tel := tel_
; tuel
:= tuel_
;
4145 tftyl
:= fty :: !tftyl
;
4146 env, method_
, None
) in
4147 let env, ty = obj_get ~obj_pos
:p ~is_method
:true ~
nullsafe ~pos_params
:el
4148 ~explicit_tparams
:tal
env ty1 infer_e m fn in
4152 | tftyl
-> (Reason.none
, Tunion tftyl
)
4154 make_call env (T.make_typed_expr fpos
tfty (T.Fun_id
m)) tal
!tel !tuel
ty
4156 (* Call instance method *)
4157 | Obj_get
(e1
, (pos_id
, Id
m), nullflavor
) ->
4158 let is_method = call_type
= Cnormal
in
4159 let env, te1, ty1 = expr ~accept_using_var
:true env e1
in
4161 (match nullflavor
with
4162 | OG_nullthrows
-> None
4163 | OG_nullsafe
-> Some
p
4165 let tel = ref [] and tuel
= ref [] and tftyl
= ref [] in
4166 let k = (fun (env, fty, _) ->
4167 let env = check_coroutine_call env fty in
4168 let env, tel_
, tuel_
, method_
=
4170 ~method_call_info
:(TR.make_call_info ~receiver_is_self
:false
4171 ~is_static
:false ty1 (snd
m))
4173 tel := tel_
; tuel
:= tuel_
;
4174 tftyl
:= fty :: !tftyl
;
4175 env, method_
, None
) in
4176 let env, ty = obj_get ~obj_pos
:(fst e1
) ~
is_method ~
nullsafe ~pos_params
:el
4177 ~explicit_tparams
:tal
env ty1 (CIexpr e1
) m k in
4181 | tftyl
-> (Reason.none
, Tunion tftyl
)
4183 make_call env (T.make_typed_expr fpos
tfty (T.Obj_get
(te1,
4184 T.make_typed_expr pos_id
tfty (T.Id
m), nullflavor
))) tal
!tel !tuel
ty
4186 (* Function invocation *)
4188 let env, fty = fun_type_of_id
env x tal
in
4189 let env = check_coroutine_call env fty in
4190 let env, tel, tuel
, ty =
4191 call ~
expected p env fty el uel
in
4192 make_call env (T.make_typed_expr fpos
fty (T.Fun_id
x)) tal
tel tuel
ty
4193 | Id
(_, id as x) ->
4194 let env, fty = fun_type_of_id
env x tal
in
4195 let env = check_coroutine_call env fty in
4196 let env, tel, tuel
, ty =
4197 call ~
expected p env fty el uel
in
4198 let is_mutable = id = SN.Rx.mutable_
in
4199 let is_move = id = SN.Rx.move
in
4200 let is_freeze = id = SN.Rx.freeze
in
4201 (* error when rx builtins are used in non-reactive context *)
4202 if not
(Env.env_local_reactive
env) then begin
4203 if is_mutable then Errors.mutable_in_nonreactive_context
p
4204 else if is_move then Errors.move_in_nonreactive_context
p
4205 else if is_freeze then Errors.freeze_in_nonreactive_context
p
4207 (* ban unpacking when calling builtings *)
4208 if (is_mutable || is_move || is_freeze) && uel
<> [] then begin
4209 Errors.unpacking_disallowed_builtin_function
p id
4211 (* adjust env for Rx\freeze or Rx\move calls *)
4214 then Typing_mutability.freeze_local
p env tel
4216 then Typing_mutability.move_local
p env tel
4219 make_call env (T.make_typed_expr fpos
fty (T.Id
x)) tal
tel tuel
ty
4221 let env, te, fty = expr env e in
4222 let env, fty = SubType.expand_type_and_solve
4223 ~description_of_expected
:"a function value" env fpos
fty in
4224 let env = check_coroutine_call env fty in
4225 let env, tel, tuel
, ty = call ~
expected p env fty el uel
in
4226 make_call env te tal
tel tuel
ty
4228 and fun_type_of_id
env x tal
=
4229 match Env.get_fun
env (snd
x) with
4230 | None
-> let env, _, ty = unbound_name env x in env, ty
4232 let ety_env = Phase.env_with_self
env in
4235 ~instantiation
: { use_name
= strip_ns
(snd
x); use_pos
= fst
x; explicit_tparams
= tal
}
4236 ~
ety_env env fty) in
4237 env, (Reason.Rwitness
fty.ft_pos
, Tfun
fty)
4240 * Checks if a class (given by cty) contains a given static method.
4242 * We could refactor this + class_get
4244 and class_contains_smethod
env cty
(_pos
, mid
) =
4245 let lookup_member ty =
4247 | _, Tclass
((_, c), _, _) ->
4248 (match Env.get_class
env c with
4251 Option.is_some
@@ Env.get_static_member
true env class_ mid
4254 let _env, tyl = TUtils.get_concrete_supertypes
env cty
in
4255 List.exists
tyl ~
f:lookup_member
4257 and class_get ~
is_method ~is_const ?
(explicit_tparams
=[]) ?
(incl_tc
=false)
4258 ?
(pos_params
: expr list
option) env cty
(p, mid
) cid =
4261 this_for_method
env cid cty
4265 type_expansions
= [];
4267 substs
= SMap.empty
;
4268 from_class
= Some
cid;
4269 validate_dty
= None
;
4271 class_get_ ~
is_method ~is_const ~
ety_env ~explicit_tparams ~incl_tc
4272 ~pos_params
env cid cty
(p, mid
)
4274 and class_get_ ~
is_method ~is_const ~
ety_env ?
(explicit_tparams
=[])
4275 ?
(incl_tc
=false) ~pos_params
env cid cty
4277 let env, cty
= Env.expand_type
env cty
in
4279 | r, Tany
-> env, (r, Typing_utils.tany env), None
4280 | r, Terr
-> env, err_witness env (Reason.to_pos
r), None
4281 | _, Tdynamic
-> env, cty
, None
4284 List.map_env
env tyl begin fun env ty ->
4286 class_get_ ~
is_method ~is_const ~
ety_env ~explicit_tparams ~incl_tc
4287 ~pos_params
env cid ty (p, mid
)
4290 let env, ty = Union.union_list
env (fst cty
) tyl in
4291 let env, method_
= TUtils.in_var
env ty in
4293 | _, Tabstract
(_, Some
ty) ->
4294 class_get_ ~
is_method ~is_const ~
ety_env ~explicit_tparams ~incl_tc
4295 ~pos_params
env cid ty (p, mid
)
4296 | _, Tabstract
(_, None
) ->
4297 let resl = TUtils.try_over_concrete_supertypes
env cty
(fun env ty ->
4298 class_get_ ~
is_method ~is_const ~
ety_env ~explicit_tparams ~incl_tc
4299 ~pos_params
env cid ty (p, mid
)) in
4300 begin match resl with
4302 Errors.non_class_member
4303 mid
p (Typing_print.error
env cty
)
4304 (Reason.to_pos
(fst cty
));
4305 (env, err_witness env p, None
)
4306 | ((_, (_, ty), _) as res
)::rest ->
4307 if List.exists
rest (fun (_, (_, ty'
), _) -> ty'
<> ty)
4310 Errors.ambiguous_member
4311 mid
p (Typing_print.error
env cty
)
4312 (Reason.to_pos
(fst cty
));
4313 (env, err_witness env p, None
)
4317 | _, Tclass
((_, c), _, paraml
) ->
4318 let class_ = Env.get_class
env c in
4320 | None
-> env, (Reason.Rwitness
p, Typing_utils.tany env), None
4322 (* We need to instantiate generic parameters in the method signature *)
4325 substs
= Subst.make
(Cls.tparams
class_) paraml
} in
4326 if is_const
then begin
4328 if incl_tc
then Env.get_const
env class_ mid
else
4329 match Env.get_typeconst
env class_ mid
with
4331 Errors.illegal_typeconst_direct_access
p;
4334 Env.get_const
env class_ mid
4338 smember_not_found
p ~is_const ~
is_method class_ mid
;
4339 env, (Reason.Rnone
, Typing_utils.terr
env), None
4340 | Some
{ cc_type
; cc_abstract
; cc_pos
; _ } ->
4341 let env, cc_type
= Phase.localize ~
ety_env env cc_type
in
4344 then Some
(cc_pos
, (Cls.name class_) ^
"::" ^ mid
)
4347 let smethod = Env.get_static_member
is_method env class_ mid
in
4350 smember_not_found
p ~is_const ~
is_method class_ mid
;
4351 env, (Reason.Rnone
, Typing_utils.terr
env), None
4352 | Some
{ ce_visibility
= vis
; ce_lsb
= lsb
; ce_type
= lazy method_
; _ } ->
4353 let p_vis = Reason.to_pos
(fst method_
) in
4354 TVis.check_class_access
p env (p_vis, vis
, lsb
) cid class_;
4356 begin match method_
with
4357 (* We special case Tfun here to allow passing in explicit tparams to localize_ft. *)
4360 Phase.(localize_ft ~instantiation
: { use_name
=strip_ns mid
; use_pos
=p; explicit_tparams
}
4362 in env, (r, Tfun
ft)
4364 Phase.localize ~
ety_env env method_
4369 | _, (Tvar
_ | Tnonnull
| Tarraykind
_ | Toption
_
4370 | Tprim
_ | Tfun
_ | Ttuple
_ | Tanon
(_, _) | Tobject
4372 (* should never happen; static_class_id takes care of these *)
4373 env, (Reason.Rnone
, Typing_utils.tany env), None
4375 and smember_not_found
pos ~is_const ~
is_method class_ member_name
=
4377 if is_const
then `class_constant
4378 else if is_method then `static_method
4379 else `class_variable
in
4381 let cid = ((Cls.pos class_), (Cls.name class_)) in
4382 Errors.smember_not_found
kind pos cid member_name hint
4384 match Env.suggest_static_member
is_method class_ member_name
with
4386 (match Env.suggest_member
is_method class_ member_name
with
4387 | None
when not
(Cls.members_fully_known
class_) ->
4388 (* no error in this case ... the member might be present
4389 * in one of the parents of class_ that the typing cannot see *)
4394 error (`closest
(pos2
, v
))
4397 error (`did_you_mean
(pos2
, v
))
4399 and member_not_found
pos ~
is_method class_ member_name
r =
4400 let kind = if is_method then `method_
else `member
in
4401 let cid = (Cls.pos class_), (Cls.name class_) in
4402 let reason = Reason.to_string
4403 ("This is why I think it is an object of type "^strip_ns
(Cls.name class_)) r
4406 Errors.member_not_found
kind pos cid member_name hint
reason in
4407 match Env.suggest_member
is_method class_ member_name
with
4409 (match Env.suggest_static_member
is_method class_ member_name
with
4410 | None
when not
(Cls.members_fully_known
class_) ->
4411 (* no error in this case ... the member might be present
4412 * in one of the parents of class_ that the typing cannot see *)
4416 | Some
(def_pos
, v
) ->
4417 error (`closest
(def_pos
, v
))
4419 | Some
(def_pos
, v
) ->
4420 error (`did_you_mean
(def_pos
, v
))
4422 (* Look up the type of the property or method id in the type ty1 of the
4423 *receiver and use the function k to postprocess the result.
4424 * Return any fresh type variables that were substituted for generic type
4425 * parameters in the type of the property or method.
4427 * Essentially, if ty1 is a concrete type, e.g., class C, then k is applied
4428 * to the type of the property id in C; and if ty1 is an unresolved type,
4429 * e.g., a union of classes (C1 | ... | Cn), then k is applied to the type
4430 * of the property id in each Ci and the results are collected into an
4433 * The extra flexibility offered by the functional argument k is used in two
4436 * (1) when type-checking method calls: if the receiver has an unresolved
4437 * type, then we need to type-check the method call with each possible
4438 * receiver type and collect the results into an unresolved type;
4440 * (2) when type-checking assignments to properties: if the receiver has
4441 * an unresolved type, then we need to check that the right hand side
4442 * value can be assigned to the property id for each of the possible types
4445 and obj_get ~obj_pos ~
is_method ~
nullsafe ?
(valkind = `other
) ?
(explicit_tparams
=[])
4446 ?
(pos_params
: expr list
option) env ty1 cid id k =
4447 let env, method_
, _ =
4448 obj_get_with_visibility ~
is_method ~
nullsafe ~
valkind ~obj_pos ~pos_params
4449 ~explicit_tparams
env ty1 cid id k in
4452 and obj_get_with_visibility ~
is_method ~
nullsafe ~
valkind ~obj_pos ~pos_params
4453 ?
(explicit_tparams
=[]) env ty1 cid id k =
4454 obj_get_ ~
is_method ~
nullsafe ~
valkind ~obj_pos ~pos_params ~explicit_tparams
env ty1
4455 cid id k (fun ty -> ty)
4457 (* We know that the receiver is a concrete class: not a generic with
4458 * bounds, or a Tunion. *)
4459 and obj_get_concrete_ty ~
is_method ~
valkind ?
(explicit_tparams
=[])
4460 env concrete_ty class_id
(id_pos
, id_str
) k_lhs =
4461 let default () = env, (Reason.Rwitness id_pos
, Typing_utils.tany env), None
in
4462 let mk_ety_env r class_info x e paraml
=
4463 let this_ty = k_lhs (r, (Tclass
(x, e, paraml
))) in
4465 type_expansions
= [];
4467 substs
= Subst.make
(Cls.tparams
class_info) paraml
;
4468 from_class
= Some class_id
;
4469 validate_dty
= None
;
4472 match concrete_ty
with
4473 | (r, Tclass
(x, exact
, paraml
)) ->
4474 begin match Env.get_class
env (snd
x) with
4478 | Some
class_info when not
is_method
4479 && not
(Env.is_strict
env)
4480 && (Cls.name class_info) = SN.Classes.cStdClass
->
4483 | Some
class_info ->
4485 if List.length
paraml = 0
4486 then List.map
(Cls.tparams
class_info)
4487 (fun _ -> Reason.Rwitness id_pos
, Typing_utils.tany env)
4489 let old_member_info = Env.get_member
is_method env class_info id_str
in
4490 let self = Env.get_self_id
env in
4491 let member_info, shadowed
= if Cls.has_ancestor
class_info self
4493 (* We look up the current context to see if there is a field/method with
4494 * private visibility. If there is one, that one takes precedence *)
4495 begin match Env.get_class
env self with
4496 | None
-> old_member_info, false
4497 | Some self_class
->
4498 match Env.get_member
is_method env self_class id_str
with
4499 | Some
{ ce_visibility
= Vprivate
_; _ } as member_info ->
4501 | _ -> old_member_info, false
4503 else old_member_info, false
4506 begin match member_info with
4507 | None
when not
is_method ->
4508 if not
(SN.Members.is_special_xhp_attribute id_str
)
4509 then member_not_found id_pos ~
is_method class_info id_str
r;
4513 begin match Env.get_member
is_method env class_info SN.Members.__call
with
4515 member_not_found id_pos ~
is_method class_info id_str
r;
4518 | Some
{ce_visibility
= vis
; ce_type
= lazy (r, Tfun
ft); _} ->
4519 let mem_pos = Reason.to_pos
r in
4520 TVis.check_obj_access id_pos
env (mem_pos, vis
);
4522 (* the return type of __call can depend on the class params or be this *)
4523 let ety_env = mk_ety_env r class_info x exact
paraml in
4524 let env, ft = Phase.(localize_ft
4525 ~instantiation
:{use_pos
=id_pos
; use_name
=strip_ns id_str
; explicit_tparams
=[]}
4528 let arity_pos = match ft.ft_params
with
4529 | [_; { fp_pos
; fp_kind
= FPnormal
; _ }] -> fp_pos
4530 (* we should really assert here but this is not yet validated *)
4533 (* we change the params of the underlying declaration to act as a
4534 * variadic function ... this transform cannot be done when processing
4535 * the declaration of call because direct calls to $inst->__call are also
4539 ft_arity
= Fellipsis
(0, arity_pos); ft_tparams
= ([], FTKtparams
); ft_params
= []; } in
4541 let member_ty = (r, Tfun
ft) in
4542 env, member_ty, Some
(mem_pos, vis
)
4546 end (* match Env.get_member is_method env class_info SN.Members.__call *)
4548 | Some
({ce_visibility
= vis
; ce_type
= lazy member_
; _ } as member_ce
) ->
4549 let mem_pos = Reason.to_pos
(fst member_
) in
4550 if shadowed
then begin match old_member_info with
4551 | Some
({ce_visibility
= old_vis
; ce_type
= lazy old_member
; _ }) ->
4552 let old_mem_pos = Reason.to_pos
(fst old_member
) in
4553 begin match class_id
with
4554 | CIexpr
(_, This
) when snd
x = self -> ()
4555 | _ -> Errors.ambiguous_object_access
4556 id_pos id_str
mem_pos (TUtils.string_of_visibility old_vis
) old_mem_pos self (snd
x)
4560 TVis.check_obj_access id_pos
env (mem_pos, vis
);
4561 let member_ty = Typing_enum.member_type
env member_ce
in
4562 let ety_env = mk_ety_env r class_info x exact
paraml in
4563 let env, member_ty =
4564 begin match member_ty with
4566 (* We special case function types here to be able to pass explicit type
4569 Phase.(localize_ft ~instantiation
:{ use_name
=strip_ns id_str
; use_pos
=id_pos
; explicit_tparams
}
4573 Phase.localize ~
ety_env env member_ty
4576 if member_ce
.ce_const
&& valkind = `lvalue
then
4577 if not
(env.Env.inside_constructor
&&
4578 (* expensive call behind short circuiting && *)
4579 SubType.is_sub_type
env (Env.get_self
env) concrete_ty
) then
4580 Errors.assigning_to_const id_pos
;
4582 env, member_ty, Some
(mem_pos, vis
)
4583 end (* match member_info *)
4585 end (* match Env.get_class env (snd x) *)
4587 let ty = MakeType.dynamic
(Reason.Rdynamic_prop id_pos
) in
4594 Errors.non_object_member
4595 id_str id_pos
(Typing_print.error env concrete_ty
)
4596 (Reason.to_pos
(fst concrete_ty
));
4599 and widen_class_for_obj_get ~
is_method ~
nullsafe member_name
env ty =
4602 if Option.is_some
nullsafe then env, Some
ty else env, None
4604 | (r2
, Tclass
((_, class_name) as class_id
, _, tyl)) ->
4606 let ty = (r2
, Tclass
(class_id
, Nonexact
, tyl)) in
4608 begin match Env.get_class
env class_name with
4609 | None
-> default ()
4610 | Some
class_info ->
4611 match Env.get_member
is_method env class_info member_name
with
4612 | Some
{ ce_origin
; _ } ->
4613 (* If this member was inherited then we obtain the type from which
4614 * it is inherited as our wider type *)
4615 if ce_origin
= class_name
4618 begin match Cls.get_ancestor
class_info ce_origin
with
4619 | None
-> default ()
4623 type_expansions
= [];
4624 substs
= Subst.make
(Cls.tparams
class_info) tyl;
4627 validate_dty
= None
;
4629 let env, basety
= Phase.localize ~
ety_env env basety
in
4638 (* k_lhs takes the type of the object receiver *)
4639 and obj_get_ ~
is_method ~
nullsafe ~
valkind ~obj_pos
4640 ~
(pos_params
: expr list
option) ?
(explicit_tparams
=[])
4641 env ty1 cid (id_pos
, id_str
as id) k k_lhs =
4644 then SubType.expand_type_and_solve
env ~description_of_expected
:"an object" obj_pos
ty1
4645 else SubType.expand_type_and_narrow
env ~description_of_expected
:"an object"
4646 (widen_class_for_obj_get ~
is_method ~
nullsafe id_str
) obj_pos
ty1 in
4647 let nullable_obj_get ty = match nullsafe with
4649 let env, method_
, x = obj_get_ ~obj_pos ~
is_method ~
nullsafe ~
valkind
4650 ~pos_params ~explicit_tparams
env ty cid id k k_lhs in
4651 let env, method_
= TUtils.non_null
env id_pos method_
in
4652 env, MakeType.nullable
(Reason.Rnullsafe_op p1
) method_
, x
4654 Errors.null_member id_str id_pos
4656 "This is what makes me believe it can be null"
4659 k (env, (fst ety1
, Typing_utils.terr
env), None
) in
4662 let (env, vis
), tyl = List.map_env
(env, None
) tyl
4663 begin fun (env, vis
) ty ->
4665 obj_get_ ~obj_pos ~
is_method ~
nullsafe ~
valkind ~pos_params
4666 ~explicit_tparams
env ty cid id k k_lhs in
4667 (* There is one special case where we need to expose the
4668 * visibility outside of obj_get (checkout inst_meth special
4670 * We keep a witness of the "most restrictive" visibility
4671 * we encountered (position + visibility), to be able to
4672 * special case inst_meth.
4674 let vis = TVis.min_vis_opt
vis vis'
in
4677 let env, ty = Union.union_list
env (fst ety1
) tyl in
4678 let env, method_
= TUtils.in_var
env ty in
4681 | p'
, (Tabstract
(ak
, Some
ty)) ->
4682 let k_lhs'
ty = match ak
with
4683 | AKnewtype
(_, _) -> k_lhs ty
4684 | _ -> k_lhs (p'
, Tabstract
(ak
, Some
ty)) in
4685 obj_get_ ~obj_pos ~
is_method ~
nullsafe ~
valkind ~pos_params
4686 ~explicit_tparams
env ty cid id k k_lhs'
4688 | p'
, (Tabstract
(ak
,_)) ->
4690 TUtils.try_over_concrete_supertypes
env ety1
4692 (* We probably don't want to rewrap new types for the 'this' closure *)
4693 (* TODO AKENN: we shouldn't refine constraints by changing
4694 * the type like this *)
4695 let k_lhs'
ty = match ak
with
4696 | AKnewtype
(_, _) -> k_lhs ty
4697 | _ -> k_lhs (p'
, Tabstract
(ak
, Some
ty)) in
4698 obj_get_concrete_ty ~
is_method ~
valkind ~explicit_tparams
env ty cid id k_lhs'
4700 begin match resl with
4702 Errors.non_object_member
4703 id_str id_pos
(Typing_print.error env ety1
)
4704 (Reason.to_pos
(fst ety1
));
4705 k (env, err_witness env id_pos
, None
)
4707 | ((_env, (_, ty), _vis
) as res
)::rest ->
4708 if List.exists
rest (fun (_, (_,ty'
), _) -> ty'
<> ty)
4711 Errors.ambiguous_member
4712 id_str id_pos
(Typing_print.error env ety1
)
4713 (Reason.to_pos
(fst ety1
));
4714 k (env, err_witness env id_pos
, None
)
4719 | _, Toption
ty -> nullable_obj_get ty
4720 | r, Tprim
Nast.Tnull
->
4722 if TypecheckerOptions.new_inference
(Env.get_tcopt
env)
4726 (* We are trying to access a member through a value of unknown type *)
4728 Errors.unknown_object_member id_str id_pos
(Reason.to_string
"It is unknown" r);
4729 k (env, (r, Typing_utils.terr
env), None
)
4732 k (obj_get_concrete_ty ~
is_method ~
valkind ~explicit_tparams
env ety1
cid id k_lhs)
4734 and class_id_for_new ~exact
p env cid tal
=
4735 let env, te, cid_ty
= static_class_id ~exact ~check_constraints
:false p env tal
cid in
4736 (* Need to deal with union case *)
4737 let rec get_info res
tyl =
4739 | [] -> env, te, res
4743 get_info res
(tyl'
@ tyl)
4745 (* Instantiation on an abstract class (e.g. from classname<T>) is
4746 * via the base type (to check constructor args), but the actual
4747 * type `ty` must be preserved. *)
4748 match TUtils.get_base_type
env ty with
4749 | _, Tclass
(sid
, _, _) ->
4751 let class_ = Env.get_class
env (snd sid
) in
4753 | None
-> get_info res
tyl
4754 | Some
class_info ->
4755 match te, cid_ty
with
4756 (* When computing the classes for a new T() where T is a generic,
4757 * the class must be consistent (final, final constructor, or
4758 * <<__ConsistentConstruct>>) for its constructor to be considered *)
4759 | (_, T.CI
(_, c)), (_, Tabstract
(AKgeneric cg
, _)) when c = cg
->
4760 (* Only have this choosing behavior for new T(), not all generic types
4761 * i.e. new classname<T>, TODO: T41190512 *)
4762 if Tast_utils.valid_newable_class
class_info
4763 then get_info ((sid
, class_info, ty)::res
) tyl
4764 else get_info res
tyl
4766 get_info ((sid
, class_info, ty)::res
) tyl
4768 | _, (Tany
| Terr
| Tnonnull
| Tarraykind
_ | Toption
_
4769 | Tprim
_ | Tvar
_ | Tfun
_ | Tabstract
(_, _) | Ttuple
_
4770 | Tanon
(_, _) | Tunion
_ | Tobject
| Tshape
_ | Tdynamic
) ->
4772 get_info [] [cid_ty
]
4774 (* To be a valid trait declaration, all of its 'require extends' must
4775 * match; since there's no multiple inheritance, it follows that all of
4776 * the 'require extends' must belong to the same inheritance hierarchy
4777 * and one of them should be the child of all the others *)
4778 and trait_most_concrete_req_class trait
env =
4779 Sequence.fold
(Cls.all_ancestor_reqs trait
) ~
f:begin fun acc (_p
, ty) ->
4780 let _r, (_p
, name), _paraml
= TUtils.unwrap_class_type
ty in
4781 let keep = match acc with
4782 | Some
(c, _ty
) -> Cls.has_ancestor
c name
4787 let class_ = Env.get_class
env name in
4790 | Some
c when Cls.kind c = Ast.Cinterface
-> acc
4791 | Some
c when Cls.kind c = Ast.Ctrait
->
4792 (* this is an error case for which the nastCheck spits out
4793 * an error, but does *not* currently remove the offending
4794 * 'require extends' or 'require implements' *)
4796 | Some
c -> Some
(c, ty)
4800 (* If there are no explicit type arguments then generate fresh type variables
4801 * for all of them. Otherwise, check the arity, and use the explicit types. *)
4802 and resolve_type_argument
env hint
=
4803 (* For explicit type arguments we support a wildcard syntax `_` for which
4804 * Hack will generate a fresh type variable *)
4806 | (p, Happly
((_, id), [])) when id = SN.Typehints.wildcard
->
4807 Env.fresh_unresolved_type
env p
4809 Phase.localize_hint_with_self
env hint
4810 and resolve_type_arguments
env p class_id tparaml hintl
=
4811 let length_hintl = List.length hintl
in
4812 let length_tparaml = List.length tparaml
in
4813 if length_hintl <> length_tparaml
4815 List.map_env
env tparaml
begin fun env tparam ->
4816 let env, tvar
= Env.fresh_unresolved_type_reason
env
4817 (Reason.Rtype_variable_generics
(p, snd
tparam.tp_name
, strip_ns
(snd class_id
))) in
4818 Typing_log.log_tparam_instantiation
env p tparam tvar
;
4821 else List.map_env
env hintl resolve_type_argument
4823 (* Do all of the above, and also check any constraints associated with the type parameters.
4825 and resolve_type_arguments_and_check_constraints ~exact ~check_constraints
4826 env p class_id from_class tparaml hintl
=
4827 let env, type_argl
= resolve_type_arguments
env p class_id tparaml hintl
in
4828 let this_ty = (Reason.Rwitness
(fst class_id
), Tclass
(class_id
, exact
, type_argl
)) in
4830 if check_constraints
4831 then let ety_env = {
4832 type_expansions
= [];
4834 substs
= Subst.make tparaml type_argl
;
4835 from_class
= Some from_class
;
4836 validate_dty
= None
;
4838 Phase.check_tparams_constraints ~use_pos
:p ~
ety_env env tparaml
4842 (* When invoking a method the class_id is used to determine what class we
4843 * lookup the method in, but the type of 'this' will be the late bound type.
4847 * public static function get(): this { return new static(); }
4849 * public static function alias(): this { return self::get(); }
4852 * In C::alias, when we invoke self::get(), 'self' is resolved to the class
4853 * in the lexical scope (C), so call C::get. However the method is executed in
4854 * the current context, so static inside C::get will be resolved to the late
4855 * bound type (get_called_class() within C::alias).
4857 * This means when determining the type of this, CIparent and CIself should be
4858 * changed to CIstatic. For the other cases of C::get() or $c::get(), we only
4859 * look at the left hand side of the '::' and use the type type associated
4862 * Thus C::get() will return a type C, while $c::get() will return the same
4865 and this_for_method
env cid default_ty
= match cid with
4866 | CIparent
| CIself
| CIstatic
->
4867 let p = Reason.to_pos
(fst default_ty
) in
4868 let env, _te
, ty = static_class_id ~check_constraints
:false p env [] CIstatic
in
4869 ExprDepTy.make
env CIstatic
ty
4873 and static_class_id ?
(exact
= Nonexact
) ~check_constraints
p env tal
=
4874 let make_result env te ty = env, ((p, ty), te), ty in
4877 (match Env.get_self
env with
4878 | _, Tclass
((_, self), _, _) ->
4879 (match Env.get_class
env self with
4880 | Some trait
when Cls.kind trait
= Ast.Ctrait
->
4881 (match trait_most_concrete_req_class trait
env with
4883 Errors.parent_in_trait
p;
4884 make_result env T.CIparent
(Reason.Rwitness
p, Typing_utils.terr
env)
4885 | Some
(_, parent_ty
) ->
4886 (* inside a trait, parent is SN.Typehints.this, but with the
4887 * type of the most concrete class that the trait has
4888 * "require extend"-ed *)
4889 let r = Reason.Rwitness
p in
4890 let env, parent_ty
= Phase.localize_with_self
env parent_ty
in
4891 make_result env T.CIparent
(r, TUtils.this_of parent_ty
)
4894 let parent = Env.get_parent
env in
4895 let parent_defined = snd
parent <> Typing_utils.tany env in
4896 if not
parent_defined
4897 then Errors.parent_undefined
p;
4898 let r = Reason.Rwitness
p in
4899 let env, parent = Phase.localize_with_self
env parent in
4900 (* parent is still technically the same object. *)
4901 make_result env T.CIparent
(r, TUtils.this_of
(r, snd
parent))
4903 | _, (Terr
| Tany
| Tnonnull
| Tarraykind
_ | Toption
_ | Tprim
_
4904 | Tfun
_ | Ttuple
_ | Tshape
_ | Tvar
_ | Tdynamic
4905 | Tanon
(_, _) | Tunion
_ | Tabstract
(_, _) | Tobject
4907 let parent = Env.get_parent
env in
4908 let parent_defined = snd
parent <> Typing_utils.tany env in
4909 if not
parent_defined
4910 then Errors.parent_undefined
p;
4911 let r = Reason.Rwitness
p in
4912 let env, parent = Phase.localize_with_self
env parent in
4913 (* parent is still technically the same object. *)
4914 make_result env T.CIparent
(r, TUtils.this_of
(r, snd
parent))
4917 let this = (Reason.Rwitness
p, TUtils.this_of
(Env.get_self
env)) in
4918 make_result env T.CIstatic
this
4921 match snd
(Env.get_self
env) with
4922 | Tclass
(c, _, tyl) -> Tclass
(c, exact
, tyl)
4924 make_result env T.CIself
(Reason.Rwitness
p, self)
4925 | CI
((p, id) as c) as e1
->
4926 if Env.is_generic_parameter
env id
4928 let r = Reason.Rhint
p in
4929 let tgeneric = (r, Tabstract
(AKgeneric
id, None
)) in
4930 make_result env (T.CI
c) tgeneric
4932 let class_ = Env.get_class
env (snd
c) in
4935 make_result env (T.CI
c) (Reason.Rwitness
p, Typing_utils.tany env)
4938 resolve_type_arguments_and_check_constraints ~exact ~check_constraints
4939 env p c e1
(Cls.tparams
class_) tal
in
4940 make_result env (T.CI
c) ty
4942 | CIexpr
(p, _ as e) ->
4943 let env, te, ty = expr env e in
4944 let rec resolve_ety env ty =
4945 let env, ty = SubType.expand_type_and_solve ~description_of_expected
:"an object" env p ty in
4946 let env, ty = TUtils.fold_unresolved
env ty in
4947 match TUtils.get_base_type
env ty with
4948 | _, Tabstract
(AKnewtype
(classname
, [the_cls
]), _) when
4949 classname
= SN.Classes.cClassname
-> resolve_ety env the_cls
4950 | _, Tabstract
(AKgeneric
_, _)
4951 | _, Tclass
_ -> env, ty
4953 let env, tyl = List.map_env
env tyl resolve_ety in
4954 env, (r, Tunion
tyl)
4955 | _, Tdynamic
as ty -> env, ty
4956 | _, (Tany
| Tprim Tstring
| Tabstract
(_, None
) | Tobject
)
4957 when not
(Env.is_strict
env) ->
4958 env, (Reason.Rwitness
p, Typing_utils.tany env)
4960 Errors.unknown_type
"an object" p (Reason.to_string
"It is unknown" r);
4961 env, (Reason.Rwitness
p, Typing_utils.terr
env)
4963 | (_, (Terr
| Tany
| Tnonnull
| Tarraykind
_ | Toption
_
4964 | Tprim
_ | Tfun
_ | Ttuple
_
4965 | Tabstract
((AKenum
_ | AKdependent
_ | AKnewtype
_), _)
4966 | Tanon
(_, _) | Tobject
| Tshape
_)) as ty
4968 Errors.expected_class ~suffix
:(", but got "^
Typing_print.error env ty) p;
4969 env, (Reason.Rwitness
p, Typing_utils.terr
env) in
4970 let env, result_ty = resolve_ety env ty in
4971 make_result env (T.CIexpr
te) result_ty
4973 and call_construct
p env class_ params el uel
cid =
4974 let cid = if cid = CIparent
then CIstatic
else cid in
4975 let env, tcid
, cid_ty
= static_class_id ~check_constraints
:false p env [] cid in
4977 type_expansions
= [];
4979 substs
= Subst.make
(Cls.tparams
class_) params;
4980 from_class
= Some
cid;
4981 validate_dty
= None
;
4983 let env = Phase.check_tparams_constraints ~use_pos
:p ~
ety_env env (Cls.tparams
class_) in
4984 if (Cls.is_xhp
class_) then env, tcid
, [], [], (Reason.Rnone
, TUtils.tany env) else
4985 let cstr = Env.get_construct
env class_ in
4986 let mode = Env.get_mode
env in
4987 match (fst
cstr) with
4990 (FileInfo.is_strict
mode || mode = FileInfo.Mpartial
) &&
4991 (Cls.members_fully_known
class_)
4992 then Errors.constructor_no_args
p;
4993 let env, tel, _tyl
= exprs env el
in
4994 env, tcid
, tel, [], (Reason.Rnone
, TUtils.terr
env)
4995 | Some
{ ce_visibility
= vis; ce_type
= lazy m; _ } ->
4996 TVis.check_obj_access
p env (Reason.to_pos
(fst
m), vis);
4997 let env, m = Phase.localize ~
ety_env env m in
4998 let env, tel, tuel
, _ty
= call ~
expected:None
p env m el uel
in
4999 env, tcid
, tel, tuel
, m
5001 and check_arity ?
(did_unpack
=false) pos pos_def
(arity:int) exp_arity
=
5002 let exp_min = (Typing_defs.arity_min exp_arity
) in
5004 then Errors.typing_too_few_args
exp_min arity pos pos_def
;
5005 match exp_arity
with
5006 | Fstandard
(_, exp_max
) ->
5007 let arity = if did_unpack
then arity + 1 else arity in
5009 then Errors.typing_too_many_args exp_max
arity pos pos_def
;
5010 | Fvariadic
_ | Fellipsis
_ -> ()
5012 and check_lambda_arity lambda_pos def_pos lambda_arity expected_arity
=
5013 let expected_min = Typing_defs.arity_min expected_arity
in
5014 match lambda_arity
, expected_arity
with
5015 | Fstandard
(lambda_min
, _), Fstandard
_ ->
5016 if lambda_min
< expected_min
5017 then Errors.typing_too_few_args
expected_min lambda_min lambda_pos def_pos
;
5018 if lambda_min
> expected_min
5019 then Errors.typing_too_many_args
expected_min lambda_min lambda_pos def_pos
5022 and check_deprecated
p { ft_pos
; ft_deprecated
; _ } =
5023 match ft_deprecated
with
5024 | Some
s -> Errors.deprecated_use
p ft_pos
s
5027 (* The variadic capture argument is an array listing the passed
5028 * variable arguments for the purposes of the function body; callsites
5029 * should not unify with it *)
5030 and variadic_param
env ft =
5031 match ft.ft_arity
with
5032 | Fvariadic
(_, param) -> env, Some
param
5033 | Fellipsis
(_, pos) ->
5034 env, Some
(TUtils.default_fun_param ~
pos (Reason.Rvar_param
pos, Tany
))
5035 | Fstandard
_ -> env, None
5037 and param_modes ?
(is_variadic
=false) { fp_pos
; fp_kind
; _ } (pos, e) =
5038 match fp_kind
, e with
5039 | FPnormal
, Unop
(Ast.Uref
, _) ->
5040 Errors.pass_by_ref_annotation_unexpected
pos fp_pos is_variadic
5041 | FPnormal
, Callconv
_ ->
5042 Errors.inout_annotation_unexpected
pos fp_pos is_variadic
5044 | FPref
, Unop
(Ast.Uref
, _) -> ()
5045 | FPref
, Callconv
(kind, _) ->
5047 (* HHVM supports pass-by-ref for arguments annotated as 'inout'. *)
5051 Errors.pass_by_ref_annotation_missing
pos fp_pos
5052 (* HHVM also allows '&' on arguments to inout parameters via interop layer. *)
5053 | FPinout
, Unop
(Ast.Uref
, _)
5054 | FPinout
, Callconv
(Ast.Pinout
, _) -> ()
5056 Errors.inout_annotation_missing
pos fp_pos
5058 and inout_write_back
env { fp_type
; _ } (_, e) =
5060 | Callconv
(Ast.Pinout
, e1
) ->
5061 (* Translate the write-back semantics of inout parameters.
5063 * This matters because we want to:
5064 * (1) make sure we can write to the original argument
5065 * (modifiable lvalue check)
5066 * (2) allow for growing of locals / Tunions (type side effect)
5067 * but otherwise unify the argument type with the parameter hint
5069 let env, _te
, _ty
= assign_
(fst e1
) Reason.URparam_inout
env e1 fp_type
in
5073 and call ~
expected ?method_call_info
pos env fty el uel
=
5074 let env, tel, tuel
, ty =
5075 call_ ~
expected ~method_call_info
pos env fty el uel
in
5076 let new_inference = TypecheckerOptions.new_inference (Env.get_tcopt
env) in
5077 let env = if not
new_inference then Env.check_todo
env else env in
5080 and call_ ~
expected ~method_call_info
pos env fty el uel
=
5081 let make_unpacked_traversable_ty pos ty = MakeType.traversable
(Reason.Runpack_param
pos) ty in
5082 let resl = TUtils.try_over_concrete_supertypes
env fty begin fun env fty ->
5083 let env, efty
= SubType.expand_type_and_solve
5084 ~description_of_expected
:"a function value" env pos fty in
5086 | _, (Terr
| Tany
| Tunion
[] | Tdynamic
) ->
5087 let el = el @ uel
in
5088 let env, tel = List.map_env
env el begin fun env elt
->
5090 expr ~
expected:(pos, Reason.URparam
, (Reason.Rnone
, Typing_utils.tany env))
5091 ~is_func_arg
:true env elt
5095 | _, Callconv
(Ast.Pinout
, e1
) ->
5096 let env, _te
, _ty
= assign_
(fst e1
) Reason.URparam_inout
env e1 efty
in
5098 | _, Unop
(Ast.Uref
, e1
) ->
5099 let env, _te
, _ty
= assign_
(fst e1
) Reason.URparam
env e1 efty
in
5104 let env = call_untyped_unpack
env uel
in
5106 if snd efty
= Tdynamic
5107 then MakeType.dynamic
(Reason.Rdynamic_call
pos)
5108 else (Reason.Rnone
, Typing_utils.tany env)
5112 call ~
expected pos env ty el uel
5114 let env, retl
= List.map_env
env tyl begin fun env ty ->
5115 let env, _, _, ty = call ~
expected pos env ty el uel
in env, ty
5117 let env, ty = TUtils.in_var
env (r, Tunion retl
) in
5120 (* Typing of format string functions. It is dependent on the arguments (el)
5121 * so it cannot be done earlier.
5123 let pos_def = Reason.to_pos r2
in
5124 let env, ft = Typing_exts.retype_magic_func
env ft el in
5125 check_deprecated
pos ft;
5126 let env, var_param = variadic_param
env ft in
5128 (* Force subtype with expected result *)
5129 let env = check_expected_ty
"Call result" env ft.ft_ret
expected in
5130 let env = Env.set_tyvar_variance
env ft.ft_ret
in
5131 let is_lambda e = match snd
e with Efun
_ | Lfun
_ -> true | _ -> false in
5133 let get_next_param_info paraml =
5136 false, Some
param, paraml
5138 true, var_param, paraml in
5140 let check_arg env (pos, _ as e) opt_param ~is_variadic
=
5141 match opt_param
with
5144 expr ~is_func_arg
:true ~accept_using_var
:param.fp_accept_disposable
5145 ~
expected:(pos, Reason.URparam
, param.fp_type
) env e in
5146 let env = call_param
env param (e, ty) ~is_variadic
in
5149 let env, te, ty = expr ~
expected:(pos, Reason.URparam
,
5150 (Reason.Rnone
, Typing_utils.tany env)) ~is_func_arg
:true env e in
5151 env, Some
(te, ty) in
5153 let set_tyvar_variance_from_lambda_param env opt_param
=
5154 match opt_param
with
5156 let rec set_params_variance env ty =
5157 let env, ty = Env.expand_type
env ty in
5159 | _, Tunion
[ty] -> set_params_variance env ty
5160 | _, Toption
ty -> set_params_variance env ty
5161 | _, Tfun
{ ft_params
; ft_ret
; _ } ->
5162 let env = List.fold ~init
:env ~
f:(fun env param ->
5163 Env.set_tyvar_variance
env param.fp_type
) ft_params
in
5164 Env.set_tyvar_variance
env ft_ret ~flip
:true
5166 set_params_variance env param.fp_type
5170 (* Given an expected function type ft, check types for the non-unpacked
5171 * arguments. Don't check lambda expressions if check_lambdas=false *)
5172 let rec check_args check_lambdas
env el paraml =
5174 (* We've got an argument *)
5175 | (e, opt_result
) :: el ->
5176 (* Pick up next parameter type info *)
5177 let is_variadic, opt_param
, paraml = get_next_param_info paraml in
5178 let env, one_result
= match check_lambdas
, is_lambda e with
5181 check_arg env e opt_param ~
is_variadic
5183 let env = set_tyvar_variance_from_lambda_param env opt_param
in
5187 let env, rl
, paraml = check_args check_lambdas
env el paraml in
5188 env, (e, one_result
)::rl
, paraml
5193 (* First check the non-lambda arguments. For generic functions, this
5194 * is likely to resolve type variables to concrete types *)
5195 let rl = List.map
el (fun e -> (e, None
)) in
5196 let env, rl, _ = check_args false env rl ft.ft_params
in
5197 (* Now check the lambda arguments, hopefully with type variables resolved *)
5198 let env, rl, paraml = check_args true env rl ft.ft_params
in
5199 (* We expect to see results for all arguments after this second pass *)
5203 | None
-> failwith
"missing parameter in check_args" in
5205 let l = List.map
rl (fun (_, opt
) -> get_param opt
) in
5207 TR.check_call env method_call_info
pos r2
ft tys
;
5208 let env, tuel
, arity, did_unpack
=
5210 | [] -> env, [], List.length
el, false
5212 (* Enforces that e is unpackable. If e is a tuple, check types against
5213 * parameter types *)
5214 let env, te, ty = expr env e in
5215 let env, ety
= SubType.expand_type_and_solve
5216 ~description_of_expected
:"an unpackable value" env (fst
e) ty in
5219 let rec check_elements env tyl paraml =
5223 let is_variadic, opt_param
, paraml = get_next_param_info paraml in
5224 match opt_param
with
5227 let env = call_param
env param (e, ty) ~
is_variadic in
5228 check_elements env tyl paraml in
5229 let env = check_elements env tyl paraml in
5230 env, [te], List.length
el + List.length
tyl, false
5232 let param_tyl = List.map
paraml (fun param -> param.fp_type
) in
5233 let add_variadic_param_ty param_tyl =
5234 match var_param with
5235 | Some
param -> param.fp_type
:: param_tyl
5236 | None
-> param_tyl in
5237 let param_tyl = add_variadic_param_ty param_tyl in
5239 let env = List.fold_right
param_tyl ~init
:env
5240 ~
f:(fun param_ty env ->
5241 let traversable_ty = make_unpacked_traversable_ty pos param_ty in
5242 Type.sub_type
pos Reason.URparam
env ety
traversable_ty)
5244 env, [te], List.length
el, true
5246 (* If we unpacked an array, we don't check arity exactly. Since each
5247 * unpacked array consumes 1 or many parameters, it is nonsensical to say
5248 * that not enough args were passed in (so we don't do the min check).
5250 let () = check_arity ~did_unpack
pos pos_def arity ft.ft_arity
in
5251 (* Variadic params cannot be inout so we can stop early *)
5252 let env = wfold_left2 inout_write_back
env ft.ft_params
el in
5254 TR.get_adjusted_return_type
env method_call_info
ft.ft_ret
in
5255 env, tel, tuel
, ret_ty
5256 | r2
, Tanon
(arity, id) ->
5257 let env, tel, tyl = exprs ~is_func_arg
:true env el in
5258 let expr_for_unpacked_expr_list env = function
5259 | [] -> env, [], None
, Pos.none
5260 | (pos, _) as e :: _ ->
5261 let env, te, ety
= expr env e in
5262 env, [te], Some ety
, pos
5264 let append_tuple_types tyl = function
5265 | Some
(_, Ttuple tuple_tyl
) -> tyl @ tuple_tyl
5268 let determine_arity env min_arity
pos = function
5270 | Some
(_, Ttuple
_) ->
5271 env, Fstandard
(min_arity
, min_arity
)
5273 (* We need to figure out the underlying type of the unpacked expr type.
5275 * For example, assume the call is:
5277 * where $y is a variadic or collection of strings.
5279 * $y may have the type Tarraykind or Traversable, however we need to
5280 * pass Fvariadic a param of type string.
5282 * Assuming $y has type Tarraykind, in order to get the underlying type
5283 * we create a fresh_type(), wrap it in a Traversable and make that
5284 * Traversable a super type of the expr type (Tarraykind). This way
5285 * we can infer the underlying type and create the correct param for
5288 let env, ty = Env.fresh_type
env pos in
5289 let traversable_ty = make_unpacked_traversable_ty pos ty in
5290 let env = Type.sub_type
pos Reason.URparam
env ety
traversable_ty in
5296 fp_accept_disposable
= false;
5297 fp_mutability
= None
;
5298 fp_rx_annotation
= None
;
5301 env, Fvariadic
(min_arity
, param)
5303 let env, tuel
, uety_opt
, uepos
= expr_for_unpacked_expr_list env uel
in
5304 let tyl = append_tuple_types tyl uety_opt
in
5305 let env, call_arity
= determine_arity env (List.length
tyl) uepos uety_opt
in
5306 let anon = Env.get_anonymous
env id in
5307 let fpos = Reason.to_pos r2
in
5310 Errors.anonymous_recursive_call
pos;
5311 env, tel, tuel
, err_witness env pos
5312 | Some
(reactivity, is_coroutine, ftys, _, anon) ->
5313 let () = check_arity
pos fpos (Typing_defs.arity_min call_arity
) arity in
5314 let tyl = List.map
tyl TUtils.default_fun_param
in
5315 let env, _, ty = anon ~
el env tyl call_arity
in
5317 (Reason.Rlambda_use
pos, Tfun
{
5319 ft_deprecated
= None
;
5320 ft_abstract
= false;
5321 ft_is_coroutine
= is_coroutine;
5323 ft_tparams
= ([], FTKtparams
);
5324 ft_where_constraints
= [];
5327 ft_reactive
= reactivity;
5328 ft_return_disposable
= false;
5329 ft_mutability
= None
;
5330 ft_returns_mutable
= false;
5331 ft_decl_errors
= None
;
5332 ft_returns_void_to_rx
= false;
5334 ftys := TUtils.add_function_type
env fty !ftys;
5336 | _, Tarraykind
_ when not
(Env.is_strict
env) ->
5337 (* Relaxing call_user_func to work with an array in partial mode *)
5338 let env = call_untyped_unpack
env uel
in
5339 env, [], [], (Reason.Rnone
, Typing_utils.tany env)
5341 bad_call
env pos ty;
5342 let env = call_untyped_unpack
env uel
in
5343 env, [], [], err_witness env pos
5348 bad_call
env pos fty;
5349 let env = call_untyped_unpack
env uel
in
5350 env, [], [], err_witness env pos
5352 and call_param
env param ((pos, _ as e), arg_ty
) ~
is_variadic =
5353 (match param.fp_name
with
5355 | Some
name -> Typing_suggest.save_param
name env param.fp_type arg_ty
5357 param_modes ~
is_variadic param e;
5359 (* When checking params the type 'x' may be expression dependent. Since
5360 * we store the expression id in the local env for Lvar, we want to apply
5363 let env, dep_ty
= match snd
e with
5364 | Lvar
_ -> ExprDepTy.make
env (CIexpr
e) arg_ty
5365 | _ -> env, arg_ty
in
5366 Type.coerce_type
pos Reason.URparam
env dep_ty
param.fp_type
5368 and call_untyped_unpack
env uel
= match uel
with
5369 (* In the event that we don't have a known function call type, we can still
5370 * verify that any unpacked arguments (`...$args`) are something that can
5371 * be actually unpacked. *)
5374 let env, _, ety
= expr env e in
5376 | _, Ttuple
_ -> env (* tuples are always fine *)
5379 let env, ty = Env.fresh_type
env pos in
5380 let unpack_r = Reason.Runpack_param
pos in
5381 let unpack_ty = MakeType.traversable
unpack_r ty in
5382 Type.coerce_type
pos Reason.URparam
env ety
unpack_ty
5386 and bad_call
env p ty =
5387 Errors.bad_call
p (Typing_print.error env ty)
5389 (* to be used to throw typing error if failing to satisfy subtype relation *)
5390 and enforce_sub_ty
env p ty1 ty2 =
5391 let env = Type.sub_type
p Reason.URnone
env ty1 ty2 in
5392 Env.expand_type
env ty1
5394 (* throws typing error if neither t <: ty nor t <: dynamic, and adds appropriate
5395 * constraint to env otherwise *)
5396 and check_type
ty p env t
=
5397 let is_ty = SubType.is_sub_type
env t
ty in
5398 let is_dynamic = SubType.is_sub_type
env t
(MakeType.dynamic
(fst
ty)) in
5399 match is_ty, is_dynamic with
5400 | false, true -> enforce_sub_ty
env p t
(MakeType.dynamic
(fst
ty))
5401 | _ -> enforce_sub_ty
env p t
ty
5403 (* does check_type with num and then gives back normalized type and env *)
5404 and check_num
env p t
r =
5405 let env2, t2
= check_type
(MakeType.num
r) p env t
in
5407 env2, if SubType.is_sub_type
env2 t
(MakeType.int r2)
5408 then MakeType.int r2
5409 else if SubType.is_sub_type
env2 t
(MakeType.float r2)
5410 then MakeType.float r2
5411 else if SubType.is_sub_type
env2 t
(MakeType.num
r2)
5412 then MakeType.num
r2
5413 else MakeType.dynamic
r2
5415 (* does check_type with int and then gives back normalized type and env *)
5416 and check_int
env p t
r =
5417 let env2, t2
= check_type
(MakeType.int r) p env t
in
5419 env2, if SubType.is_sub_type
env2 t
(MakeType.int r2)
5420 then MakeType.int r2
5421 else MakeType.dynamic
r2
5423 and unop ~is_func_arg ~
array_ref_ctx p env uop
te ty =
5424 let make_result env te result_ty =
5425 env, T.make_typed_expr
p result_ty (T.Unop
(uop
, te)), result_ty in
5426 let is_any = TUtils.is_any env in
5430 then make_result env te ty
5431 else (* args isn't any or a variant thereof so can actually do stuff *)
5432 (* !$x (logical not) works with any type, so we just return Tbool *)
5433 make_result env te (MakeType.bool (Reason.Rlogic_ret
p))
5436 then make_result env te ty
5437 else (* args isn't any or a variant thereof so can actually do stuff *)
5438 let env, t
= check_int
env p ty (Reason.Rbitwise
p) in
5441 | Tdynamic
-> make_result env te (MakeType.dynamic
(Reason.Rbitwise_dynamic
p))
5442 | _ -> make_result env te (MakeType.int (Reason.Rbitwise_ret
p))
5448 (* increment and decrement operators modify the value,
5449 * check for immutability violation here *)
5452 | _, T.ImmutableVar
(p, x) ->
5453 Errors.let_var_immutability_violation
p (Local_id.get_name
x);
5454 expr_error env p (Reason.Rwitness
p)
5457 then make_result env te ty
5458 else (* args isn't any or a variant thereof so can actually do stuff *)
5459 let env, t
= check_num
env p ty (Reason.Rarith
p) in
5461 if Env.env_local_reactive
env then
5462 Typing_mutability.handle_assignment_mutability
env te (Some
(snd
te))
5467 make_result env te (MakeType.float (Reason.Rarith_ret_float
(p, fst t
, Reason.Aonly
)))
5469 make_result env te (MakeType.num
(Reason.Rarith_ret_num
(p, fst t
, Reason.Aonly
)))
5470 | Tprim Tint
-> make_result env te (MakeType.int (Reason.Rarith_ret_int
p))
5471 | Tdynamic
-> make_result env te (MakeType.dynamic
(Reason.Rincdec_dynamic
p))
5472 | _ -> make_result env te (MakeType.num
(Reason.Rarith_ret
p))
5477 then make_result env te ty
5478 else (* args isn't any or a variant thereof so can actually do stuff *)
5479 let env, t
= check_num
env p ty (Reason.Rarith
p) in
5483 make_result env te (MakeType.float (Reason.Rarith_ret_float
(p, fst t
, Reason.Aonly
)))
5485 make_result env te (MakeType.num
(Reason.Rarith_ret_num
(p, fst t
, Reason.Aonly
)))
5486 | Tprim Tint
-> make_result env te (MakeType.int (Reason.Rarith_ret_int
p))
5487 | _ -> make_result env te (MakeType.num
(Reason.Rarith_ret
p))
5490 if Env.env_local_reactive
env
5491 && not
(TypecheckerOptions.unsafe_rx
(Env.get_tcopt
env))
5492 then Errors.reference_in_rx
p;
5494 if array_ref_ctx <> NoArray
5496 match array_ref_ctx with
5497 | ElementAccess
-> Errors.binding_ref_to_array
p (* &$x[0]; *)
5498 | ElementAssignment
-> Errors.binding_ref_in_array
p (* $x[0] = &y; *)
5500 else if is_func_arg
(* Normal function calls, excludes e.g. isset(&x); *)
5505 Errors.passing_array_cell_by_ref
p
5507 (* foo(&x); // permitted *)
5509 else Errors.reference_expr
p; (* &$x; *)
5511 (* any check omitted because would return the same anyway *)
5512 make_result env te ty
5514 (* Silencing does not change the type *)
5515 (* any check omitted because would return the same anyway *)
5516 make_result env te ty
5518 and binop
p env bop p1
te1 ty1 p2
te2 ty2 =
5519 let make_result env te1 te2 ty =
5520 env, T.make_typed_expr
p ty (T.Binop
(bop
, te1, te2)), ty in
5521 let is_any = TUtils.is_any env in
5522 let contains_any = (is_any ty1) || (is_any ty2) in
5524 | Ast.Plus
when not
contains_any ->
5525 let env, t1
= check_num
env p ty1 (Reason.Rarith p1
) in
5526 let env, t2
= check_num
env p ty2 (Reason.Rarith p2
) in
5527 (* postcondition: t1 and t2 are dynamic or subtypes of num and
5528 annotated as such, or we are e.g. HH_FIXMEing *)
5530 match snd t1
, snd t2
with
5531 | Tprim Tint
, Tprim Tint
-> make_result env te1 te2 (MakeType.int (Reason.Rarith_ret_int
p))
5532 | Tprim Tfloat
, _ ->
5533 make_result env te1 te2 (MakeType.float (Reason.Rarith_ret_float
(p, fst t1
, Reason.Afirst
)))
5534 | _, Tprim Tfloat
->
5535 make_result env te1 te2 (MakeType.float (Reason.Rarith_ret_float
(p, fst t2
, Reason.Asecond
)))
5537 make_result env te1 te2 (MakeType.num
(Reason.Rarith_ret_num
(p, fst t1
, Reason.Afirst
)))
5539 make_result env te1 te2 (MakeType.num
(Reason.Rarith_ret_num
(p, fst t2
, Reason.Asecond
)))
5540 | Tdynamic
, Tdynamic
-> make_result env te1 te2 (MakeType.dynamic
(Reason.Rsum_dynamic
p))
5541 | _ -> make_result env te1 te2 (MakeType.num
(Reason.Rarith_ret
p))
5543 | Ast.Minus
| Ast.Star
when not
contains_any ->
5544 let env, t1
= check_num
env p ty1 (Reason.Rarith p1
) in
5545 let env, t2
= check_num
env p ty2 (Reason.Rarith p2
) in
5546 (* postcondition: t1 and t2 are dynamic or subtypes of num and
5547 annotated as such, or we are e.g. HH_FIXMEing *)
5549 match snd t1
, snd t2
with
5550 | Tprim Tint
, Tprim Tint
-> make_result env te1 te2 (MakeType.int (Reason.Rarith_ret_int
p))
5551 | Tprim Tfloat
, _ ->
5552 make_result env te1 te2 (MakeType.float (Reason.Rarith_ret_float
(p, fst t1
, Reason.Afirst
)))
5553 | _, Tprim Tfloat
->
5554 make_result env te1 te2 (MakeType.float (Reason.Rarith_ret_float
(p, fst t2
, Reason.Asecond
)))
5556 make_result env te1 te2 (MakeType.num
(Reason.Rarith_ret_num
(p, fst t1
, Reason.Afirst
)))
5558 make_result env te1 te2 (MakeType.num
(Reason.Rarith_ret_num
(p, fst t2
, Reason.Asecond
)))
5559 | _ -> make_result env te1 te2 (MakeType.num
(Reason.Rarith_ret
p))
5561 | Ast.Slash
| Ast.Starstar
when not
contains_any ->
5562 let env, t1
= check_num
env p ty1 (Reason.Rarith p1
) in
5563 let env, t2
= check_num
env p ty2 (Reason.Rarith p2
) in
5564 (* postcondition: t1 and t2 are dynamic or subtypes of num and
5565 annotated as such, or we are e.g. HH_FIXMEing *)
5566 let r = match bop
with
5567 | Ast.Slash
-> Reason.Rret_div
p
5568 | _ -> Reason.Rarith_ret
p in
5570 match snd t1
, snd t2
with
5571 | Tprim Tfloat
, _ ->
5572 make_result env te1 te2 (MakeType.float (Reason.Rarith_ret_float
(p, fst t1
, Reason.Afirst
)))
5573 | _, Tprim Tfloat
->
5574 make_result env te1 te2 (MakeType.float (Reason.Rarith_ret_float
(p, fst t2
, Reason.Asecond
)))
5575 | _ -> make_result env te1 te2 (MakeType.num
r)
5577 | Ast.Percent
| Ast.Ltlt
| Ast.Gtgt
when not
contains_any ->
5578 let env, _ = check_int
env p ty1 (Reason.Rarith p1
) in
5579 let env, _ = check_int
env p ty2 (Reason.Rarith p2
) in
5580 (* postcondition: t1 and t2 are dynamic or int and
5581 annotated as such, or we are e.g. HH_FIXMEing *)
5582 let r = match bop
with
5583 | Ast.Percent
-> Reason.Rarith_ret_int
p
5584 | _ -> Reason.Rbitwise_ret
p in
5585 make_result env te1 te2 (MakeType.int r)
5586 | Ast.Xor
| Ast.Amp
| Ast.Bar
when not
contains_any ->
5587 let env, t1
= check_int
env p ty1 (Reason.Rbitwise p1
) in
5588 let env, t2
= check_int
env p ty2 (Reason.Rbitwise p2
) in
5589 (* postcondition: t1 and t2 are dynamic or int and
5590 annotated as such, or we are e.g. HH_FIXMEing *)
5592 match snd t1
, snd t2
with
5593 | Tdynamic
, Tdynamic
-> make_result env te1 te2 (MakeType.dynamic
(Reason.Rbitwise_dynamic
p))
5594 | _ -> make_result env te1 te2 (MakeType.int (Reason.Rbitwise_ret
p))
5596 | Ast.Eqeq
| Ast.Diff
| Ast.Eqeqeq
| Ast.Diff2
->
5597 make_result env te1 te2 (MakeType.bool (Reason.Rcomp
p))
5598 | Ast.Lt
| Ast.Lte
| Ast.Gt
| Ast.Gte
| Ast.Cmp
->
5599 let ty_num = MakeType.num
(Reason.Rcomp
p) in
5600 let ty_int = MakeType.int (Reason.Rcomp
p) in
5601 let ty_bool = MakeType.bool (Reason.Rcomp
p) in
5602 let ty_result = match bop
with Ast.Cmp
-> ty_int | _ -> ty_bool in
5603 let ty_string = MakeType.string (Reason.Rcomp
p) in
5604 let ty_datetime = MakeType.datetime
(Reason.Rcomp
p) in
5605 let ty_datetimeimmutable = MakeType.datetime_immutable
(Reason.Rcomp
p) in
5606 let ty_dynamic = MakeType.dynamic
(Reason.Rcomp
p) in
5608 (List.exists
tyl ~
f:(SubType.is_sub_type
env ty1))
5609 && (List.exists
tyl ~
f:(SubType.is_sub_type
env ty2)) in
5611 * Comparison here is allowed when both args are num, both string, or both
5612 * DateTime | DateTimeImmutable. Alternatively, either or both args can be
5613 * dynamic. We use both_sub to check that both arguments subtype a type.
5615 * This actually does not properly handle union types. For instance,
5616 * DateTime | DateTimeImmutable is neither a subtype of DateTime nor
5617 * DateTimeImmutable, but it will be the type of an element coming out
5618 * of a vector containing both. Further, dynamic could be comparable to
5619 * num | string | DateTime | DateTimeImmutable | dynamic. Better union
5620 * handling would be an improvement.
5622 if not
contains_any &&
5623 not
(both_sub [ty_num; ty_dynamic]
5624 || both_sub [ty_string; ty_dynamic]
5625 || both_sub [ty_datetime; ty_datetimeimmutable; ty_dynamic])
5627 let ty1 = Typing_expand.fully_expand
env ty1 in
5628 let ty2 = Typing_expand.fully_expand
env ty2 in
5629 let tys1 = Typing_print.error env ty1 in
5630 let tys2 = Typing_print.error env ty2 in
5631 Errors.comparison_invalid_types
p
5632 (Reason.to_string
("This is " ^
tys1) (fst
ty1))
5633 (Reason.to_string
("This is " ^
tys2) (fst
ty2))
5635 make_result env te1 te2 ty_result
5637 (* A bit weird, this one:
5638 * function(Stringish | string, Stringish | string) : string)
5640 let env = SubType.sub_string p1
env ty1 in
5641 let env = SubType.sub_string p2
env ty2 in
5642 make_result env te1 te2 (MakeType.string (Reason.Rconcat_ret
p))
5643 | Ast.Barbar
| Ast.Ampamp
| Ast.LogXor
->
5644 make_result env te1 te2 (MakeType.bool (Reason.Rlogic_ret
p))
5645 | Ast.QuestionQuestion
5646 | Ast.Eq
_ when not
contains_any ->
5649 assert contains_any;
5651 then make_result env te1 te2 ty1
5652 else make_result env te1 te2 ty2
5654 and make_a_local_of
env e =
5656 | p, Class_get
((_, cname
), CGstring
(_, member_name
)) ->
5657 let env, local = Env.FakeMembers.make_static
p env cname member_name
in
5658 env, Some
(p, local)
5659 | p, Obj_get
((_, This
| _, Lvar
_ as obj
), (_, Id
(_, member_name
)), _) ->
5660 let env, local = Env.FakeMembers.make
p env obj member_name
in
5661 env, Some
(p, local)
5664 | _, Dollardollar
x -> env, Some
x
5667 (* This function captures the common bits of logic behind refinement
5668 * of the type of a local variable or a class member variable as a
5669 * result of a dynamic check (e.g., nullity check, simple type check
5670 * using functions like is_int, is_string, is_array etc.). The
5671 * argument refine is a function that takes the type of the variable
5672 * and returns a refined type (making necessary changes to the
5673 * environment, which is threaded through).
5675 and refine_lvalue_type
env ((_p
, ty), _ as te) ~refine
=
5676 let env, ty = refine
env ty in
5677 let e = T.to_nast_expr
te in
5678 let env, localopt
= make_a_local_of
env e in
5679 (* TODO TAST: generate an assignment to the fake local in the TAST *)
5682 set_local
env local ty
5685 and condition_nullity ~nonnull
(env: Env.env) te =
5687 (* assignment: both the rhs and lhs of the '=' must be made null/non-null *)
5688 | _, T.Binop
(Ast.Eq None
, var
, te) ->
5689 let env = condition_nullity ~nonnull
env te in
5690 condition_nullity ~nonnull
env var
5691 (* case where `Shapes::idx(...)` must be made null/non-null *)
5694 (_, T.Class_const
((_, T.CI
(_, shapes
)), (_, idx
))),
5698 when shapes
= SN.Shapes.cShapes
&& idx
= SN.Shapes.idx
->
5699 let field = T.to_nast_expr
field in
5700 let refine env shape_ty
= if nonnull
5701 then Typing_shapes.shapes_idx_not_null
env shape_ty
field
5702 else env, shape_ty
in
5703 refine_lvalue_type
env shape ~
refine
5705 let refine env ty = if nonnull
5706 then TUtils.non_null
env p ty
5708 refine_lvalue_type
env te ~
refine
5710 and condition_isset
env = function
5711 | _, T.Array_get
(x, _) -> condition_isset
env x
5712 | v
-> condition_nullity ~nonnull
:true env v
5715 * Build an environment for the true or false branch of
5716 * conditional statements.
5718 and condition ?lhs_of_null_coalesce
env tparamet
5719 ((p, ty as pty
), e as te: Tast.expr) =
5720 let condition = condition ?lhs_of_null_coalesce
in
5721 let enable_instanceof_refinement =
5722 not
(TypecheckerOptions.disable_instanceof_refinement
(Env.get_tcopt
env))
5726 | T.Expr_list
[] when not tparamet
->
5727 LEnv.drop_cont
env C.Next
5728 | T.False
when tparamet
->
5729 LEnv.drop_cont
env C.Next
5730 | T.Expr_list
[] -> env
5731 | T.Expr_list
[x] ->
5732 condition env tparamet
x
5733 | T.Expr_list
(_::xs
) ->
5734 condition env tparamet
(pty
, T.Expr_list xs
)
5735 | T.Call
(Cnormal
, (_, T.Id
(_, func
)), _, [param], [])
5736 when SN.PseudoFunctions.isset
= func
&& tparamet
&&
5737 not
(Env.is_strict
env) ->
5738 condition_isset
env param
5739 | T.Call
(Cnormal
, (_, T.Id
(_, func
)), _, [te], [])
5740 when SN.StdlibFunctions.is_null
= func
->
5741 condition_nullity ~nonnull
:(not tparamet
) env te
5742 | T.Binop
((Ast.Eqeq
| Ast.Eqeqeq
), (_, T.Null
), e)
5743 | T.Binop
((Ast.Eqeq
| Ast.Eqeqeq
), e, (_, T.Null
)) ->
5744 condition_nullity ~nonnull
:(not tparamet
) env e
5745 | (T.Lvar
_ | T.Obj_get
_ | T.Class_get
_ | T.Binop
(Ast.Eq None
, _, _)) ->
5746 let env, ety
= Env.expand_type
env ty in
5748 | _, Tarraykind
(AKany
| AKempty
)
5749 | _, Tprim Tbool
-> env
5750 | _, (Terr
| Tany
| Tnonnull
| Tarraykind
_ | Toption
_ | Tdynamic
5751 | Tprim
_ | Tvar
_ | Tfun
_ | Tabstract
_ | Tclass
_
5752 | Ttuple
_ | Tanon
(_, _) | Tunion
_ | Tobject
| Tshape
_
5754 condition_nullity ~nonnull
:tparamet
env te)
5755 | T.Binop
((Ast.Diff
| Ast.Diff2
as op
), e1
, e2
) ->
5756 let op = if op = Ast.Diff
then Ast.Eqeq
else Ast.Eqeqeq
in
5757 condition env (not tparamet
) (pty
, T.Binop
(op, e1
, e2
))
5758 | T.Id
(_, s) when s = SN.Rx.is_enabled
->
5759 (* when Rx\IS_ENABLED is false - switch env to non-reactive *)
5761 then Env.set_env_reactive
env Nonreactive
5763 | T.Binop
((Ast.Ampamp
| Ast.Barbar
) as bop
, e1
, e2
)
5764 when tparamet
= (bop
= Ast.Ampamp
) ->
5765 let env = condition env tparamet e1
in
5766 (* This is necessary in case there is an assignment in e2
5767 * We essentially redo what has been undone in the
5768 * `Binop (AMpamp|BArbar)` case of `expr` *)
5769 let env, _, _ = expr env (Tast.to_nast_expr e2
) in
5770 let env = condition env tparamet e2
in
5772 | T.Call
(Cnormal
, ((p, _), T.Id
(_, f)), _, [lv
], [])
5773 when tparamet
&& f = SN.StdlibFunctions.is_array
->
5774 is_array
env `PHPArray
p f lv
5777 (_, T.Class_const
((_, T.CI
(_, class_name)), (_, method_name
))),
5781 when tparamet
&& class_name = SN.Shapes.cShapes
&& method_name
= SN.Shapes.keyExists
->
5782 key_exists
env p shape
field
5783 | T.Unop
(Ast.Unot
, e) ->
5784 condition env (not tparamet
) e
5785 | T.InstanceOf
(ivar
, (_, cid))
5786 when enable_instanceof_refinement && tparamet
&& is_instance_var
(T.to_nast_expr ivar
) ->
5787 let ivar = T.to_nast_expr
ivar in
5788 (* Check the expession and determine its static type *)
5789 let env, _te
, x_ty
= raw_expr env ivar in
5791 (* What is the local variable bound to the expression? *)
5792 let env, ((ivar_pos
, _) as ivar) = get_instance_var
env ivar in
5794 (* The position p here is not really correct... it's the position
5795 * of the instanceof expression, not the class id. But we don't store
5796 * position data for the latter. *)
5797 let env, _te
, obj_ty = static_class_id ~check_constraints
:false p env []
5798 (T.to_nast_class_id_
cid) in
5800 let safe_instanceof_enabled =
5801 TypecheckerOptions.experimental_feature_enabled
5802 (Env.get_tcopt
env) TypecheckerOptions.experimental_instanceof
in
5803 let rec resolve_obj env obj_ty =
5804 (* Expand so that we don't modify x. Also, solve under new-inference
5805 * if it's a type variable *)
5806 let env, obj_ty = SubType.expand_type_and_solve
5807 ~description_of_expected
:"a value" env ivar_pos
obj_ty in
5809 (* If it's a generic that's expression dependent, we need to
5810 look at all of its upper bounds and create an unresolved type to
5811 represent all of the possible types.
5813 | r, Tabstract
(AKgeneric
s, _) when AbstractKind.is_generic_dep_ty
s ->
5814 let upper_bounds = TySet.elements
(Env.get_upper_bounds
env s) in
5815 let env, tyl = List.map_env
env upper_bounds resolve_obj in
5816 env, (r, Tunion
tyl)
5817 | _, Tabstract
(AKgeneric
name, _) ->
5818 if safe_instanceof_enabled
5819 then Errors.instanceof_generic_classname
p name;
5821 | _, Tabstract
(AKdependent
(`
this, []), Some
(_, Tclass
_)) ->
5823 | _, Tabstract
((AKdependent
_ | AKnewtype
_), Some
ty) ->
5825 | _, Tclass
((_, cid as _c
), _, tyl) ->
5826 begin match Env.get_class
env cid with
5827 (* Why would this happen? *)
5829 env, (Reason.Rwitness ivar_pos
, Tobject
)
5831 | Some
class_info ->
5832 if SubType.is_sub_type
env x_ty
obj_ty
5834 (* If the right side of the `instanceof` object is
5835 * a super type of what we already knew. In this case,
5836 * since we already have a more specialized object, we
5837 * don't touch the original object. Check out the unit
5838 * test srecko.php if this is unclear.
5840 * Note that if x_ty is Typing_utils.tany env, no amount of subtype
5841 * checking will be able to specify it
5842 * further. This is arguably desirable to maintain
5843 * the invariant that removing annotations gets rid
5844 * of typing errors in partial mode (See also
5848 if tyl = [] || safe_instanceof_enabled
5849 then safe_instanceof
env p _c
class_info ivar_pos x_ty
obj_ty
5853 let env, tyl = List.map_env
env tyl resolve_obj in
5854 env, (r, Tunion
tyl)
5855 | _, (Terr
| Tany
| Tnonnull
| Tarraykind
_ | Tprim
_ | Tvar
_
5856 | Tfun
_ | Tabstract
((AKenum
_ | AKnewtype
_ | AKdependent
_), _)
5857 | Ttuple
_ | Tanon
(_, _) | Toption
_ | Tobject
| Tshape
_
5859 env, (Reason.Rwitness ivar_pos
, Tobject
)
5861 let env, x_ty
= resolve_obj env obj_ty in
5862 set_local
env ivar x_ty
5863 | T.Is
(ivar, h) when is_instance_var
(T.to_nast_expr
ivar) ->
5864 (* Stash env so we don't return an updated one if we don't refine *)
5866 let ety_env = { (Phase.env_with_self
env) with from_class
= Some CIstatic
; } in
5867 let env, hint_ty = Phase.localize_hint ~
ety_env env h in
5868 let env, hint_ty = Env.expand_type
env hint_ty in
5869 let reason = Reason.Ris
p in
5870 let refine_type hint_ty =
5871 let ivar_pos, ivar_ty
= fst
ivar in
5872 let env, ivar = get_instance_var
env (T.to_nast_expr
ivar) in
5874 if snd
hint_ty <> Tdynamic
&& SubType.is_sub_type
env ivar_ty
hint_ty
5876 else safely_refine_type
env p reason ivar_pos ivar_ty
hint_ty in
5877 set_local
env ivar hint_ty
5879 begin match snd
hint_ty with
5880 | _ when tparamet
-> refine_type hint_ty
5881 | Tprim
Nast.Tnull
-> refine_type (reason, Tnonnull
)
5886 and safely_refine_type
env p reason ivar_pos ivar_ty
hint_ty =
5887 match snd ivar_ty
, snd
hint_ty with
5888 | _, Tclass
((_, cid) as _c
, _, tyl) ->
5889 begin match Env.get_class
env cid with
5890 | Some
class_info ->
5891 let env, tparams_with_new_names
, tyl_fresh
=
5892 isexpr_generate_fresh_tparams
env class_info reason tyl in
5893 safely_refine_class_type
5894 env p _c
class_info ivar_ty
hint_ty reason tparams_with_new_names
5897 env, (Reason.Rwitness
ivar_pos, Tobject
)
5899 | Ttuple ivar_tyl
, Ttuple hint_tyl
5900 when (List.length ivar_tyl
) = (List.length hint_tyl
) ->
5902 List.map2_env
env ivar_tyl hint_tyl
begin fun env ivar_ty
hint_ty ->
5903 safely_refine_type
env p reason ivar_pos ivar_ty
hint_ty
5906 env, (reason, Ttuple
tyl)
5908 TUtils.non_null
env p ivar_ty
5909 | _, (Tany
| Tprim
_ | Toption
_ | Ttuple
_
5910 | Tshape
_ | Tvar
_ | Tabstract
_ | Tarraykind
_ | Tanon
_
5911 | Tunion
_ | Tobject
| Terr
| Tfun
_ | Tdynamic
) ->
5912 (* TODO(kunalm) Implement the type refinement for each type *)
5915 and safe_instanceof
env p class_name class_info ivar_pos ivar_ty
obj_ty =
5916 (* Generate fresh names consisting of formal type parameter name
5917 * with unique suffix *)
5918 let env, (tparams_with_new_names
: (decl
tparam * string) option list
) =
5919 List.map_env
env (Cls.tparams
class_info)
5920 (fun env ({tp_name
= (_, name); tp_reified
= reified
; tp_user_attributes
; _ } as tp
) ->
5921 let enforceable = Attributes.mem
SN.UserAttributes.uaEnforceable tp_user_attributes
in
5922 let newable = Attributes.mem
SN.UserAttributes.uaNewable tp_user_attributes
in
5923 let env, name = Env.add_fresh_generic_parameter
env name ~reified ~
enforceable ~
newable in
5924 env, Some
(tp
, name)) in
5925 let new_names = List.map
5926 ~
f:(fun x -> snd
@@ Option.value_exn
x)
5927 tparams_with_new_names
in
5929 snd
class_name ^
"<" ^
5930 String.concat ~sep
:"," new_names
5932 let reason = Reason.Rinstanceof
(ivar_pos, s) in
5933 let tyl_fresh = List.map
5934 ~
f:(fun new_name
-> (reason, Tabstract
(AKgeneric new_name
, None
)))
5937 safely_refine_class_type
5938 env p class_name class_info ivar_ty
obj_ty reason tparams_with_new_names
tyl_fresh in
5941 (** If we are dealing with a refinement like
5943 then class_info is the class info of MyClass and hint_tyl corresponds
5945 and isexpr_generate_fresh_tparams
env class_info reason hint_tyl
=
5946 let tparams_len = List.length
(Cls.tparams
class_info) in
5947 let hint_tyl = List.take
hint_tyl tparams_len in
5948 let pad_len = tparams_len - (List.length
hint_tyl) in
5950 List.map
hint_tyl (fun x -> Some
x) @ (List.init
pad_len (fun _ -> None
)) in
5951 let replace_wildcard env hint_ty ({ tp_name
= (_, tparam_name
); tp_reified
= reified
; tp_user_attributes
; _ } as tp
) =
5952 let enforceable = Attributes.mem
SN.UserAttributes.uaEnforceable tp_user_attributes
in
5953 let newable = Attributes.mem
SN.UserAttributes.uaNewable tp_user_attributes
in
5955 | Some
(_, Tabstract
(AKgeneric
name, _))
5956 when Env.is_fresh_generic_parameter
name ->
5957 env, (Some
(tp
, name), (reason, Tabstract
(AKgeneric
name, None
)))
5961 let env, new_name
= Env.add_fresh_generic_parameter
env tparam_name ~reified ~
enforceable ~
newable in
5962 env, (Some
(tp
, new_name
), (reason, Tabstract
(AKgeneric new_name
, None
)))
5964 let env, tparams_and_tyl
= List.map2_env
env hint_tyl (Cls.tparams
class_info)
5965 ~
f:replace_wildcard in
5966 let tparams_with_new_names, tyl_fresh = List.unzip tparams_and_tyl
in
5967 env, tparams_with_new_names, tyl_fresh
5969 and safely_refine_class_type
5970 env p class_name class_info ivar_ty
obj_ty reason
5971 (tparams_with_new_names : (decl
tparam * string) option list
)
5973 (* Type of variable in block will be class name
5974 * with fresh type parameters *)
5975 let obj_ty = (fst
obj_ty, Tclass
(class_name, Nonexact
, tyl_fresh)) in
5977 (* Add in constraints as assumptions on those type parameters *)
5979 type_expansions
= [];
5980 substs
= Subst.make
(Cls.tparams
class_info) tyl_fresh;
5981 this_ty = obj_ty; (* In case `this` appears in constraints *)
5983 validate_dty
= None
;
5985 let add_bounds env (t
, ty_fresh
) =
5986 List.fold_left t
.tp_constraints ~init
:env ~
f:begin fun env (ck
, ty) ->
5987 (* Substitute fresh type parameters for
5988 * original formals in constraint *)
5989 let env, ty = Phase.localize ~
ety_env env ty in
5990 SubType.add_constraint
p env ck ty_fresh
ty end in
5992 List.fold_left
(List.zip_exn
(Cls.tparams
class_info) tyl_fresh)
5993 ~
f:add_bounds ~init
:env in
5995 (* Finally, if we have a class-test on something with static class type,
5996 * then we can chase the hierarchy and decompose the types to deduce
5997 * further assumptions on type parameters. For example, we might have
5998 * class B<Tb> { ... }
5999 * class C extends B<int>
6000 * and have obj_ty = C and x_ty = B<T> for a generic parameter T.
6001 * Then SubType.add_constraint will deduce that T=int and add int as
6002 * both lower and upper bound on T in env.lenv.tpenv
6004 let env = SubType.add_constraint
p env Ast.Constraint_as
obj_ty ivar_ty
in
6006 (* It's often the case that the fresh name isn't necessary. For
6007 * example, if C<T> extends B<T>, and we have $x:B<t> for some type t
6008 * then $x instanceof C should refine to $x:C<t>.
6009 * We take a simple approach:
6010 * For a fresh type parameter T#1, if
6011 * - There is an eqality constraint T#1 = t,
6012 * - T#1 is covariant, and T#1 has only one upper bound t
6013 * - T#1 is contravariant, and t <: T#1 has only one lower bount t,
6014 * then replace T#1 with t.
6015 * This is done in Env.simplify_tpenv
6017 let tparam_names = List.filter_map
tparams_with_new_names
6018 ~
f:(Option.map ~
f:(fun (tp
, name) -> (name, tp
.tp_variance
))) in
6019 let env, tparam_substs
= Env.simplify_tpenv
env tparam_names reason in
6020 let tyl_fresh = List.map2_exn
tyl_fresh tparams_with_new_names
6021 ~
f:(fun orig_ty tparam_opt
->
6022 match tparam_opt
with
6024 | Some
(_tp
, name) -> SMap.find
name tparam_substs
) in
6025 let obj_ty_simplified = (fst
obj_ty, Tclass
(class_name, Nonexact
, tyl_fresh)) in
6026 env, obj_ty_simplified
6028 and is_instance_var
= function
6029 | _, (Lvar
_ | This
| Dollardollar
_) -> true
6030 | _, Obj_get
((_, This
), (_, Id
_), _) -> true
6031 | _, Obj_get
((_, Lvar
_), (_, Id
_), _) -> true
6032 | _, Class_get
(_, _) -> true
6035 and get_instance_var
env = function
6036 | p, Class_get
((_, cname
), CGstring
(_, member_name
)) ->
6037 let env, local = Env.FakeMembers.make_static
p env cname member_name
in
6039 | p, Obj_get
((_, This
| _, Lvar
_ as obj
), (_, Id
(_, member_name
)), _) ->
6040 let env, local = Env.FakeMembers.make
p env obj member_name
in
6042 | _, Dollardollar
(p, x)
6043 | _, Lvar
(p, x) -> env, (p, x)
6044 | p, This
-> env, (p, this)
6045 | _ -> failwith
"Should only be called when is_instance_var is true"
6047 (* Refine type for is_array, is_vec, is_keyset and is_dict tests
6048 * `pred_name` is the function name itself (e.g. 'is_vec')
6049 * `p` is position of the function name in the source
6050 * `arg_expr` is the argument to the function
6052 and is_array
env ty p pred_name arg_expr
=
6053 refine_lvalue_type
env arg_expr ~
refine:begin fun env arg_ty
->
6054 let r = Reason.Rpredicated
(p, pred_name
) in
6055 let env, tarrkey_name
= Env.add_fresh_generic_parameter
env "Tk" ~reified
:Nast.Erased ~
enforceable:false ~
newable:false in
6056 let tarrkey = (r, Tabstract
(AKgeneric tarrkey_name
, None
)) in
6057 let env = SubType.add_constraint
p env Ast.Constraint_as
tarrkey (MakeType.arraykey
r) in
6058 let env, tfresh_name
= Env.add_fresh_generic_parameter
env "T" ~reified
:Nast.Erased ~
enforceable:false ~
newable:false in
6059 let tfresh = (r, Tabstract
(AKgeneric tfresh_name
, None
)) in
6060 (* This is the refined type of e inside the branch *)
6064 MakeType.dict
r tarrkey tfresh
6066 MakeType.vec
r tfresh
6068 MakeType.keyset
r tarrkey
6070 let safe_isarray_enabled =
6071 TypecheckerOptions.experimental_feature_enabled
6072 (Env.get_tcopt
env) TypecheckerOptions.experimental_isarray
in
6073 if safe_isarray_enabled
6074 then (r, Tarraykind
(AKvarray_or_darray
tfresh))
6075 else (r, Tarraykind AKany
) in
6076 (* Add constraints on generic parameters that must
6077 * hold for refined_ty <:arg_ty. For example, if arg_ty is Traversable<T>
6078 * and refined_ty is keyset<T#1> then we know T#1 <: T *)
6079 let env = SubType.add_constraint
p env Ast.Constraint_as
refined_ty arg_ty
in
6083 and key_exists
env pos shape
field =
6084 let field = T.to_nast_expr
field in
6085 refine_lvalue_type
env shape ~
refine:begin fun env shape_ty
->
6086 match TUtils.shape_field_name
env field with
6087 | None
-> env, shape_ty
6088 | Some field_name
-> Typing_shapes.refine_shape field_name
pos env shape_ty
6091 and string2
env idl
=
6093 List.fold_left idl ~init
:(env,[]) ~
f:begin fun (env,tel) x ->
6094 let env, te, ty = expr env x in
6096 let env = SubType.sub_string
p env ty in
6101 (* If the current class inherits from classes that take type arguments, we need
6102 * to check that the arguments provided are consistent with the constraints on
6103 * the type parameters. *)
6104 and check_implements_tparaml
(env: Env.env) ht
=
6105 let _r, (p, c), paraml = TUtils.unwrap_class_type ht
in
6106 let class_ = Decl_env.get_class_dep
env.Env.decl_env
c in
6109 (* The class lives in PHP land *)
6112 let size1 = List.length
class_.dc_tparams
in
6113 let size2 = List.length
paraml in
6114 if size1 <> size2 then Errors.class_arity
p class_.dc_pos
c size1;
6115 let subst = Inst.make_subst
class_.dc_tparams
paraml in
6116 iter2_shortest
begin fun t
ty ->
6117 let ty_pos = Reason.to_pos
(fst
ty) in
6118 List.iter t
.tp_constraints
begin fun (ck
, cstr) ->
6119 (* Constraint might contain uses of generic type parameters *)
6120 let cstr = Inst.instantiate
subst cstr in
6122 | Ast.Constraint_as
->
6123 Type.sub_type_decl
ty_pos Reason.URnone
env ty cstr
6124 | Ast.Constraint_eq
->
6125 (* This code could well be unreachable, because we don't allow
6126 * equality constraints on class generics. *)
6127 Type.sub_type_decl
ty_pos Reason.URnone
env ty cstr;
6128 Type.sub_type_decl
ty_pos Reason.URnone
env cstr ty
6129 | Ast.Constraint_super
->
6130 Type.sub_type_decl
ty_pos Reason.URnone
env cstr ty
6131 | Ast.Constraint_pu_from
->
6132 failwith
"Typing.check_implements_tparaml: \
6133 implement typing for 'from' constraints"
6135 end class_.dc_tparams
paraml
6137 (* In order to type-check a class, we need to know what "parent"
6138 * refers to. Sometimes people write "parent::", when that happens,
6139 * we need to know the type of parent.
6141 and class_def_parent
env class_def class_type
=
6142 match class_def
.c_extends
with
6143 | (_, Happly
((_, x), _) as parent_ty
) :: _ ->
6144 let parent_type = Decl_env.get_class_dep
env.Env.decl_env
x in
6145 (match parent_type with
6146 | Some
parent_type -> check_parent class_def class_type
parent_type
6148 let parent_ty = Decl_hint.hint
env.Env.decl_env
parent_ty in
6149 env, Some
x, parent_ty
6150 (* The only case where we have more than one parent class is when
6151 * dealing with interfaces and interfaces cannot use parent.
6154 | _ -> env, None
, (Reason.Rnone
, Typing_utils.tany env)
6156 and check_parent class_def class_type
parent_type =
6157 let position = fst class_def
.c_name
in
6158 if (Cls.const class_type
) && not
parent_type.dc_const
6159 then Errors.self_const_parent_not
position;
6160 if parent_type.dc_const
&& not
(Cls.const class_type
)
6161 then Errors.parent_const_self_not
position;
6162 (* Are all the parents in Hack? Do we know all their methods?
6163 * If so, let's check that the abstract methods have been implemented.
6165 if (Cls.members_fully_known class_type
)
6166 then check_parent_abstract
position parent_type class_type
;
6167 if parent_type.dc_final
6168 then Errors.extend_final
position parent_type.dc_pos
parent_type.dc_name
6171 and check_parent_sealed child_type
parent_type =
6172 match parent_type.dc_sealed_whitelist
with
6175 let parent_pos = parent_type.dc_pos
in
6176 let parent_name = parent_type.dc_name
in
6177 let child_pos = (Cls.pos child_type
) in
6178 let child_name = (Cls.name child_type
) in
6179 let check kind action
=
6180 if not
(SSet.mem
child_name whitelist
)
6181 then Errors.extend_sealed
child_pos parent_pos parent_name kind action
in
6182 begin match parent_type.dc_kind
, (Cls.kind child_type
) with
6183 | Ast.Cinterface
, Ast.Cinterface
-> check "interface" "extend"
6184 | Ast.Cinterface
, _ -> check "interface" "implement"
6185 | Ast.Ctrait
, _ -> check "trait" "use"
6187 | Ast.Cnormal
, _ -> check "class" "extend"
6188 | Ast.Cenum
, _ -> ()
6189 | Ast.Crecord
, _ -> ()
6192 and check_parents_sealed
env child_def child_type
=
6193 let parents = child_def
.c_extends
@ child_def
.c_implements
@ child_def
.c_uses
in
6194 List.iter parents begin function
6195 | _, Happly
((_, name), _) ->
6196 begin match Decl_env.get_class_dep
env.Env.decl_env
name with
6197 | Some
parent_type -> check_parent_sealed child_type
parent_type
6203 and check_parent_abstract
position parent_type class_type
=
6204 let is_final = (Cls.final class_type
) in
6205 if parent_type.dc_kind
= Ast.Cabstract
&&
6206 ((Cls.kind class_type
) <> Ast.Cabstract
|| is_final)
6208 check_extend_abstract_meth ~
is_final position (Cls.methods class_type
);
6209 check_extend_abstract_meth ~
is_final position (Cls.smethods class_type
);
6210 check_extend_abstract_const ~
is_final position (Cls.consts class_type
);
6211 check_extend_abstract_typeconst
6212 ~
is_final position (Cls.typeconsts class_type
);
6215 and shallow_decl_enabled
() =
6216 TCO.shallow_class_decl
(GlobalNamingOptions.get
())
6218 and class_def
tcopt c =
6219 let env = EnvFromDef.class_env
tcopt c in
6220 let tc = Env.get_class
env (snd
c.c_name
) in
6221 add_decl_errors
(Option.(map
tc (fun tc -> value_exn
(Cls.decl_errors
tc))));
6222 let c = TNBody.class_meth_bodies
c in
6223 NastCheck.class_ env c;
6224 NastInitCheck.class_ env c;
6227 (* This can happen if there was an error during the declaration
6231 Typing_requirements.check_class
env tc;
6232 if shallow_decl_enabled
() then
6233 Typing_inheritance.check_class
env tc;
6234 Some
(class_def_
env c tc)
6236 and class_def_
env c tc =
6238 let kind = match c.c_kind
with
6239 | Ast.Cenum
-> SN.AttributeKinds.enum
6240 | _ -> SN.AttributeKinds.cls
in
6241 Typing_attributes.check_def
env new_object
kind c.c_user_attributes
in
6243 { env with Env.inside_ppl_class
=
6244 Attributes.mem
SN.UserAttributes.uaProbabilisticModel
c.c_user_attributes
6246 let pc, _ = c.c_name
in
6248 (c.c_extends
@ c.c_implements
@ c.c_uses
)
6249 (Decl_hint.hint
env.Env.decl_env
) in
6250 let env, constraints
=
6251 Phase.localize_generic_parameters_with_bounds
env c.c_tparams
.c_tparam_list
6252 ~
ety_env:(Phase.env_with_self
env) in
6253 let env = add_constraints
(fst
c.c_name
) env constraints
in
6254 Typing_variance.class_ (Env.get_tcopt
env) (snd
c.c_name
) tc impl;
6255 List.iter impl (check_implements_tparaml
env);
6256 check_parents_sealed
env c tc;
6258 let env, parent_id
, parent = class_def_parent
env c tc in
6259 let is_final = (Cls.final
tc) in
6260 if ((Cls.kind tc) = Ast.Cnormal
|| is_final) && (Cls.members_fully_known
tc)
6262 check_extend_abstract_meth ~
is_final pc (Cls.methods
tc);
6263 check_extend_abstract_meth ~
is_final pc (Cls.smethods
tc);
6264 check_extend_abstract_const ~
is_final pc (Cls.consts
tc);
6265 check_extend_abstract_typeconst ~
is_final pc (Cls.typeconsts
tc);
6267 let env = Env.set_parent
env parent in
6268 let env = match parent_id
with
6270 | Some parent_id
-> Env.set_parent_id
env parent_id
in
6271 if (Cls.final
tc) then begin
6273 | Ast.Cinterface
-> Errors.interface_final
(fst
c.c_name
)
6274 | Ast.Cabstract
-> ()
6275 | Ast.Ctrait
-> Errors.trait_final
(fst
c.c_name
)
6278 Errors.internal_error
pc ("The parser should not parse final on" ^
6279 (if c.c_kind
= Ast.Cenum
then "enums" else "records"))
6282 let static_vars, vars = split_vars
c in
6283 List.iter static_vars ~
f:begin fun {cv_id
=(p,id); _} ->
6284 check_static_class_element
(Cls.get_prop
tc) ~elt_type
:`Property
id p
6286 List.iter vars ~
f:begin fun {cv_id
=(p,id); _} ->
6287 check_dynamic_class_element
(Cls.get_sprop
tc) ~elt_type
:`Property
id p
6289 let constructor, static_methods
, methods
= split_methods
c in
6290 List.iter static_methods ~
f:begin fun {m_name
=(p,id); _} ->
6291 check_static_class_element
(Cls.get_method
tc) ~elt_type
:`Method
id p
6293 List.iter methods ~
f:begin fun {m_name
=(p,id); _} ->
6294 check_dynamic_class_element
(Cls.get_smethod
tc) ~elt_type
:`Method
id p
6296 (* get a map of method names to list of traits from which they were removed *)
6297 let alist = List.map
c.c_method_redeclarations ~
f:(fun m ->
6298 let _, name = m.mt_method
in
6299 let _, trait
, _ = Decl_utils.unwrap_class_hint
m.mt_trait
in
6301 let removals = String.Map.of_alist_fold
alist ~init
:[] ~
f:(Fn.flip
List.cons
) in
6302 List.iter impl (class_implements_type
env c removals);
6303 let env = List.fold
c.c_method_redeclarations ~init
:env ~
f:(supertype_redeclared_method
tc) in
6304 if (Cls.is_disposable
tc)
6305 then List.iter (c.c_extends
@ c.c_uses
) (Typing_disposable.enforce_is_disposable
env);
6306 let typed_vars = List.map
vars (class_var_def
env ~is_static
:false c) in
6307 let typed_method_redeclarations = [] in
6308 let typed_methods = List.filter_map methods
(method_def
env) in
6309 let typed_typeconsts = List.map
c.c_typeconsts
(typeconst_def
env) in
6310 let typed_consts, const_types
=
6311 List.unzip
(List.map
c.c_consts
(class_const_def
env)) in
6312 let env = Typing_enum.enum_class_check
env tc c.c_consts const_types
in
6313 let typed_constructor = class_constr_def
env constructor in
6314 let env = Env.set_static
env in
6315 let typed_static_vars =
6316 List.map
static_vars (class_var_def
env ~is_static
:true c) in
6317 let typed_static_methods = List.filter_map static_methods
(method_def
env) in
6318 let filename = Pos.filename (fst
c.c_name
) in
6319 let droot = env.Env.decl_env
.Decl_env.droot in
6321 file_attributes
(Env.get_tcopt
env) filename c.c_mode
droot c.c_file_attributes
in
6323 match typed_constructor with
6324 | None
-> typed_static_methods @ typed_methods
6325 | Some
m -> m :: typed_static_methods @ typed_methods in
6327 T.c_span
= c.c_span
;
6328 T.c_annotation
= Env.save
env.Env.lenv.Env.tpenv
env;
6329 T.c_mode
= c.c_mode
;
6330 T.c_final
= c.c_final
;
6331 T.c_is_xhp
= c.c_is_xhp
;
6332 T.c_kind
= c.c_kind
;
6333 T.c_name
= c.c_name
;
6334 T.c_tparams
= class_type_param
env c.c_tparams
;
6335 T.c_extends
= c.c_extends
;
6336 T.c_uses
= c.c_uses
;
6337 (* c_use_as_alias and c_insteadof_alias are PHP features not supported
6338 * in Hack but are required since we have runtime support for it
6340 T.c_use_as_alias
= [];
6341 T.c_insteadof_alias
= [];
6342 T.c_method_redeclarations
= typed_method_redeclarations;
6343 T.c_xhp_attr_uses
= c.c_xhp_attr_uses
;
6344 T.c_xhp_category
= c.c_xhp_category
;
6345 T.c_reqs
= c.c_reqs
;
6346 T.c_implements
= c.c_implements
;
6347 T.c_consts
= typed_consts;
6348 T.c_typeconsts
= typed_typeconsts;
6349 T.c_vars
= typed_static_vars @ typed_vars;
6350 T.c_methods
= methods;
6351 T.c_file_attributes
= file_attrs;
6352 T.c_user_attributes
= List.map
c.c_user_attributes
(user_attribute
env);
6353 T.c_namespace
= c.c_namespace
;
6354 T.c_enum
= c.c_enum
;
6355 T.c_doc_comment
= c.c_doc_comment
;
6356 T.c_attributes
= [];
6357 T.c_xhp_children
= c.c_xhp_children
;
6359 T.c_pu_enums
= []; (* TODO PU (typing) *)
6362 and check_dynamic_class_element get_static_elt element_name dyn_pos ~elt_type
=
6363 (* The non-static properties that we get passed do not start with '$', but the
6364 static properties we want to look up do, so add it. *)
6367 | `Method
-> element_name
6368 | `Property
-> "$"^element_name
6370 match get_static_elt
id with
6372 | Some static_element
->
6373 let lazy (static_element_reason
, _) = static_element
.ce_type
in
6374 Errors.static_redeclared_as_dynamic
6376 (Reason.to_pos static_element_reason
)
6380 and check_static_class_element get_dyn_elt element_name static_pos ~elt_type
=
6381 (* The static properties that we get passed in start with '$', but the
6382 non-static properties we're matching against don't, so we need to detect
6383 that and remove it if present. *)
6384 let element_name = String_utils.lstrip
element_name "$" in
6385 match get_dyn_elt
element_name with
6387 | Some dyn_element
->
6388 let lazy (dyn_element_reason
, _) = dyn_element
.ce_type
in
6389 Errors.dynamic_redeclared_as_static
6391 (Reason.to_pos dyn_element_reason
)
6395 and check_extend_abstract_meth ~
is_final p seq
=
6396 Sequence.iter seq
begin fun (x, ce
) ->
6397 match ce
.ce_type
with
6398 | lazy (r, Tfun
{ ft_abstract
= true; _ }) ->
6399 Errors.implement_abstract ~
is_final p (Reason.to_pos
r) "method" x
6403 (* Type constants must be bound to a concrete type for non-abstract classes.
6405 and check_extend_abstract_typeconst ~
is_final p seq
=
6406 Sequence.iter seq
begin fun (x, tc) ->
6407 if tc.ttc_type
= None
then
6408 Errors.implement_abstract ~
is_final p (fst
tc.ttc_name
) "type constant" x
6411 and check_extend_abstract_const ~
is_final p seq
=
6412 Sequence.iter seq
begin fun (x, cc
) ->
6413 if cc
.cc_abstract
&& not cc
.cc_synthesized
then
6414 let cc_pos = Reason.to_pos
(fst cc
.cc_type
) in
6415 Errors.implement_abstract ~
is_final p cc_pos "constant" x
6418 and typeconst_abstract_kind
= function
6419 | Nast.TCAbstract
default -> T.TCAbstract
default
6420 | Nast.TCPartiallyAbstract
-> T.TCPartiallyAbstract
6421 | Nast.TCConcrete
-> T.TCConcrete
6423 and typeconst_def
env {
6425 c_tconst_name
= (pos, _) as id;
6426 c_tconst_constraint
;
6427 c_tconst_type
= hint
;
6428 c_tconst_user_attributes
;
6430 let env, cstr = opt
Phase.localize_hint_with_self
env c_tconst_constraint
in
6431 let env, ty = opt
Phase.localize_hint_with_self
env hint
in
6433 Option.map2
ty cstr ~
f:(Type.sub_type
pos Reason.URtypeconst_cstr
env)
6435 let env = begin match hint
with
6436 | Some
(pos, Hshape
{ nsi_field_map
; _ }) ->
6437 let get_name sfi
= sfi
.sfi_name
in
6438 check_shape_keys_validity
env pos (List.map ~
f:get_name nsi_field_map
)
6441 let env = Typing_attributes.check_def
env new_object
6442 SN.AttributeKinds.typeconst c_tconst_user_attributes
in
6444 T.c_tconst_abstract
= typeconst_abstract_kind c_tconst_abstract
;
6445 T.c_tconst_name
= id;
6446 T.c_tconst_constraint
= c_tconst_constraint
;
6447 T.c_tconst_type
= hint
;
6448 T.c_tconst_user_attributes
= List.map c_tconst_user_attributes
(user_attribute
env);
6451 and class_const_def
env (h, id, e) =
6452 let env, ty, opt_expected
=
6455 let env, ty = Env.fresh_type
env (fst
id) in
6458 let env, ty = Phase.localize_hint_with_self
env h in
6459 env, ty, Some
(fst
id, Reason.URhint
, ty)
6463 let env, te, ty'
= expr ?
expected:opt_expected
env e in
6464 ignore
(Type.coerce_type
(fst
id) Reason.URhint
env ty'
ty);
6465 (h, id, Some
te), ty'
6469 and class_constr_def
env constructor =
6470 let env = { env with Env.inside_constructor
= true } in
6471 Option.bind
constructor (method_def
env)
6473 and class_implements_type
env c1
removals ctype2
=
6475 List.map c1
.c_tparams
.c_tparam_list
begin fun { tp_name
= (p, s); _ } ->
6476 (Reason.Rwitness
p, Tgeneric
s)
6478 let r = Reason.Rwitness
(fst c1
.c_name
) in
6479 let ctype1 = r, Tapply
(c1
.c_name
, params) in
6480 Typing_extends.check_implements
env removals ctype2
ctype1;
6483 (* Type-check a property declaration, with optional initializer *)
6484 and class_var_def
env ~is_static
c cv
=
6485 (* First pick up and localize the hint if it exists *)
6487 match cv
.cv_type
with
6490 | Some
(p, _ as cty
) ->
6492 (* If this is an XHP attribute and we're in strict mode,
6493 relax to partial mode to allow the use of the "array"
6494 annotation without specifying type parameters. Until
6495 recently HHVM did not allow "array" with type parameters
6496 in XHP attribute declarations, so this is a temporary
6497 hack to support existing code for now. *)
6498 (* Task #5815945: Get rid of this Hack *)
6499 if cv
.cv_is_xhp
&& (Env.is_strict
env)
6500 then Env.set_mode
env FileInfo.Mpartial
6502 let cty = Decl_hint.hint
env.Env.decl_env
cty in
6503 let env, cty = Phase.localize_with_self
env cty in
6504 env, Some
(p, Reason.URhint
, cty) in
6505 (* Next check the expression, passing in expected type if present *)
6506 let env, typed_cv_expr
, ty =
6507 match cv
.cv_expr
with
6509 let env, ty = Env.fresh_type
env (fst cv
.cv_id
) in
6512 let env, te, ty = expr ?
expected env e in
6513 (* Check that the inferred type is a subtype of the expected type.
6514 * Eventually this will be the responsibility of `expr`
6519 | Some
(p, ur
, cty) -> Type.coerce_type
p ur
env ty cty in
6523 then Typing_attributes.check_def
env new_object
6524 SN.AttributeKinds.staticProperty cv
.cv_user_attributes
6525 else Typing_attributes.check_def
env new_object
6526 SN.AttributeKinds.instProperty cv
.cv_user_attributes
in
6528 if Option.is_none cv
.cv_type
6530 if Env.is_strict
env
6531 then Errors.add_a_typehint
(fst cv
.cv_id
)
6533 let pos, name = cv
.cv_id
in
6534 let name = if is_static
then "$" ^
name else name in
6535 let var_type = Reason.Rwitness
pos, Typing_utils.tany env in
6536 if Option.is_none cv
.cv_expr
6537 then Typing_suggest.uninitialized_member
(snd
c.c_name
) name env var_type ty
6538 else Typing_suggest.save_member
name env var_type ty
6541 T.cv_final
= cv
.cv_final
;
6542 T.cv_is_xhp
= cv
.cv_is_xhp
;
6543 T.cv_visibility
= cv
.cv_visibility
;
6544 T.cv_type
= cv
.cv_type
;
6546 T.cv_expr
= typed_cv_expr
;
6547 T.cv_user_attributes
= List.map cv
.cv_user_attributes
(user_attribute
env);
6548 T.cv_is_promoted_variadic
= cv
.cv_is_promoted_variadic
;
6549 T.cv_doc_comment
= cv
.cv_doc_comment
; (* Can make None to save space *)
6550 T.cv_is_static
= is_static
6554 and add_constraints
p env constraints
=
6555 let add_constraint env (ty1, ck
, ty2) =
6556 SubType.add_constraint p env ck
ty1 ty2 in
6557 List.fold_left constraints ~
f:add_constraint ~init
: env
6559 and supertype_redeclared_method
tc env m =
6560 let pos, name = m.mt_name
in
6561 let get_method = if m.mt_static
then Env.get_static_member
else Env.get_member
in
6563 let class_member_opt = get_method true env tc name in
6564 let _, trait
, _ = Decl_utils.unwrap_class_hint
m.mt_trait
in
6565 let _, trait_method
= m.mt_method
in
6567 let trait_member_opt = Env.get_class
env trait
>>= (fun trait_tc
->
6568 get_method true env trait_tc trait_method
) in
6570 ignore
(map2
trait_member_opt class_member_opt ~
f:begin fun trait_member class_member
->
6571 match trait_member
.ce_type
, class_member
.ce_type
with
6572 | lazy (r_child
, Tfun ft_child
), lazy (r_parent
, Tfun ft_parent
) ->
6575 ignore
(Typing_subtype.(subtype_method
6577 ~extra_info
:{ method_info
= None
; class_ty
= None
; parent_class_ty
= None
}
6586 (fun () -> Errors.bad_method_override
pos name errorl
)
6588 Errors.bad_decl_override
6589 (Reason.to_pos r_parent
)
6598 and file_attributes
tcopt file
mode droot file_attrs =
6599 let env = Env.empty
tcopt file ~
droot in
6600 let env = Env.set_mode
env mode in
6601 let uas = List.concat_map ~
f:(fun fa
-> fa
.fa_user_attributes
) file_attrs in
6603 Typing_attributes.check_def
env new_object
SN.AttributeKinds.file
uas in
6606 { T.fa_user_attributes
= List.map ~
f:(user_attribute
env) fa
.fa_user_attributes
;
6607 T.fa_namespace
= fa
.fa_namespace
;
6611 and user_attribute
env ua
=
6612 let typed_ua_params =
6613 List.map ua
.ua_params
(fun e -> let _env, te, _ty
= expr env e in te) in
6615 T.ua_name
= ua
.ua_name
;
6616 T.ua_params
= typed_ua_params;
6619 and reify_kind
= function
6620 | Nast.Erased
-> T.Erased
6621 | Nast.SoftReified
-> T.SoftReified
6622 | Nast.Reified
-> T.Reified
6625 and type_param
env t
=
6626 let env = Typing_attributes.check_def
env new_object
6627 SN.AttributeKinds.typeparam t
.tp_user_attributes
in
6629 T.tp_variance
= t
.tp_variance
;
6630 T.tp_name
= t
.tp_name
;
6631 T.tp_constraints
= t
.tp_constraints
;
6632 T.tp_reified
= reify_kind t
.tp_reified
;
6633 T.tp_user_attributes
= List.map t
.tp_user_attributes
(user_attribute
env);
6636 and class_type_param
env ct
=
6638 T.c_tparam_list
= List.map ~
f:(type_param
env) ct
.c_tparam_list
;
6639 T.c_tparam_constraints
= SMap.map
(Tuple.T2.map_fst ~
f:reify_kind
) ct
.c_tparam_constraints
;
6642 and method_def
env m =
6643 with_timeout env m.m_name ~do_
:begin fun env ->
6644 (* reset the expression dependent display ids for each method body *)
6645 Reason.expr_display_id_map
:= IMap.empty
;
6646 let pos = fst
m.m_name
in
6647 let env = Env.reinitialize_locals
env in
6648 let env = Env.set_env_function_pos
env pos in
6649 let env = Typing_attributes.check_def
env new_object
6650 SN.AttributeKinds.mthd
m.m_user_attributes
in
6651 let reactive = fun_reactivity env.Env.decl_env
m.m_user_attributes
m.m_params
in
6653 match TUtils.fun_mutable
m.m_user_attributes
with
6655 (* <<__Mutable>> is implicit on constructors *)
6656 if snd
m.m_name
= SN.Members.__construct
6657 then Some Param_borrowed_mutable
6660 let env = Env.set_env_reactive
env reactive in
6661 let env = Env.set_fun_mutable
env mut in
6663 { (Phase.env_with_self
env) with from_class
= Some CIstatic
; } in
6664 let env, constraints
=
6665 Phase.localize_generic_parameters_with_bounds
env m.m_tparams
6667 let env = add_constraints
pos env constraints
in
6669 Phase.localize_where_constraints ~
ety_env env m.m_where_constraints
in
6671 if Env.is_static
env then env
6672 else Env.set_local
env this (Env.get_self
env) in
6674 match Env.get_class
env (Env.get_self_id
env) with
6677 (* Mark $this as a using variable if it has a disposable type *)
6678 if (Cls.is_disposable
c)
6679 then Env.set_using_var
env this
6681 let env = Env.clear_params
env in
6682 let env, ty = match m.m_ret
with
6684 env, Typing_return.make_default_return
env m.m_name
6686 let ret = Decl_hint.hint
env.Env.decl_env
ret in
6687 (* If a 'this' type appears it needs to be compatible with the
6691 { (Phase.env_with_self
env) with
6692 from_class
= Some CIstatic
} in
6693 Phase.localize ~
ety_env env ret in
6694 let return = Typing_return.make_info
m.m_fun_kind
m.m_user_attributes
env
6695 ~is_explicit
:(Option.is_some
m.m_ret
) ty in
6696 let env, param_tys
=
6697 List.map_env
env m.m_params
make_param_local_ty in
6698 if Env.is_strict
env then begin
6699 List.iter2_exn ~
f:(check_param
env) m.m_params param_tys
;
6701 Typing_memoize.check_method
env m;
6702 let env, typed_params
=
6703 List.map_env
env (List.zip_exn param_tys
m.m_params
) bind_param in
6704 let env, t_variadic
= match m.m_variadic
with
6705 | FVvariadicArg vparam
->
6706 let env, ty = make_param_local_ty env vparam
in
6707 if Env.is_strict
env then
6708 check_param
env vparam
ty;
6709 let env, t_variadic
= bind_param env (ty, vparam
) in
6710 env, (T.FVvariadicArg t_variadic
)
6711 | FVellipsis
p -> env, T.FVellipsis
p
6712 | FVnonVariadic
-> env, T.FVnonVariadic
in
6713 let nb = Nast.assert_named_body
m.m_body
in
6714 let local_tpenv = env.Env.lenv.Env.tpenv
in
6716 fun_ ~abstract
:m.m_abstract
env return pos nb m.m_fun_kind
in
6717 let env = SubType.solve_all_unsolved_tyvars
env in
6718 Typing_subtype.log_prop
env;
6719 let env = Env.check_todo
env in
6720 (* restore original method reactivity *)
6721 let env = Env.set_env_reactive
env reactive in
6725 snd
m.m_name
= SN.Members.__destruct
6726 || snd
m.m_name
= SN.Members.__construct
->
6727 Some
(pos, Happly
((pos, "void"), []))
6728 | None
when Env.is_strict
env ->
6729 Typing_return.suggest_return
env pos return.Typing_env_return_info.return_type; None
6730 | None
-> let (pos, id) = m.m_name
in
6731 let id = (Env.get_self_id
env) ^
"::" ^
id in
6732 Typing_suggest.save_fun_or_method
(pos, id);
6735 Typing_return.async_suggest_return
(m.m_fun_kind
) hint
(fst
m.m_name
); m.m_ret in
6736 let m = { m with m_ret = m_ret; } in
6738 if Nast.named_body_is_unsafe
nb
6739 then Tast.Annotations.FuncBodyAnnotation.HasUnsafeBlocks
6740 else Tast.Annotations.FuncBodyAnnotation.NoUnsafeBlocks
in
6742 T.m_annotation
= Env.save
local_tpenv env;
6743 T.m_span
= m.m_span
;
6744 T.m_final
= m.m_final
;
6745 T.m_static
= m.m_static
;
6746 T.m_abstract
= m.m_abstract
;
6747 T.m_visibility
= m.m_visibility
;
6748 T.m_name
= m.m_name
;
6749 T.m_tparams
= List.map
m.m_tparams
(type_param
env);
6750 T.m_where_constraints
= m.m_where_constraints
;
6751 T.m_variadic
= t_variadic
;
6752 T.m_params
= typed_params
;
6753 T.m_fun_kind
= m.m_fun_kind
;
6754 T.m_user_attributes
= List.map
m.m_user_attributes
(user_attribute
env);
6756 T.m_body
= { T.fb_ast
= tb; fb_annotation
= annotation };
6757 T.m_external
= m.m_external
;
6758 T.m_doc_comment
= m.m_doc_comment
;
6760 Typing_lambda_ambiguous.suggest_method_def
env method_def
6761 end (* with_timeout *)
6763 and typedef_def
tcopt typedef
=
6764 let env = EnvFromDef.typedef_env
tcopt typedef
in
6765 let tdecl = Env.get_typedef
env (snd typedef
.t_name
) in
6766 add_decl_errors
(Option.(map
tdecl (fun tdecl -> value_exn
tdecl.td_decl_errors
)));
6767 let env, constraints
=
6768 Phase.localize_generic_parameters_with_bounds
env typedef
.t_tparams
6769 ~
ety_env:(Phase.env_with_self
env) in
6770 let env = add_constraints
(fst typedef
.t_name
) env constraints
in
6771 NastCheck.typedef
env typedef
;
6776 t_constraint
= tcstr
;
6778 t_user_attributes
= _;
6783 let ty = Decl_hint.hint
env.Env.decl_env hint
in
6784 let env, ty = Phase.localize_with_self
env ty in
6785 let env = begin match tcstr
with
6787 let cstr = Decl_hint.hint
env.Env.decl_env tcstr
in
6788 let env, cstr = Phase.localize_with_self
env cstr in
6789 Typing_ops.sub_type t_pos
Reason.URnewtype_cstr
env ty cstr
6792 let env = begin match hint
with
6793 | pos, Hshape
{ nsi_allows_unknown_fields
=_; nsi_field_map
} ->
6794 let get_name sfi
= sfi
.sfi_name
in
6795 check_shape_keys_validity
env pos (List.map ~
f:get_name nsi_field_map
)
6798 let env = Typing_attributes.check_def
env new_object
6799 SN.AttributeKinds.typealias typedef
.t_user_attributes
in
6801 T.t_annotation
= Env.save
env.Env.lenv.Env.tpenv
env;
6802 T.t_name
= typedef
.t_name
;
6803 T.t_mode
= typedef
.t_mode
;
6804 T.t_vis
= typedef
.t_vis
;
6805 T.t_user_attributes
= List.map typedef
.t_user_attributes
(user_attribute
env);
6806 T.t_constraint
= typedef
.t_constraint
;
6807 T.t_kind
= typedef
.t_kind
;
6808 T.t_tparams
= List.map typedef
.t_tparams
(type_param
env);
6809 T.t_namespace
= typedef
.t_namespace
;
6812 and gconst_def
tcopt cst
=
6813 let env = EnvFromDef.gconst_env
tcopt cst
in
6814 add_decl_errors
(Option.map
(Env.get_gconst
env (snd cst
.cst_name
)) ~
f:snd
);
6816 let typed_cst_value, env =
6817 match cst
.cst_value
with
6820 match cst
.cst_type
with
6822 let ty = Decl_hint.hint
env.Env.decl_env hint
in
6823 let env, dty
= Phase.localize_with_self
env ty in
6824 let env, te, value_type
=
6825 expr ~
expected:(fst hint
, Reason.URhint
, dty
) env value in
6826 let env = Typing_utils.sub_type
env value_type dty
in
6829 let env, te, _value_type
= expr env value in
6832 { T.cst_annotation
= Env.save
env.Env.lenv.Env.tpenv
env;
6833 T.cst_mode
= cst
.cst_mode
;
6834 T.cst_name
= cst
.cst_name
;
6835 T.cst_type
= cst
.cst_type
;
6836 T.cst_value
= typed_cst_value;
6837 T.cst_namespace
= cst
.cst_namespace
;
6838 T.cst_span
= cst
.cst_span
;
6841 (* Calls the method of a class, but allows the f callback to override the
6842 * return value type *)
6843 and overload_function make_call fpos p env (cpos
, class_id
) method_id
el uel
f =
6844 let env, tcid
, ty = static_class_id ~check_constraints
:false cpos
env [] class_id
in
6845 let env, _tel
, _ = exprs ~is_func_arg
:true env el in
6847 class_get ~
is_method:true ~is_const
:false env ty method_id class_id
in
6848 (* call the function as declared to validate arity and input types,
6849 but ignore the result and overwrite with custom one *)
6850 let (env, tel, tuel
, res
), has_error
= Errors.try_with_error
6851 (* TODO: Should we be passing hints here *)
6852 (fun () -> (call ~
expected:None
p env fty el uel
), false)
6853 (fun () -> (env, [], [], (Reason.Rwitness
p, Typing_utils.tany env)), true) in
6854 (* if there are errors already stop here - going forward would
6855 * report them twice *)
6857 then env, T.make_typed_expr
p res
T.Any
, res
6859 let env, ty = f env fty res
el in
6862 | r, Tfun
ft -> r, Tfun
{ft with ft_ret
= ty}
6864 let te = T.make_typed_expr
fpos fty (T.Class_const
(tcid
, method_id
)) in
6865 make_call env te [] tel tuel
ty
6867 and update_array_type ?lhs_of_null_coalesce
p env e1 e2
valkind =
6869 Typing_arrays.update_array_type
p ~is_map
:(Option.is_some e2
) in
6871 | `lvalue
| `lvalue_subexpr
->
6873 raw_expr ~
valkind:`lvalue_subexpr ~check_defined
:true env e1
in
6874 let env, ty1 = type_mapper env ty1 in
6876 | (_, Lvar
(_, x)) ->
6877 (* type_mapper has updated the type in ty1 typevars, but we
6878 need to update the local variable type too *)
6879 let env, ty1 = set_valid_rvalue
p env x ty1 in
6881 | _ -> env, te1, ty1
6884 raw_expr ?lhs_of_null_coalesce
env e1
6886 let nast_to_tast opts nast
=
6887 let convert_def = function
6889 begin match fun_def opts
f with
6891 | None
-> failwith
@@ Printf.sprintf
6892 "Error when typechecking function: %s" (snd
f.f_name
)
6894 | Nast.Constant gc
-> T.Constant
(gconst_def opts gc
)
6895 | Nast.Typedef td
-> T.Typedef
(typedef_def opts td
)
6896 | Nast.Class
c -> begin
6897 match class_def opts
c with
6898 | Some
c -> (T.Class
c)
6899 | None
-> failwith
@@ Printf.sprintf
6900 "Error in declaration of class: %s" (snd
c.c_name
)
6902 (* We don't typecheck top level statements:
6903 * https://docs.hhvm.com/hack/unsupported/top-level
6904 * so just create the minimal env for us to construct a Stmt.
6907 let env = Env.empty opts
Relative_path.default None
in
6908 T.Stmt
(snd
(stmt
env s))
6910 | Nast.NamespaceUse
_
6911 | Nast.SetNamespaceEnv
_
6912 | Nast.FileAttributes
_ ->
6913 failwith
"Invalid nodes in NAST. These nodes should be removed during naming."
6915 Nast_check.program nast
;
6916 let tast = List.map nast
convert_def in
6917 Tast_check.program
tast;