2 * Copyright (c) 2015, Facebook, Inc.
5 * This source code is licensed under the BSD-style license found in the
6 * LICENSE file in the "hack" directory of this source tree. An additional grant
7 * of patent rights can be found in the PATENTS file in the same directory.
11 (* This module implements the typing.
13 * Given an Nast.program, it infers the type of all the local
14 * variables, and checks that all the types are correct (aka
22 module TUtils
= Typing_utils
23 module Reason
= Typing_reason
24 module Inst
= Typing_instantiate
25 module Type
= Typing_ops
26 module Env
= Typing_env
27 module LEnv
= Typing_lenv
28 module Dep
= Typing_deps.Dep
29 module Async
= Typing_async
30 module SubType
= Typing_subtype
31 module Unify
= Typing_unify
32 module TGen
= Typing_generic
33 module SN
= Naming_special_names
34 module TAccess
= Typing_taccess
35 module TS
= Typing_structure
36 module Phase
= Typing_phase
37 module TSubst
= Typing_subst
38 module ExprDepTy
= Typing_dependent_type.ExprDepTy
40 (*****************************************************************************)
42 (*****************************************************************************)
44 (* A guess as to the last position we were typechecking, for use in debugging,
45 * such as figuring out what a runaway hh_server thread is doing. Updated
46 * only best-effort -- it's an approximation to point debugging in the right
47 * direction, nothing more. *)
48 let debug_last_pos = ref Pos.none
49 let debug_print_last_pos _
= print_endline
(Pos.string (Pos.to_absolute
52 (****************************************************************************)
54 (****************************************************************************)
56 let expr_hook = ref None
58 let with_expr_hook hook f
= with_context
59 ~enter
: (fun () -> expr_hook := Some hook
)
60 ~exit
: (fun () -> expr_hook := None
)
63 (*****************************************************************************)
65 (*****************************************************************************)
67 let suggest env p ty
=
68 let ty = Typing_expand.fully_expand env
ty in
69 (match Typing_print.suggest ty with
70 | "..." -> Errors.expecting_type_hint p
71 | ty -> Errors.expecting_type_hint_suggest p
ty
74 let suggest_return env p
ty =
75 let ty = Typing_expand.fully_expand env
ty in
76 (match Typing_print.suggest ty with
77 | "..." -> Errors.expecting_return_type_hint p
78 | ty -> Errors.expecting_return_type_hint_suggest p
ty
81 let any = Reason.Rnone
, Tany
83 let compare_field_kinds x y
=
85 | Nast.AFvalue
(p1
, _
), Nast.AFkvalue
((p2
, _
), _
)
86 | Nast.AFkvalue
((p2
, _
), _
), Nast.AFvalue
(p1
, _
) ->
87 Errors.field_kinds p1 p2
90 let check_consistent_fields x l
=
91 List.iter l
(compare_field_kinds x
)
93 let unbound_name env
(pos
, name
)=
94 (match Env.get_mode env
with
96 Errors.unbound_name_typing pos name
97 | FileInfo.Mdecl
| FileInfo.Mpartial
->
100 env
, (Reason.Rnone
, Tany
)
102 (*****************************************************************************)
103 (* Global constants typing *)
104 (*****************************************************************************)
106 let gconst_decl tcopt cst
=
107 let env = Env.empty tcopt
(Pos.filename
(fst cst
.cst_name
)) in
108 let env = Env.set_mode
env cst
.cst_mode
in
109 let env = Env.set_root
env (Dep.GConst
(snd cst
.cst_name
)) in
111 match cst
.cst_type
with
112 | None
-> env, (Reason.Rnone
, Tany
)
113 | Some h
-> Typing_hint.hint
env h
115 Env.GConsts.add
(snd cst
.cst_name
) hint_ty
117 (*****************************************************************************)
118 (* Handling function/method arguments *)
119 (*****************************************************************************)
121 let rec wfold_left_default f
(env, def1
) l1 l2
=
122 match l1
, def1
, l2
with
125 | [], Some d1
, x2
:: rl2
->
126 let env = f
env d1 x2
in
127 wfold_left_default f
(env, def1
) [] rl2
128 | x1
:: rl1
, _, x2
:: rl2
->
129 let env = f
env x1 x2
in
130 wfold_left_default f
(env, def1
) rl1 rl2
132 (* This function is used to determine the type of an argument.
133 * When we want to type-check the body of a function, we need to
134 * introduce the type of the arguments of the function in the environment
135 * Let's take an example, we want to check the code of foo:
137 * function foo(int $x): int {
138 * // CALL TO make_param_type on (int $x)
139 * // Now we know that the type of $x is int
141 * return $x; // in the environment $x is an int, the code is correct
144 let make_param_type_ ~phase ~for_body ~default ~localize ~var_args
env param
=
145 let param_pos = (fst param
.param_id
) in
147 match param
.param_hint
with
149 (* if the type is missing, use the default one (an unbound
151 let _r, ty = default
() in
152 let r = Reason.Rwitness
param_pos in
154 (* if the code is strict, use the type-hint *)
155 | Some x
when Env.is_strict
env ->
156 let env, ty = Typing_hint.hint
env x
in
158 (* This code is there because we used to be more tolerant in
159 * partial-mode we use to allow (A $x = null) as an argument
160 * instead of (?A $x = null) for the transition, we give this error
161 * message, that explains what's going on, that despite the the (=
162 * null) users are now required to use the optional type (write ?A
164 | Some
(_, (Hoption
_ | Hmixed
) as x
) ->
165 let env, ty = Typing_hint.hint
env x
in
168 match (param
.param_expr
) with
169 | Some
(null_pos
, Null
) ->
170 Errors.nullable_parameter
(fst x
);
171 let env, ty = Typing_hint.hint
env x
in
172 let env, ty = localize
env ty in
173 env, (Reason.Rwitness null_pos
, Toption
ty)
175 let env, ty = Typing_hint.hint
env x
in
178 let ty = match ty with
179 | _, t
when param
.param_is_variadic
&& for_body
->
180 (* when checking the body of a function with a variadic
181 * argument, "f(C ...$args)", $args is an array<C> ... *)
182 let r = Reason.Rvar_param
param_pos in
183 let arr_values = r, t
in
184 var_args
r arr_values
185 | _, t
when param
.param_is_variadic
->
186 (* ... but when checking a call to such a function: "f($a, $b)",
187 * both $a and $b must be of type C *)
188 Reason.Rvar_param
param_pos, t
191 Typing_hooks.dispatch_infer_ty_hook
(phase
ty) param_pos env;
192 env, (Some param
.param_name
, ty)
194 (* externally exposed convenience wrapper *)
195 let make_param_ty env reason param
=
199 ~default
:(fun() -> reason
, Tany
)
200 ~var_args
:(fun r tv
-> r, Tarray
(Some tv
, None
))
201 ~localize
:(fun env ty -> env, ty)
204 (* For params we want to resolve to "static" or "$this" depending on the
205 * context. Even though we are passing in CIstatic, resolve_with_class_id
206 * is smart enough to know what to do. Why do this? Consider the following
209 * abstract const type T;
211 * private this::T $val;
213 * final public function __construct(this::T $x) {
217 * public static function create(this::T $x): this {
218 * return new static($x);
222 * class D extends C { const type T = int; }
224 * In __construct() we want to be able to assign $x to $this->val. The type of
225 * $this->val will expand to '$this::T', so we need $x to also be '$this::T'.
226 * We can do this soundly because when we construct a new class such as,
227 * 'new D(0)' we can determine the late static bound type (D) and resolve
228 * 'this::T' to 'D::T' which is int.
230 * A similar line of reasoning is applied for the static method create.
232 let make_param_local_ty env param
=
234 { (Phase.env_with_self
env) with
235 from_class
= Some CIstatic
; } in
239 ~default
:Env.fresh_type
240 ~localize
:(Phase.localize ~
ety_env)
241 ~var_args
:(fun r tv
-> r, Tarraykind
(AKvec tv
))
244 let rec fun_decl nenv f
=
245 let tcopt = Naming.typechecker_options nenv
in
246 let env = Env.empty
tcopt (Pos.filename
(fst f
.f_name
)) in
247 let env = Env.set_mode
env f
.f_mode
in
248 let env = Env.set_root
env (Dep.Fun
(snd f
.f_name
)) in
249 let _, ft
= fun_decl_in_env
env f
in
250 Env.add_fun
(snd f
.f_name
) ft
;
253 and ret_from_fun_kind pos kind
=
254 let ty_any = (Reason.Rwitness pos
, Tany
) in
257 let r = Reason.Rret_fun_kind
(pos
, kind
) in
258 r, Tapply
((pos
, SN.Classes.cGenerator
), [ty_any ; ty_any ; ty_any])
259 | Ast.FAsyncGenerator
->
260 let r = Reason.Rret_fun_kind
(pos
, kind
) in
261 r, Tapply
((pos
, SN.Classes.cAsyncGenerator
), [ty_any ; ty_any ; ty_any])
263 let r = Reason.Rret_fun_kind
(pos
, kind
) in
264 r, Tapply
((pos
, SN.Classes.cAwaitable
), [ty_any])
265 | Ast.FSync
-> ty_any
267 and fun_decl_in_env
env f
=
268 let mandatory_init = true in
269 let env, arity_min
, params
= make_params
env mandatory_init 0 f
.f_params
in
270 let env, ret_ty
= match f
.f_ret
with
271 | None
-> env, ret_from_fun_kind
(fst f
.f_name
) f
.f_fun_kind
272 | Some
ty -> Typing_hint.hint
env ty in
273 let env, arity
= match f
.f_variadic
with
274 | FVvariadicArg param
->
275 assert param
.param_is_variadic
;
276 assert (param
.param_expr
= None
);
277 let env, (p_name
, p_ty
) = make_param_ty env Reason.Rnone param
in
278 env, Fvariadic
(arity_min
, (p_name
, p_ty
))
279 | FVellipsis
-> env, Fellipsis
(arity_min
)
280 | FVnonVariadic
-> env, Fstandard
(arity_min
, List.length f
.f_params
)
282 let env, tparams
= lfold type_param
env f
.f_tparams
in
284 ft_pos
= fst f
.f_name
;
286 Attributes.deprecated ~kind
:"function" f
.f_name f
.f_user_attributes
;
289 ft_tparams
= tparams
;
295 and type_param
env (variance
, x
, cstr
) =
296 let env, cstr
= match cstr
with
298 let env, ty = Typing_hint.hint
env h
in
300 | None
-> env, None
in
301 env, (variance
, x
, cstr
)
303 and check_default pos mandatory e
=
304 if not mandatory
&& e
= None
305 then Errors.previous_default pos
308 (* Functions building the types for the parameters of a function *)
309 (* It's not completely trivial because of optional arguments *)
310 and make_param
env mandatory arity param
=
311 let env, ty = make_param_ty env Reason.Rnone param
in
313 if param
.param_is_variadic
then begin
314 assert(param
.param_expr
= None
);
317 check_default
(fst param
.param_id
) mandatory param
.param_expr
;
318 mandatory && param
.param_expr
= None
321 let arity = if mandatory then arity + 1 else arity in
322 env, arity, mandatory, ty
324 and make_params
env mandatory arity paraml
=
326 | [] -> env, arity, []
328 let env, arity, mandatory, ty = make_param
env mandatory arity param
in
329 let env, arity, rest
= make_params
env mandatory arity rl
in
330 env, arity, ty :: rest
332 (* In strict mode, we force you to give a type declaration on a parameter *)
333 (* But the type checker is nice: it makes a suggestion :-) *)
334 and check_param
env param
(_, ty) =
335 match (param
.param_hint
) with
336 | None
-> suggest env (fst param
.param_id
) ty
339 and bind_param
env (_, ty1
) param
=
340 let env, ty2
= opt expr
env param
.param_expr
in
341 Option.iter param
.param_expr
Typing_sequencing.sequence_check_expr
;
342 let ty2 = match ty2 with
343 | None
-> Reason.none
, Tany
346 Typing_suggest.save_param
(param
.param_name
) env ty1
ty2;
347 let env = Type.sub_type
(fst param
.param_id
) Reason.URhint
env ty1
ty2 in
348 Env.set_local
env (snd param
.param_id
) ty1
350 and check_memoizable
env param
(pname
, ty) =
351 let env, ty = Env.expand_type
env ty in
352 let p, _ = param
.param_id
in
354 | _, Tprim
(Tarraykey
| Tbool
| Tint
| Tfloat
| Tstring
| Tnum
)
358 | _, Tprim
(Tvoid
| Tresource
| Tnoreturn
) ->
359 let ty_str = Typing_print.error
(snd
ty) in
360 let msgl = Reason.to_string
("This is "^
ty_str) (fst
ty) in
361 Errors.invalid_memoized_param
p msgl
363 check_memoizable
env param
(pname
, ty)
364 | _, Tshape
(_, fdm
) ->
365 ShapeMap.iter
begin fun name
_ ->
366 match ShapeMap.get name fdm
with
367 | Some
ty -> check_memoizable
env param
(pname
, ty)
369 let ty_str = Typing_print.error
(snd
ty) in
370 let msgl = Reason.to_string
("This is "^
ty_str) (fst
ty) in
371 Errors.invalid_memoized_param
p msgl;
374 List.iter tyl
begin fun ty ->
375 check_memoizable
env param
(pname
, ty)
377 | _, Tabstract
(AKenum
_, _) ->
379 | _, Tabstract
(AKnewtype
(_, _), _) ->
381 let ety_env = Phase.env_with_self
env in
382 Typing_tdef.force_expand_typedef ~
ety_env env ty in
383 check_memoizable
env param
(pname
, t'
)
384 (* Just accept all generic types for now. Stricter checks to come later. *)
385 | _, Tabstract
(AKgeneric
_, _) ->
387 (* For parameter type 'this::TID' defined by 'type const TID as Bar' checks
390 | _, Tabstract
(AKdependent
_, Some
ty) ->
391 check_memoizable
env param
(pname
, ty)
392 (* Allow unconstrined dependent type `abstract type const TID` just as we
393 * allow unconstrained generics. *)
394 | _, Tabstract
(AKdependent
_, None
) ->
396 (* Handling Tunresolved case here for completeness, even though it
397 * shouldn't be possible to have an unresolved type when checking
398 * the method declaration. No corresponding test case for this.
400 | _, Tunresolved tyl
->
401 List.iter tyl
begin fun ty ->
402 check_memoizable
env param
(pname
, ty)
404 (* Allow untyped arrays. *)
405 | _, Tarraykind AKany
->
407 | _, Tarraykind
(AKvec
ty)
408 | _, Tarraykind
(AKmap
(_, ty)) ->
409 check_memoizable
env param
(pname
, ty)
410 | _, Tclass
(_, _) ->
411 let type_param = Env.fresh_type
() in
414 Tclass
((Pos.none
, SN.Collections.cContainer
), [type_param]) in
415 let env, is_container
=
418 SubType.sub_type
env container_type ty, true)
419 (fun _ -> env, false) in
421 check_memoizable
env param
(pname
, type_param)
424 let memoizable_type =
425 r, Tclass
((Pos.none
, SN.Classes.cIMemoizeParam
), []) in
426 if SubType.is_sub_type
env memoizable_type ty
429 let ty_str = Typing_print.error
(snd
ty) in
430 let msgl = Reason.to_string
("This is "^
ty_str) (fst
ty) in
431 Errors.invalid_memoized_param
p msgl;
436 let ty_str = Typing_print.error
(snd
ty) in
437 let msgl = Reason.to_string
("This is "^
ty_str) (fst
ty) in
438 Errors.invalid_memoized_param
p msgl
440 (*****************************************************************************)
441 (* Now we are actually checking stuff! *)
442 (*****************************************************************************)
443 and fun_def
env nenv
_ f
=
444 (* reset the expression dependent display ids for each function body *)
445 Reason.expr_display_id_map
:= IMap.empty
;
446 Typing_hooks.dispatch_enter_fun_def_hook f
;
447 let nb = Naming.func_body nenv f
in
448 NastCheck.fun_
env f
nb;
449 (* Fresh type environment is actually unnecessary, but I prefer to
450 * have a guarantee that we are using a clean typing environment. *)
453 let env = { env_up
with Env.lenv
= Env.empty_local
} in
454 let env = Env.set_mode
env f
.f_mode
in
455 let env = Env.set_root
env (Dep.Fun
(snd f
.f_name
)) in
458 | None
-> env, (Reason.Rwitness
(fst f
.f_name
), Tany
)
460 Typing_hint.hint_locl ~ensure_instantiable
:true env ret
in
461 let f_params = match f
.f_variadic
with
462 | FVvariadicArg param
-> param
:: f
.f_params
465 let env = Typing_hint.check_params_instantiable
env f_params in
466 let env = Typing_hint.check_tparams_instantiable
env f
.f_tparams
in
468 lfold
make_param_local_ty env f_params in
469 let env = List.fold2_exn ~f
:bind_param ~init
:env params
f_params in
470 let env = fun_
env hret
(fst f
.f_name
) nb f
.f_fun_kind
in
471 let env = fold_fun_list
env env.Env.todo
in
472 if Env.is_strict
env then begin
473 List.iter2_exn
f_params params
(check_param
env);
475 | None
-> suggest_return env (fst f
.f_name
) hret
479 Typing_hooks.dispatch_exit_fun_def_hook f
481 (*****************************************************************************)
482 (* function used to type closures, functions and methods *)
483 (*****************************************************************************)
485 and fun_ ?
(abstract
=false) env hret pos named_body f_kind
=
486 Env.with_return
env begin fun env ->
487 debug_last_pos := pos
;
488 let env = Env.set_return
env hret
in
489 let env = Env.set_fn_kind
env f_kind
in
490 let env = block
env named_body
.fnb_nast
in
491 Typing_sequencing.sequence_check_block named_body
.fnb_nast
;
492 let ret = Env.get_return
env in
494 if Nast_terminality.Terminal.block
env named_body
.fnb_nast
||
496 named_body
.fnb_unsafe
||
499 else fun_implicit_return
env pos
ret named_body
.fnb_nast f_kind
in
500 debug_last_pos := Pos.none
;
504 and fun_implicit_return
env pos
ret _b
= function
505 | Ast.FGenerator
| Ast.FAsyncGenerator
-> env
507 (* A function without a terminal block has an implicit return; the
509 let rty = Reason.Rno_return pos
, Tprim
Nast.Tvoid
in
510 Typing_suggest.save_return
env ret rty;
511 Type.sub_type pos
Reason.URreturn
env ret rty
513 (* An async function without a terminal block has an implicit return;
514 * the Awaitable<void> type *)
515 let r = Reason.Rno_return_async pos
in
516 let rty = r, Tclass
((pos
, SN.Classes.cAwaitable
), [r, Tprim
Nast.Tvoid
]) in
517 Typing_suggest.save_return
env ret rty;
518 Type.sub_type pos
Reason.URreturn
env ret rty
521 List.fold_left stl ~f
:stmt ~init
:env
523 and stmt
env = function
528 let env, ty = expr
env e
in
529 (* NB: this check does belong here and not in expr, even though it only
530 * applies to expressions -- we actually want to perform the check on
531 * statements that are expressions, e.g., "foo();" we want to check, but
532 * "return foo();" we do not even though the expression "foo()" is a
533 * subexpression of the statement "return foo();". *)
535 | Nast.Binop
(Ast.Eq
_, _, _) -> ()
536 | _ -> Async.enforce_not_awaitable
env (fst e
) ty);
539 let env, ty = expr
env e
in
540 Async.enforce_not_awaitable
env (fst e
) ty;
541 let parent_lenv = env.Env.lenv
in
542 let env = condition
env true e
in
543 let env = block
env b1
in
544 let lenv1 = env.Env.lenv
in
545 let env = { env with Env.lenv
= parent_lenv } in
546 let env = condition
env false e
in
547 let env = block
env b2
in
548 let lenv2 = env.Env.lenv
in
549 let terminal1 = Nast_terminality.Terminal.block
env b1
in
550 let terminal2 = Nast_terminality.Terminal.block
env b2
in
551 if terminal1 && terminal2
553 let env = LEnv.integrate
env parent_lenv lenv1 in
554 let env = LEnv.integrate
env env.Env.lenv
lenv2 in
555 LEnv.integrate
env env.Env.lenv
parent_lenv
558 let env = LEnv.integrate
env parent_lenv lenv1 in
559 LEnv.integrate
env env.Env.lenv
lenv2
563 let env = LEnv.integrate
env parent_lenv lenv2 in
564 LEnv.integrate
env env.Env.lenv
lenv1
566 else LEnv.intersect
env parent_lenv lenv1 lenv2
567 | Return
(p, None
) ->
568 let rty = match Env.get_fn_kind
env with
569 | Ast.FSync
-> (Reason.Rwitness
p, Tprim Tvoid
)
571 | Ast.FAsyncGenerator
-> any (* Return type checked against the "yield". *)
572 | Ast.FAsync
-> (Reason.Rwitness
p, Tclass
((p, SN.Classes.cAwaitable
), [(Reason.Rwitness
p, Tprim Tvoid
)])) in
573 let expected_return = Env.get_return
env in
574 Typing_suggest.save_return
env expected_return rty;
575 let env = Type.sub_type
p Reason.URreturn
env expected_return rty in
577 | Return
(p, Some e
) ->
579 let env, rty = expr
env e
in
580 let rty = match Env.get_fn_kind
env with
583 | Ast.FAsyncGenerator
-> any (* Is an error, but caught in NastCheck. *)
584 | Ast.FAsync
-> (Reason.Rwitness
p), Tclass
((p, SN.Classes.cAwaitable
), [rty]) in
585 let expected_return = Env.get_return
env in
586 (match snd
(Env.expand_type
env expected_return) with
588 (* Yell about returning a value from a void function. This catches
589 * more issues than just unifying with void would do -- in particular
590 * just unifying allows you to return a Tany from a void function,
591 * which is clearly wrong. Note this check is best-effort; if the
592 * function returns a generic type which later ends up being Tvoid
593 * then there's not much we can do here. *)
594 Errors.return_in_void
p (Reason.to_pos
r);
596 | _, Tunresolved
_ ->
597 (* we allow return types to grow for anonymous functions *)
598 let env, rty = TUtils.unresolved
env rty in
599 let env, _ = Type.unify
pos Reason.URreturn
env expected_return rty in
601 | _, (Tany
| Tmixed
| Tarraykind
_ | Toption
_ | Tprim
_
602 | Tvar
_ | Tfun
_ | Tabstract
(_, _) | Tclass
(_, _) | Ttuple
_
603 | Tanon
(_, _) | Tobject
| Tshape
_) ->
604 Typing_suggest.save_return
env expected_return rty;
605 let env = Type.sub_type
pos Reason.URreturn
env expected_return rty in
609 (* NOTE: leaks scope as currently implemented; this matches
610 the behavior in naming (cf. `do_stmt` in naming/naming.ml).
612 let parent_lenv = env.Env.lenv
in
613 let env = Env.freeze_local_env
env in
614 let env = block
env b
in
615 let env, ty = expr
env e
in
616 Async.enforce_not_awaitable
env (fst e
) ty;
617 let after_block = env.Env.lenv
in
619 if env.Env.in_loop
then 1 else Typing_alias.get_depth st
in
620 let env = Env.in_loop
env begin
621 iter_n_acc
alias_depth begin fun env ->
622 let env = condition
env true e
in
623 let env = block
env b
in
628 if NastVisitor.HasContinue.block b
629 then LEnv.fully_integrate
env parent_lenv
631 let env = LEnv.integrate
env parent_lenv env.Env.lenv
in
632 let env = { env with Env.lenv
= after_block } in
635 condition
env false e
636 | While
(e
, b
) as st
->
637 let env, ty = expr
env e
in
638 Async.enforce_not_awaitable
env (fst e
) ty;
639 let parent_lenv = env.Env.lenv
in
640 let env = Env.freeze_local_env
env in
642 if env.Env.in_loop
then 1 else Typing_alias.get_depth st
in
643 let env = Env.in_loop
env begin
644 iter_n_acc
alias_depth begin fun env ->
645 let env = condition
env true e
in
646 let env = block
env b
in
650 let env = LEnv.fully_integrate
env parent_lenv in
651 condition
env false e
652 | For
(e1
, e2
, e3
, b
) as st
->
653 (* For loops leak their initalizer, but nothing that's defined in the
656 let (env, _) = expr
env e1
in (* initializer *)
657 let (env, _) = expr
env e2
in
658 let parent_lenv = env.Env.lenv
in
659 let env = Env.freeze_local_env
env in
661 if env.Env.in_loop
then 1 else Typing_alias.get_depth st
in
662 let env = Env.in_loop
env begin
663 iter_n_acc
alias_depth begin fun env ->
664 let env = condition
env true e2
in (* iteration 0 *)
665 let env = block
env b
in
666 let (env, _) = expr
env e3
in
670 let env = LEnv.fully_integrate
env parent_lenv in
671 condition
env false e2
673 Nast_terminality.SafeCase.check
(fst e
) env cl
;
674 let env, ty = expr
env e
in
675 Async.enforce_not_awaitable
env (fst e
) ty;
676 let env = check_exhaustiveness
env (fst e
) ty cl
in
677 let parent_lenv = env.Env.lenv
in
678 let env, cl
= case_list
parent_lenv ty env cl
in
679 LEnv.intersect_list
env parent_lenv cl
680 | Foreach
(e1
, e2
, b
) as st
->
681 let env, ty1
= expr
env e1
in
682 let env, ty1
= TUtils.fold_unresolved
env ty1
in
683 let env, ety1
= Env.expand_type
env ty1
in
684 let parent_lenv = env.Env.lenv
in
685 let env = Env.freeze_local_env
env in
686 let env, ty2 = as_expr
env (fst e1
) e2
in
687 let env = Type.sub_type
(fst e1
) Reason.URforeach
env ty2 ety1
in
689 if env.Env.in_loop
then 1 else Typing_alias.get_depth st
in
690 let env = Env.in_loop
env begin
691 iter_n_acc
alias_depth begin fun env ->
692 let env = bind_as_expr
env ty2 e2
in
693 let env = block
env b
in
697 let env = LEnv.fully_integrate
env parent_lenv in
699 | Try
(tb
, cl
, fb
) ->
700 let env = try_catch
(tb
, cl
) env in
701 let env = block
env fb
in
704 let env = List.fold_left el ~f
:begin fun env e
->
706 | _, Binop
(Ast.Eq
_, (_, Lvar
(p, x
)), _) ->
707 Env.add_todo
env (TGen.no_generic
p x
)
710 let env, _ = lfold expr
env el
in
714 let env, ty = expr
env e
in
715 exception_ty
p env ty
719 and check_exhaustiveness
env pos ty caselist
=
720 (* Right now we only do exhaustiveness checking for enums. *)
721 let env, (_, ty) = Env.expand_type
env ty in
724 List.fold_left tyl ~init
:env ~f
:begin fun env ty ->
725 check_exhaustiveness
env pos ty caselist
727 | Tabstract
(AKenum id
, _) ->
728 let tc = unsafe_opt
@@ Env.get_enum id
in
729 Typing_enum.check_enum_exhaustiveness
pos tc caselist
;
731 | Tany
| Tmixed
| Tarraykind
_ | Tclass
_ | Toption
_ | Tprim
_
732 | Tvar
_ | Tfun
_ | Tabstract
(_, _) | Ttuple
_ | Tanon
(_, _)
733 | Tobject
| Tshape
_ -> env
735 and case_list
parent_lenv ty env cl
=
736 let env = { env with Env.lenv
= parent_lenv } in
737 case_list_
parent_lenv ty env cl
739 and try_catch
(tb
, cl
) env =
740 let parent_lenv = env.Env.lenv
in
741 let env = Env.freeze_local_env
env in
742 let env = block
env tb
in
743 let after_try = env.Env.lenv
in
744 let env, term_lenv_l
= lmap
begin fun env (_, _, b
as catch_block
) ->
745 let env, lenv
= catch
parent_lenv after_try env catch_block
in
746 let term = Nast_terminality.Terminal.block
env b
in
750 (Nast_terminality.Terminal.block
env tb
, after_try) :: term_lenv_l in
751 LEnv.intersect_list
env parent_lenv term_lenv_l
753 and case_list_
parent_lenv ty env = function
756 (* TODO this is wrong, should continue on to the other cases, but it
757 * doesn't matter in practice since our parser won't parse default
758 * anywhere but in the last position :) Should fix all of this as well
759 * as totality detection for switch. *)
760 let env = block
env b
in
761 env, [Nast_terminality.Terminal.case
env (Default b
), env.Env.lenv
]
762 | Case
(e
, b
) :: rl
->
763 (* TODO - we should consider handling the comparisons the same
764 * way as Binop Ast.EqEq, since case statements work using ==
765 * comparison rules *)
767 (* The way we handle terminal/nonterminal here is not quite right, you
768 * can still break the type system with things like P3131824. *)
769 let ty_num = (Reason.Rnone
, Tprim
Nast.Tnum
) in
770 let ty_arraykey = (Reason.Rnone
, Tprim
Nast.Tarraykey
) in
771 let both_are_sub_types env tprim ty1
ty2 =
772 (SubType.is_sub_type
env tprim ty1
) &&
773 (SubType.is_sub_type
env tprim
ty2) in
774 if Nast_terminality.Terminal.block
env b
then
775 let env, ty2 = expr
env e
in
777 if (both_are_sub_types env ty_num ty ty2) ||
778 (both_are_sub_types env ty_arraykey ty ty2)
780 else Type.unify
(fst e
) Reason.URnone
env ty ty2 in
781 let env = block
env b
in
782 let lenv = env.Env.lenv in
783 let env, rl
= case_list
parent_lenv ty env rl
in
784 env, (Nast_terminality.Terminal.case
env (Case
(e
, b
)), lenv) :: rl
786 let env, ty2 = expr
env e
in
788 if (both_are_sub_types env ty_num ty ty2) ||
789 (both_are_sub_types env ty_arraykey ty ty2)
791 else Type.unify
(fst e
) Reason.URnone
env ty ty2 in
792 (* Since this block is not terminal we will end up falling through to the
793 * next block. This means the lenv will include what our current
794 * environment is, intersected (or integrated?) with the environment
795 * after executing the block. Example:
797 * $x = 0; // $x = int
800 * $x = ''; // $x = string
803 * $x; // $x = int & string
806 let lenv1 = env.Env.lenv in
807 let env = block
env b
in
808 (* PERF: If the case is empty or a Noop then we do not need to intersect
809 * the lenv since they will be the same.
811 * This saves the cost of intersecting the lenv for the common pattern of
817 let env = match b
with
819 | _ -> LEnv.intersect
env parent_lenv lenv1 env.Env.lenv in
820 case_list_
parent_lenv ty env rl
822 and catch
parent_lenv after_try env (ety
, exn
, b
) =
823 let env = { env with Env.lenv = after_try } in
824 let env = LEnv.fully_integrate
env parent_lenv in
826 let ety_p = (fst ety
) in
827 let env, _ = instantiable_cid
ety_p env cid in
828 let env, ety
= static_class_id
ety_p env cid in
829 let env = exception_ty
ety_p env ety
in
830 let env = Env.set_local
env (snd exn
) ety
in
831 let env = block
env b
in
832 (* Only keep the local bindings if this catch is non-terminal *)
835 and as_expr
env pe
= function
837 let ty = Env.fresh_type
() in
838 let tvector = Tclass
((pe
, SN.Collections.cTraversable
), [ty]) in
839 env, (Reason.Rforeach pe
, tvector)
841 let ty1 = Env.fresh_type
() in
842 let ty2 = Env.fresh_type
() in
843 let tmap = Tclass
((pe
, SN.Collections.cKeyedTraversable
), [ty1; ty2]) in
844 env, (Reason.Rforeach pe
, tmap)
846 let ty = Env.fresh_type
() in
847 let tvector = Tclass
((pe
, SN.Classes.cAsyncIterator
), [ty]) in
848 env, (Reason.Rasyncforeach pe
, tvector)
850 let ty1 = Env.fresh_type
() in
851 let ty2 = Env.fresh_type
() in
852 let tmap = Tclass
((pe
, SN.Classes.cAsyncKeyedIterator
), [ty1; ty2]) in
853 env, (Reason.Rasyncforeach pe
, tmap)
855 and bind_as_expr
env ty aexpr
=
856 let env, ety
= Env.expand_type
env ty in
858 | _, Tclass
((p, _), [ty2]) ->
861 | Await_as_v
(_, ev
) -> fst
(assign
p env ev
ty2)
862 | As_kv
((_, Lvar
(_, k
)), ev
)
863 | Await_as_kv
(_, (_, Lvar
(_, k
)), ev
) ->
864 let env, _ = set_valid_rvalue
p env k
(Reason.Rnone
, Tmixed
) in
865 fst
(assign
p env ev
ty2)
866 | _ -> (* TODO Probably impossible, should check that *)
869 | _, Tclass
((p, _), [ty1; ty2]) ->
872 | Await_as_v
(_, ev
) -> fst
(assign
p env ev
ty2)
873 | As_kv
((_, Lvar
(_, k
)), ev
)
874 | Await_as_kv
(_, (_, Lvar
(_, k
)), ev
) ->
875 let env, _ = set_valid_rvalue
p env k
ty1 in
876 fst
(assign
p env ev
ty2)
877 | _ -> (* TODO Probably impossible, should check that *)
880 | _, (Tany
| Tmixed
| Tarraykind
_ | Toption
_ | Tprim
_
881 | Tvar
_ | Tfun
_ | Tabstract
(_, _) | Tclass
(_, _) | Ttuple
_
882 | Tanon
(_, _) | Tunresolved
_ | Tobject
| Tshape
_
886 raw_expr ~in_cond
:false env e
888 and raw_expr ~in_cond
env e
=
889 debug_last_pos := fst e
;
890 let valkind = `other
in
891 let env, ty = expr_ ~in_cond ~
valkind env e
in
892 let () = match !expr_hook with
893 | Some f
-> f e
(Typing_expand.fully_expand
env ty)
895 Typing_hooks.dispatch_infer_ty_hook
(LoclTy
ty) (fst e
) env;
899 let valkind = `lvalue
in
900 expr_ ~in_cond
:false ~
valkind env e
902 and expr_ ~in_cond ~
(valkind: [> `lvalue
| `rvalue
| `other
]) env (p, e
) =
904 | Any
-> env, (Reason.Rwitness
p, Tany
)
905 | Array
[] -> env, (Reason.Rwitness
p, Tarraykind AKany
)
906 | Array
(x
:: rl
as l
) ->
907 check_consistent_fields x rl
;
908 let env, value = TUtils.in_var
env (Reason.Rnone
, Tunresolved
[]) in
910 fold_left_env
(apply_for_env_fold array_field_value
) env [] l
in
911 let has_unknown = List.exists values
(fun (_, ty) -> ty = Tany
) in
913 fold_left_env
(apply_for_env_fold
TUtils.unresolved
) env [] values
in
914 let unify_value = Type.unify
p Reason.URarray_value
in
916 if has_unknown (* If one of the values comes from PHP land,
917 * we have to be conservative and consider that
918 * we don't know what the type of the values are.
920 then env, (Reason.Rnone
, Tany
)
921 else fold_left_env
unify_value env value values
925 env, (Reason.Rwitness
p, Tarraykind
(AKvec
value))
927 let env, key
= TUtils.in_var
env (Reason.Rnone
, Tunresolved
[]) in
929 fold_left_env
(apply_for_env_fold array_field_key
) env [] l
in
931 fold_left_env
(apply_for_env_fold
TUtils.unresolved
) env [] keys
in
932 let unify_key = Type.unify
p Reason.URarray_key
in
933 let env, key
= fold_left_env
unify_key env key keys
in
934 env, (Reason.Rwitness
p, Tarraykind
(AKmap
(key
, value)))
936 | ValCollection
(name
, el
) ->
937 let env, x
= TUtils.in_var
env (Reason.Rnone
, Tunresolved
[]) in
938 let env, tyl
= lmap expr
env el
in
939 let env, tyl
= lmap
Typing_env.unbind
env tyl
in
940 let env, tyl
= lfold
TUtils.unresolved
env tyl
in
941 let env, v
= fold_left_env
(Type.unify
p Reason.URvector
) env x tyl
in
942 let tvector = Tclass
((p, name
), [v
]) in
943 let ty = Reason.Rwitness
p, tvector in
945 | KeyValCollection
(name
, l
) ->
946 let kl, vl
= List.unzip l
in
947 let env, kl = lfold expr
env kl in
948 let env, kl = lmap
Typing_env.unbind
env kl in
949 let env, vl
= lfold expr
env vl
in
950 let env, vl
= lmap
Typing_env.unbind
env vl
in
951 let env, k
= TUtils.in_var
env (Reason.Rnone
, Tunresolved
[]) in
952 let env, v
= TUtils.in_var
env (Reason.Rnone
, Tunresolved
[]) in
953 let env, kl = lfold
TUtils.unresolved
env kl in
954 let env, k
= fold_left_env
(Type.unify
p Reason.URkey
) env k
kl in
955 let env, vl
= lfold
TUtils.unresolved
env vl
in
956 let env, v
= fold_left_env
(Type.unify
p Reason.URvalue
) env v vl
in
957 let ty = Tclass
((p, name
), [k
; v
])
959 env, (Reason.Rwitness
p, ty)
960 | Clone e
-> expr
env e
961 | This
when Env.is_static
env ->
962 Errors.this_in_static
p;
963 env, (Reason.Rwitness
p, Tany
)
964 | This
when valkind = `lvalue
->
965 Errors.this_lvalue
p;
966 env, (Reason.Rwitness
p, Tany
)
968 let r, _ = Env.get_self
env in
970 then Errors.this_var_outside_class
p;
971 let env, (_, ty) = Env.get_local
env this
in
972 let r = Reason.Rwitness
p in
974 let ty = r, TUtils.this_of
ty in
975 (* '$this' always refers to the late bound static type *)
976 env, ExprDepTy.make
env CIstatic
ty
977 | Assert
(AE_assert e
) ->
978 let env = condition
env true e
in
979 env, (Reason.Rwitness
p, Tprim Tvoid
)
982 env, (Reason.Rwitness
p, Tprim Tbool
)
984 env, (Reason.Rwitness
p, Tprim Tint
)
986 env, (Reason.Rwitness
p, Tprim Tfloat
)
988 let ty = Env.fresh_type
() in
989 env, (Reason.Rwitness
p, Toption
ty)
991 env, (Reason.Rwitness
p, Tprim Tstring
)
993 let env = string2
env idl
in
994 env, (Reason.Rwitness
p, Tprim Tstring
)
996 Typing_hooks.dispatch_id_hook x
env;
997 let env, fty
= fun_type_of_id
env x
in
999 | _, Tfun fty
-> check_deprecated
(fst x
) fty
;
1003 | Id
((cst_pos
, cst_name
) as id
) ->
1004 Typing_hooks.dispatch_id_hook id
env;
1005 (match Env.get_gconst
env cst_name
with
1006 | None
when Env.is_strict
env ->
1007 Errors.unbound_global cst_pos
;
1008 env, (Reason.Rwitness cst_pos
, Tany
)
1010 env, (Reason.Rnone
, Tany
)
1012 Phase.localize_with_self
env ty
1014 | Method_id
(instance
, meth
) ->
1015 (* Method_id is used when creating a "method pointer" using the magic
1016 * inst_meth function.
1018 * Typing this is pretty simple, we just need to check that instance->meth
1019 * is public+not static and then return its type.
1021 let env, ty1 = expr
env instance
in
1022 let env, result
, vis
=
1023 obj_get_with_visibility ~is_method
:true ~nullsafe
:None
env ty1
1024 (CIexpr instance
) meth
(fun x
-> x
) in
1025 let has_lost_info = Env.FakeMembers.is_invalid
env instance
(snd meth
) in
1028 let name = "the method "^snd meth
in
1029 let env, result
= Env.lost_info
name ISet.empty
env result
in
1034 | _, Tfun fty
-> check_deprecated
p fty
1037 | Some
(method_pos
, Vprivate
_) ->
1038 Errors.private_inst_meth method_pos
p
1039 | Some
(method_pos
, Vprotected
_) ->
1040 Errors.protected_inst_meth method_pos
p
1045 | Method_caller
((pos, class_name
) as pos_cname
, meth_name
) ->
1046 (* meth_caller('X', 'foo') desugars to:
1049 let class_ = Env.get_class
env class_name
in
1051 | None
-> unbound_name env pos_cname
1053 (* Create a class type for the given object instantiated with unresolved
1054 * types for its type parameters.
1056 let env, tvarl
= lfold
TUtils.unresolved_tparam
env class_.tc_tparams
in
1057 let params = List.map
class_.tc_tparams
begin fun (_, (p, n
), cstr
) ->
1058 Reason.Rwitness
p, Tgeneric
(n
, cstr
)
1060 let obj_type = Reason.Rwitness
p, Tapply
(pos_cname
, params) in
1062 (Phase.env_with_self
env) with
1063 substs
= TSubst.make
class_.tc_tparams tvarl
;
1065 let env, local_obj_ty
= Phase.localize ~
ety_env env obj_type in
1067 obj_get ~is_method
:true ~nullsafe
:None
env local_obj_ty
1068 (CI
(pos, class_name
)) meth_name
(fun x
-> x
) in
1070 | reason
, Tfun fty
->
1071 check_deprecated
p fty
;
1072 (* We are creating a fake closure:
1073 * function<T as Class>(T $x): return_type_of(Class:meth_name)
1075 let tparam = Ast.Invariant
, pos_cname
, Some
(Ast.Constraint_as
, obj_type) in
1076 let env, tvar
= TUtils.unresolved_tparam
env tparam in
1077 let param = Reason.Rwitness
pos,
1078 Tgeneric
(class_name
, Some
(Ast.Constraint_as
, obj_type)) in
1081 substs
= TSubst.make
(tparam :: class_.tc_tparams
) (tvar
:: tvarl
)
1083 let env, param = Phase.localize ~
ety_env env param in
1084 let fty = { fty with
1085 ft_params
= (None
, param) :: fty.ft_params
} in
1086 let fun_arity = match fty.ft_arity
with
1087 | Fstandard
(min
, max
) -> Fstandard
(min
+ 1, max
+ 1)
1088 | Fvariadic
(min
, x
) -> Fvariadic
(min
+ 1, x
)
1089 | Fellipsis min
-> Fellipsis
(min
+ 1) in
1092 ft_deprecated
= None
;
1093 ft_abstract
= false;
1094 ft_arity
= fun_arity;
1095 ft_tparams
= fty.ft_tparams
;
1096 ft_params
= fty.ft_params
;
1097 ft_ret
= fty.ft_ret
;
1099 env, (reason
, Tfun
caller)
1101 (* This can happen if the method lives in PHP *)
1102 env, (Reason.Rwitness
pos, Tany
)
1105 | Smethod_id
(c
, meth
) ->
1106 (* Smethod_id is used when creating a "method pointer" using the magic
1107 * class_meth function.
1109 * Typing this is pretty simple, we just need to check that c::meth is
1110 * public+static and then return its type.
1112 let class_ = Env.get_class
env (snd c
) in
1115 (* The class given as a static string was not found. *)
1118 let smethod = Env.get_static_member
true env class_ (snd meth
) in
1120 | None
-> (* The static method wasn't found. *)
1121 smember_not_found
p ~is_const
:false ~is_method
:true class_ (snd meth
);
1122 env, (Reason.Rnone
, Tany
)
1125 let env, cid_ty
= static_class_id
(fst c
) env cid in
1127 type_expansions
= [];
1128 substs
= SMap.empty
;
1130 from_class
= Some
cid;
1132 let env, smethod_type
= Phase.localize ~
ety_env env smethod.ce_type
in
1133 (match smethod_type
with
1134 | _, Tfun
fty -> check_deprecated
p fty
1136 (match smethod_type
, smethod.ce_visibility
with
1137 | (r, (Tfun
_ as ty)), Vpublic
->
1139 | (r, Tfun
_), Vprivate
_ ->
1140 Errors.private_class_meth
(Reason.to_pos
r) p;
1142 | (r, Tfun
_), Vprotected
_ ->
1143 Errors.protected_class_meth
(Reason.to_pos
r) p;
1146 (* If this assert fails, we have a method which isn't callable. *)
1152 let r = Reason.Rplaceholder
p in
1153 let ty = r, Tprim Tvoid
in
1155 | Lvar
((_, x
) as id
) ->
1156 Typing_hooks.dispatch_lvar_hook id
env;
1157 let env, x
= Env.get_local
env x
in
1160 let env, tyl
= lmap expr
env el
in
1161 let ty = Reason.Rwitness
p, Ttuple tyl
in
1164 let env, ty1 = expr
env e1
in
1165 let env, ty2 = expr
env e2
in
1166 let ty = Reason.Rwitness
p, Tclass
((p, SN.Collections.cPair
), [ty1; ty2]) in
1169 let env, tyl
= lmap expr
env el
in
1170 let ty = Reason.Rwitness
p, Ttuple tyl
in
1172 | Array_get
(e
, None
) ->
1173 let env, ty1 = expr
env e
in
1174 let is_lvalue = (valkind == `lvalue
) in
1175 array_append
is_lvalue p env ty1
1176 | Array_get
(e1
, Some e2
) ->
1177 let env, ty1 = expr
env e1
in
1178 let env, ty1 = TUtils.fold_unresolved
env ty1 in
1179 let env, ety1
= Env.expand_type
env ty1 in
1180 let env, ty2 = expr
env e2
in
1181 let is_lvalue = (valkind == `lvalue
) in
1182 array_get
is_lvalue p env ty1 ety1 e2
ty2
1183 | Call
(Cnormal
, (_, Id
(_, hh_show
)), [x
], [])
1184 when hh_show
= SN.PseudoFunctions.hh_show
->
1185 let env, ty = expr
env x
in
1187 env, Env.fresh_type
()
1188 | Call
(call_type
, e
, el
, uel
) ->
1189 let env, result
= dispatch_call
p env call_type e el uel
in
1190 let env = Env.forget_members
env p in
1192 | Binop
(Ast.Eq
(Some op
), e1
, e2
) ->
1193 let e2 = p, Binop
(op
, e1
, e2) in
1194 let env, ty = raw_expr in_cond
env (p, Binop
(Ast.Eq None
, e1
, e2)) in
1196 | Binop
(Ast.Eq None
, e1
, e2) ->
1197 let env, ty2 = raw_expr in_cond
env e2 in
1198 let env, ty = assign
p env e1
ty2 in
1199 (* If we are assigning a local variable to another local variable then
1200 * the expression ID associated with e2 is transferred to e1
1203 | (_, Lvar
(_, x1
)), (_, Lvar
(_, x2
)) ->
1204 let eid2 = Env.get_local_expr_id
env x2
in
1208 ~f
:(Env.set_local_expr_id
env x1
) in
1212 | Binop
((Ast.AMpamp
| Ast.BArbar
as bop
), e1
, e2) ->
1213 let c = bop
= Ast.AMpamp
in
1214 let lenv = env.Env.lenv in
1215 let env, ty1 = expr
env e1
in
1216 let env = condition
env c e1
in
1217 let env, ty2 = raw_expr in_cond
env e2 in
1218 let env = { env with Env.lenv = lenv } in
1219 Typing_hooks.dispatch_binop_hook
p bop
ty1 ty2;
1220 env, (Reason.Rlogic_ret
p, Tprim Tbool
)
1221 | Binop
(bop
, e
, (_, Null
))
1222 | Binop
(bop
, (_, Null
), e
)
1223 when Env.is_strict
env && (bop
= Ast.EQeqeq
|| bop
= Ast.Diff2
) ->
1224 let _, ty = raw_expr in_cond
env e
in
1226 then TypingEqualityCheck.assert_nullable
p bop
env ty;
1227 env, (Reason.Rcomp
p, Tprim Tbool
)
1228 | Binop
(bop
, e1
, e2) ->
1229 let env, ty1 = raw_expr in_cond
env e1
in
1230 let env, ty2 = raw_expr in_cond
env e2 in
1231 let env, ty = binop in_cond
p env bop
(fst e1
) ty1 (fst
e2) ty2 in
1232 Typing_hooks.dispatch_binop_hook
p bop
ty1 ty2;
1235 let env, ty = raw_expr in_cond
env e
in
1237 | Eif
(c, e1
, e2) ->
1238 let env, tyc
= raw_expr in_cond
env c in
1239 Async.enforce_not_awaitable
env (fst
c) tyc
;
1240 let _, parent_locals
as lenv = env.Env.lenv in
1241 let env = condition
env true c in
1242 let env, ty1 = match e1
with
1248 let fake1, _locals1
= env.Env.lenv in
1249 let env = { env with Env.lenv = lenv } in
1250 let env = condition
env false c in
1251 let env, ty2 = expr
env e2 in
1252 let fake2, _locals2
= env.Env.lenv in
1253 let fake_members = LEnv.intersect_fake
fake1 fake2 in
1254 (* we restore the locals to their parent state so as not to leak the
1255 * effects of the `condition` calls above *)
1256 let env = { env with Env.lenv = fake_members, parent_locals
} in
1257 (* This is a shortened form of what we do in Typing_lenv.intersect. The
1258 * latter takes local environments as arguments, but our types here
1259 * aren't assigned to local variables in an environment *)
1260 let env, ty1 = TUtils.unresolved
env ty1 in
1261 let env, ty2 = TUtils.unresolved
env ty2 in
1262 Unify.unify
env ty1 ty2
1263 | NullCoalesce
(e1
, e2) ->
1264 (* Desugar `$a ?? $b` into `$a !== null ? $a : $b` *)
1265 let c = (p, Binop
(Ast.Diff2
, e1
, (p, Null
))) in
1266 let eif = (p, Eif
(c, Some e1
, e2)) in
1268 | Class_const
(cid, mid
) -> class_const
env p (cid, mid
)
1269 | Class_get
(x
, (_, y
))
1270 when Env.FakeMembers.get_static
env x y
<> None
->
1271 let env, local
= Env.FakeMembers.make_static
p env x y
in
1272 let local = p, Lvar
(p, local) in
1274 | Class_get
(cid, mid
) ->
1275 TUtils.process_static_find_ref
cid mid
;
1276 let env, cty
= static_class_id
p env cid in
1277 let env, cty
= Env.expand_type
env cty
in
1278 let env, ty = class_get ~is_method
:false ~is_const
:false env cty mid
cid in
1279 if Env.FakeMembers.is_static_invalid
env cid (snd mid
)
1281 let fake_name = Env.FakeMembers.make_static_id
cid (snd mid
) in
1282 let env, ty = Env.lost_info
fake_name ISet.empty
env ty in
1285 | Obj_get
(e
, (_, Id
(_, y
)), _)
1286 when Env.FakeMembers.get
env e y
<> None
->
1287 let env, local = Env.FakeMembers.make
p env e y
in
1288 let local = p, Lvar
(p, local) in
1290 | Obj_get
(e1
, (_, Id m
), nullflavor
) ->
1292 (match nullflavor
with
1293 | OG_nullthrows
-> None
1294 | OG_nullsafe
-> Some
p
1296 let env, ty1 = expr
env e1
in
1298 obj_get ~is_method
:false ~
nullsafe env ty1 (CIexpr e1
) m
(fun x
-> x
) in
1299 let has_lost_info = Env.FakeMembers.is_invalid
env e1
(snd m
) in
1302 let name = "the member "^snd m
in
1303 let env, result
= Env.lost_info
name ISet.empty
env result
in
1306 | Obj_get
(e1
, _, _) ->
1307 let env, _ = expr
env e1
in
1308 env, (Reason.Rwitness
p, Tany
)
1310 env, (Reason.Rwitness
p, Tany
)
1312 let env, key
= yield_field_key
env af
in
1313 let env, value = yield_field_value
env af
in
1314 let send = Env.fresh_type
() in
1315 let rty = match Env.get_fn_kind
env with
1317 Reason.Ryield_gen
p,
1318 Tclass
((p, SN.Classes.cGenerator
), [key
; value; send])
1319 | Ast.FAsyncGenerator
->
1320 Reason.Ryield_asyncgen
p,
1321 Tclass
((p, SN.Classes.cAsyncGenerator
), [key
; value; send])
1322 | Ast.FSync
| Ast.FAsync
->
1323 failwith
"Parsing should never allow this" in
1325 Type.sub_type
p (Reason.URyield
) env (Env.get_return
env) rty in
1326 let env = Env.forget_members
env p in
1327 env, (Reason.Ryield_send
p, Toption
send)
1329 let env, rty = expr
env e
in
1330 Async.overload_extract_from_awaitable
env p rty
1331 | Special_func func
-> special_func
env p func
1332 | New
(c, el
, uel
) ->
1333 Typing_hooks.dispatch_new_id_hook
c env p;
1334 TUtils.process_static_find_ref
c (p, SN.Members.__construct
);
1335 let check_not_abstract = true in
1336 let env, ty = new_object ~
check_not_abstract p env c el uel
in
1337 let env = Env.forget_members
env p in
1338 env, ExprDepTy.make
env c ty
1339 | Cast
((_, Harray
(None
, None
)), _) when Env.is_strict
env ->
1340 Errors.array_cast
p;
1341 env, (Reason.Rwitness
p, Tany
)
1343 let env, _ = expr
env e
in
1344 Typing_hint.hint_locl
env ty
1345 | InstanceOf
(e
, cid) ->
1346 let env, _ = expr
env e
in
1347 let env, _class
= instantiable_cid
p env cid in
1348 env, (Reason.Rwitness
p, Tprim Tbool
)
1350 let env, ft = fun_decl_in_env
env f
in
1351 (* When creating a closure, the 'this' type will mean the late bound type
1352 * of the current enclosing class
1355 { (Phase.env_with_self
env) with from_class
= Some CIstatic
} in
1356 let env, ft = Phase.localize_ft ~
ety_env env ft in
1357 (* check for recursive function calls *)
1358 let anon = anon_make
env p f
in
1359 let env, anon_id
= Env.add_anonymous
env anon in
1360 let env = Errors.try_with_error
1362 ignore
(anon env ft.ft_params
); env)
1364 (* If the anonymous function declaration has errors itself, silence
1365 them in any subsequent usages. *)
1366 let anon env fun_params
=
1367 Errors.ignore_
(fun () -> (anon env fun_params
)) in
1368 Env.set_anonymous
env anon_id
anon) in
1369 env, (Reason.Rwitness
p, Tanon
(ft.ft_arity
, anon_id
))
1370 | Xml
(sid
, attrl
, el
) ->
1372 let env, obj
= expr
env (fst sid
, New
(cid, [], [])) in
1373 let env, attr_ptyl
= lmap
begin fun env attr
->
1374 (* Typecheck the expressions - this just checks that the expressions are
1375 * valid, not that they match the declared type for the attribute *)
1376 let namepstr, valexpr
= attr
in
1377 let valp, _ = valexpr
in
1378 let env, valty
= expr
env valexpr
in
1379 env, (namepstr, (valp, valty
))
1381 let env, _body
= lfold expr
env el
in
1382 let env, class_ = class_id_for_new
p env cid in
1384 | None
-> env, (Reason.Runknown_class
p, Tobject
)
1385 | Some
(_, class_, _) ->
1386 if TypecheckerOptions.unsafe_xhp
(Env.get_options
env) then
1389 (* Check that the declared type of the XHP attribute matches the
1390 * expression type *)
1392 SMap.filter
(fun _ prop
-> prop
.ce_is_xhp_attr
) class_.tc_props
in
1393 let env = List.fold_left attr_ptyl ~f
:begin fun env attr
->
1394 let namepstr, valpty
= attr
in
1395 let valp, valty
= valpty
in
1396 (* We pretend that XHP attributes are stored as member variables,
1397 * prefixed with a colon.
1399 * This converts the member name to an attribute name. *)
1400 let name = ":" ^
(snd
namepstr) in
1401 let elt_option = SMap.get
name attrdec in
1402 (match elt_option with
1404 let env, declty
= Phase.localize_with_self
env elt
.ce_type
in
1405 let env = Type.sub_type
valp Reason.URxhp
env declty valty
in
1407 | None
when SN.Members.is_special_xhp_attribute
name -> env
1408 (* Special attributes are valid even if they're not declared - eg
1409 * any 'data-' attribute *)
1411 let r = (Reason.Rwitness
p) in
1412 member_not_found
(fst
namepstr) ~is_method
:false class_ name r;
1420 let env, fdm
= ShapeMap.map_env expr
env fdm
in
1421 (* allow_inter adds a type-variable *)
1422 let env, fdm
= ShapeMap.map_env
TUtils.unresolved
env fdm
in
1423 let env = check_shape_keys_validity
env p (ShapeMap.keys fdm
) in
1424 (* Fields are fully known, because this shape is constructed
1425 * using shape keyword and we know exactly what fields are set. *)
1426 env, (Reason.Rwitness
p, Tshape
(FieldsFullyKnown
, fdm
))
1428 and class_const ?
(incl_tc
=false) env p (cid, mid
) =
1429 TUtils.process_static_find_ref
cid mid
;
1430 let env, cty
= static_class_id
p env cid in
1431 let env, cty
= Env.expand_type
env cty
in
1433 class_get ~is_method
:false ~is_const
:true ~incl_tc
env cty mid
cid in
1435 | r, Tabstract
(AKgeneric
(n
, _), _) ->
1436 let () = match cid with
1437 | CIstatic
| CIexpr
_ -> ();
1438 | _ -> Errors.abstract_const_usage
p (Reason.to_pos
r) n
; ()
1443 (*****************************************************************************)
1444 (* Anonymous functions. *)
1445 (*****************************************************************************)
1447 and anon_bind_param
params env (param_name
, ty as pname_ty
) =
1450 (* This code cannot be executed normally, because the arity is wrong
1451 * and it will error later. Bind as many parameters as we can and carry
1454 | param :: paraml
->
1456 match param.param_hint
with
1458 let env, h
= Typing_hint.hint_locl
env h
in
1459 let pos = Reason.to_pos
(fst
ty) in
1460 let env = Type.sub_type
pos Reason.URparam
env h
ty in
1461 (* Closures are allowed to have explicit type-hints. When
1462 * that is the case we should check that the argument passed
1463 * is compatible with the type-hint.
1464 * The body of the function should be type-checked with the
1465 * hint and not the type of the argument passed.
1466 * Otherwise it leads to strange results where
1467 * foo(?string $x = null) is called with a string and fails to
1468 * type-check. If $x is a string instead of ?string, null is not
1469 * subtype of string ...
1471 bind_param
env (param_name
, h
) param
1472 | None
-> bind_param
env pname_ty
param
1474 and anon_bind_opt_param
env param =
1475 match param.param_expr
with
1477 let ty = Reason.Rnone
, Tany
in
1478 bind_param
env (None
, ty) param
1480 let env, ty = expr
env default
in
1481 Typing_sequencing.sequence_check_expr default
;
1482 bind_param
env (None
, ty) param
1484 and anon_check_param
env param =
1485 match param.param_hint
with
1488 let env, hty
= Typing_hint.hint_locl
env hty
in
1489 let env, paramty
= Env.get_local
env (snd
param.param_id
) in
1490 let hint_pos = Reason.to_pos
(fst hty
) in
1491 let env = Type.sub_type
hint_pos Reason.URhint
env hty paramty
in
1494 and anon_make tenv
p f
=
1495 let anon_lenv = tenv
.Env.lenv in
1496 let is_typing_self = ref false in
1497 let nb = Nast.assert_named_body f
.f_body
in
1501 Errors.anonymous_recursive
p;
1502 env, (Reason.Rwitness
p, Tany
)
1505 is_typing_self := true;
1506 Env.anon anon_lenv env begin fun env ->
1507 let params = ref f
.f_params in
1508 let env = List.fold_left ~f
:(anon_bind_param
params) ~init
:env tyl
in
1509 let env = List.fold_left ~f
:anon_bind_opt_param ~init
:env !params in
1510 let env = List.fold_left ~f
:anon_check_param ~init
:env f
.f_params in
1513 | None
-> TUtils.in_var
env (Reason.Rnone
, Tunresolved
[])
1516 Typing_hint.hint ~ensure_instantiable
:true env x
in
1517 (* If a 'this' type appears it needs to be compatible with the
1521 { (Phase.env_with_self
env) with
1522 from_class
= Some CIstatic
} in
1523 Phase.localize ~
ety_env env ret in
1524 let env = Env.set_return
env hret
in
1525 let env = Env.set_fn_kind
env f
.f_fun_kind
in
1526 let env = block
env nb.fnb_nast
in
1528 if Nast_terminality.Terminal.block tenv
nb.fnb_nast
1529 || nb.fnb_unsafe
|| !auto_complete
1531 else fun_implicit_return
env p hret
nb.fnb_nast f
.f_fun_kind
1533 is_typing_self := false;
1538 (*****************************************************************************)
1539 (* End of anonymous functions. *)
1540 (*****************************************************************************)
1542 and special_func
env p func
=
1543 let env, ty = (match func
with
1545 let env, ety
= expr
env e
in
1546 Async.gena
env p ety
1548 let env, etyl
= lmap expr
env el
in
1549 Async.genva
env p etyl
1550 | Gen_array_rec e
->
1551 let env, ety
= expr
env e
in
1552 Async.gen_array_rec
env p ety
1554 env, (Reason.Rwitness
p, Tclass
((p, SN.Classes.cAwaitable
), [ty]))
1556 and requires_consistent_construct
= function
1563 and new_object ~
check_not_abstract p env c el uel
=
1564 let env, class_ = instantiable_cid
p env c in
1567 let _ = lmap expr
env el
in
1568 let _ = lmap expr
env uel
in
1569 env, (Reason.Runknown_class
p, Tobject
)
1570 | Some
(cname
, class_, c_ty
) ->
1571 if check_not_abstract && class_.tc_abstract
1572 && not
(requires_consistent_construct
c) then
1573 uninstantiable_error
p c class_.tc_pos
class_.tc_name
p c_ty
;
1574 let env, params = lfold
begin fun env _ ->
1575 TUtils.in_var
env (Reason.Rnone
, Tunresolved
[])
1576 end env class_.tc_tparams
in
1578 if SSet.mem
"XHP" class_.tc_extends
then env else
1579 let env = call_construct
p env class_ params el uel
c in
1582 let r_witness = Reason.Rwitness
p in
1583 let obj_ty = r_witness, Tclass
(cname
, params) in
1584 if not
(snd
class_.tc_construct
) then
1586 | CIstatic
-> Errors.new_inconsistent_construct
p cname `static
1587 | CIexpr
_ -> Errors.new_inconsistent_construct
p cname `classname
1591 env, (r_witness, TUtils.this_of
obj_ty)
1593 (match (fst
class_.tc_construct
) with
1596 type_expansions
= [];
1597 substs
= SMap.empty
;
1601 let _, ce_type
= Phase.localize ~
ety_env env ce
.ce_type
in
1602 ignore
(check_abstract_parent_meth
SN.Members.__construct
p ce_type
)
1605 | CI
_ | CIself
-> env, obj_ty
1607 let c_ty = r_witness, snd
c_ty in
1608 (* When constructing from a (classname) variable, the variable
1609 * dictates what the constructed object is going to be. This allows
1610 * for generic and dependent types to be correctly carried
1611 * through the 'new $foo()' iff the constructed obj_ty is a
1612 * supertype of the variable-dictated c_ty *)
1613 let env = SubType.sub_type
env obj_ty c_ty in
1617 (* FIXME: we need to separate our instantiability into two parts. Currently,
1618 * all this function is doing is checking if a given type is inhabited --
1619 * that is, whether there are runtime values of type T. However,
1620 * instantiability should be the stricter notion that T has a runtime
1621 * constructor; that is, `new T()` should be valid. In particular, interfaces
1622 * are inhabited, but not instantiable.
1623 * To make this work with classname, we likely need to add something like
1624 * concrete_classname<T>, where T cannot be an interface.
1626 and instantiable_cid
p env cid =
1627 let env, class_id
= class_id_for_new
p env cid in
1628 (match class_id
with
1629 | Some
((pos, name), class_, c_ty) when
1630 class_.tc_kind
= Ast.Ctrait
|| class_.tc_kind
= Ast.Cenum
->
1632 | CIexpr
_ | CI
_ ->
1633 uninstantiable_error
p cid class_.tc_pos
name pos c_ty;
1635 | CIstatic
| CIparent
| CIself
-> env, class_id
1637 | Some
((pos, name), class_, c_ty) when
1638 class_.tc_kind
= Ast.Cabstract
&& class_.tc_final
->
1639 uninstantiable_error
p cid class_.tc_pos
name pos c_ty;
1641 | None
| Some
_ -> env, class_id
)
1643 and uninstantiable_error reason_pos
cid c_tc_pos c_name c_usage_pos
c_ty =
1644 let reason_msgl = match cid with
1646 let ty_str = "This would be "^
Typing_print.error
(snd
c_ty) in
1647 [(reason_pos
, ty_str)]
1649 Errors.uninstantiable_class c_usage_pos c_tc_pos c_name
reason_msgl
1651 and exception_ty
pos env ty =
1652 let exn_ty = Reason.Rthrow
pos, Tclass
((pos, SN.Classes.cException
), []) in
1653 Type.sub_type
pos (Reason.URthrow
) env exn_ty ty
1655 (* While converting code from PHP to Hack, some arrays are used
1656 * as tuples. Example: array('', 0). Since the elements have
1657 * incompatible types, it should be a tuple. However, while migrating
1658 * code, it is more flexible to allow it in partial.
1660 * This probably isn't a good idea and should just use ty2 in all cases, but
1661 * FB www has about 50 errors if you just use ty2 -- not impossible to clean
1662 * up but more work right now than I want to do. Also it probably affects open
1663 * source code too, so this may be a nice small test case for our upcoming
1664 * migration/upgrade strategy.
1666 and convert_array_as_tuple
env ty2 =
1668 if not
(Env.is_strict
env) && TUtils.is_array_as_tuple
env ty2
1669 then env, (r2, Tany
)
1672 and shape_field_pos
= function
1674 | SFclass_const
((cls_pos
, _), (member_pos
, _)) -> Pos.btw cls_pos member_pos
1676 and check_shape_keys_validity
env pos keys
=
1677 (* If the key is a class constant, get its class name and type. *)
1678 let get_field_info env key
=
1679 let key_pos = shape_field_pos key
in
1680 (* Empty strings or literals that start with numbers are not
1681 permitted as shape field names. *)
1683 | SFlit
(_, key_name
) ->
1684 if (String.length key_name
= 0) then
1685 (Errors.invalid_shape_field_name_empty
key_pos)
1686 else if (key_name
.[0] >= '
0'
&& key_name
.[0] <='
9'
) then
1687 (Errors.invalid_shape_field_name_number
key_pos);
1689 | SFclass_const
(_, cls
as x
, y
) ->
1690 let env, ty = class_const
env pos (CI x
, y
) in
1691 let env = Typing_enum.check_valid_array_key_type
1692 Errors.invalid_shape_field_type ~allow_any
:false
1694 env, key_pos, Some
(cls
, ty))
1697 let check_field witness_pos witness_info
env key
=
1698 let env, key_pos, key_info
= get_field_info env key
in
1699 (match witness_info
, key_info
with
1701 Errors.invalid_shape_field_literal
key_pos witness_pos
; env
1703 Errors.invalid_shape_field_const
key_pos witness_pos
; env
1705 | Some
(cls1
, ty1), Some
(cls2
, ty2) ->
1706 if cls1
<> cls2
then
1707 Errors.shape_field_class_mismatch
1708 key_pos witness_pos
(strip_ns cls2
) (strip_ns cls1
);
1709 (* We want to use our own error message here instead of the normal
1710 * unification one. *)
1712 (fun () -> Unify.iunify
env ty1 ty2)
1714 Errors.shape_field_type_mismatch
1716 (Typing_print.error
(snd
ty2)) (Typing_print.error
(snd
ty1));
1720 (* Sort the keys by their positions since the error messages will make
1721 * more sense if we take the one that appears first as canonical and if
1722 * they are processed in source order. *)
1723 let cmp_keys x y
= Pos.compare
(shape_field_pos x
) (shape_field_pos y
) in
1724 let keys = List.sort
cmp_keys keys in
1728 | witness
:: rest_keys
->
1729 let env, pos, info
= get_field_info env witness
in
1730 List.fold_left ~f
:(check_field pos info
) ~init
:env rest_keys
1732 and check_valid_rvalue
p env ty =
1733 let env, folded_ty
= TUtils.fold_unresolved
env ty in
1734 let _deliberately_discarded_env, folded_ety
=
1735 Env.expand_type
env folded_ty
in
1736 match folded_ety
with
1737 | r, Tprim Tnoreturn
->
1738 let () = Errors.noreturn_usage
p
1739 (Reason.to_string
"A noreturn function always throws or exits" r)
1742 let () = Errors.void_usage
p
1743 (Reason.to_string
"A void function doesn't return a value" r)
1747 and set_valid_rvalue
p env x
ty =
1748 let ty = check_valid_rvalue
p env ty in
1749 let env = Env.set_local
env x
ty in
1750 (* We are assigning a new value to the local variable, so we need to
1751 * generate a new expression id
1753 let env = Env.set_local_expr_id
env x
(Ident.tmp
()) in
1756 and assign
p env e1
ty2 =
1757 let env, ty2 = convert_array_as_tuple
env ty2 in
1759 | (_, Lvar
(_, x
)) ->
1760 set_valid_rvalue
p env x
ty2
1761 | (_, Lplaceholder
_) ->
1762 let placeholder_ty = Reason.Rplaceholder
p, (Tprim Tvoid
) in
1765 let env, folded_ty2
= TUtils.fold_unresolved
env ty2 in
1766 let env, folded_ety2
= Env.expand_type
env folded_ty2
in
1767 (match folded_ety2
with
1768 | _, Tclass
((_, x
), [elt_type
])
1769 when x
= SN.Collections.cVector
1770 || x
= SN.Collections.cImmVector
1771 || x
= SN.Collections.cConstVector
->
1772 let env, _ = lfold
begin fun env e
->
1773 assign
(fst e
) env e elt_type
1776 | _, Tarraykind
(AKvec elt_type
) ->
1777 let env, _ = lfold
begin fun env e
->
1778 assign
(fst e
) env e elt_type
1781 | r, Tarraykind AKany
1783 let env, _ = lfold
begin fun env e
->
1784 assign
(fst e
) env e
(r, Tany
)
1787 | r, Tclass
((_, coll
), [ty1; ty2]) when coll
= SN.Collections.cPair
->
1790 let env, _ = assign
p env x1
ty1 in
1791 let env, _ = assign
p env x2
ty2 in
1792 env, (Reason.Rwitness
(fst e1
), Tprim Tvoid
)
1794 Errors.pair_arity
p;
1798 let size1 = List.length el
in
1799 let size2 = List.length tyl
in
1801 let p2 = Reason.to_pos
r in
1804 Errors.tuple_arity
p2 size2 p1 size1;
1808 let env = List.fold2_exn el tyl ~f
:begin fun env lvalue
ty2 ->
1809 fst
(assign
p env lvalue
ty2)
1811 env, (Reason.Rwitness
p1, Tprim Tvoid
)
1812 | _, Tabstract
(_, Some
ty2) -> assign
p env e1
ty2
1813 | _, (Tmixed
| Tarraykind
_ | Toption
_ | Tprim
_
1814 | Tvar
_ | Tfun
_ | Tabstract
(_, _) | Tanon
(_, _)
1815 | Tunresolved
_ | Tclass
(_, _) | Tobject
| Tshape
_) ->
1816 assign_simple
p env e1
ty2
1820 let _, locals
as lenv = env.Env.lenv in
1821 let no_fakes = Env.empty_fake_members
, locals
in
1822 (* In this section, we check that the assignment is compatible with
1823 * the real type of a member. Remember that members can change
1824 * type (cf fake_members). But when we assign a value to $this->x,
1825 * we want to make sure that the type assign to $this->x is compatible
1826 * with the actual type hint. In this portion of the code, type-check
1827 * the assignment in an environment without fakes, and therefore
1828 * check that the assignment is compatible with the type of
1831 let env, real_type
= lvalue
{ env with Env.lenv = no_fakes } e1
in
1832 let env, exp_real_type
= Env.expand_type
env real_type
in
1833 let env = { env with Env.lenv = lenv } in
1834 let env, ety2
= Env.expand_type
env ty2 in
1835 let real_type_list =
1836 match exp_real_type
with
1837 | _, Tunresolved tyl
-> tyl
1840 let env = List.fold_left
real_type_list ~f
:begin fun env real_type
->
1841 Type.sub_type
p (Reason.URassign
) env real_type ety2
1844 | _, Obj_get
((_, This
| _, Lvar
_ as obj
),
1845 (_, Id
(_, member_name
)),
1847 let env, local = Env.FakeMembers.make
p env obj member_name
in
1848 let () = (match obj
with
1850 Typing_suggest.save_member member_name
env exp_real_type
ty2
1853 set_valid_rvalue
p env local ty2
1854 | _, Class_get
(x
, (_, y
)) ->
1855 let env, local = Env.FakeMembers.make_static
p env x y
in
1856 let env, ty3
= set_valid_rvalue
p env local ty2 in
1860 Typing_suggest.save_member y
env exp_real_type
ty2;
1865 | _, Array_get
((_, Lvar
(_, lvar
)) as shape
, Some
(p1, (String
_ as e
)))
1866 | _, Array_get
((_, Lvar
(_, lvar
)) as shape
,
1867 Some
(p1, (Class_const
(CI
_, _) as e
))) ->
1868 (* In the case of an assignment of the form $x['new_field'] = ...;
1869 * $x could be a shape where the field 'new_field' is not yet defined.
1870 * When that is the case we want to add the field to its type.
1872 let env, shape_ty
= expr
env shape
in
1873 let field = TUtils.shape_field_name
env p1 e
in
1875 Typing_shapes.grow_shape
p e1
field (Env.fresh_type
()) env shape_ty
in
1876 let env, _ty
= set_valid_rvalue
p env lvar shape_ty
in
1878 (* We still need to call assign_simple in order to bind the freshly
1879 * created variable in added shape field. Moreover, it's needed because
1880 * shape_ty could be more than just a shape. It could be an unresolved
1881 * type where some elements are shapes and some others are not.
1883 assign_simple
p env e1
ty2
1885 Errors.this_lvalue
p;
1886 env, (Reason.Rwitness
p, Tany
)
1887 | _, Unop
(Ast.Uref
, e1'
) ->
1888 (* references can be "lvalues" in foreach bindings *)
1889 assign
p env e1'
ty2
1891 assign_simple
p env e1
ty2
1893 and assign_simple
pos env e1
ty2 =
1894 let env, ty1 = lvalue
env e1
in
1896 let ty2 = check_valid_rvalue
pos env ty2 in
1898 let env, ty2 = TUtils.unresolved
env ty2 in
1899 let env = Type.sub_type
pos (Reason.URassign
) env ty1 ty2 in
1902 and array_field_value
env = function
1904 | Nast.AFkvalue
(_, x
) ->
1905 let env, ty = expr
env x
in
1906 Typing_env.unbind
env ty
1908 and yield_field_value
env x
= array_field_value
env x
1910 and array_field_key
env = function
1911 | Nast.AFvalue
(p, _) ->
1912 env, (Reason.Rwitness
p, Tprim Tint
)
1913 | Nast.AFkvalue
(x
, _) ->
1914 let env, ty = expr
env x
in
1915 Typing_env.unbind
env ty
1917 and yield_field_key
env = function
1918 | Nast.AFvalue
(p, _) ->
1919 env, (match Env.get_fn_kind
env with
1921 | Ast.FAsync
-> assert false
1923 (Reason.Rwitness
p, Tprim Tint
)
1924 | Ast.FAsyncGenerator
->
1925 (Reason.Ryield_asyncnull
p, Toption
(Env.fresh_type
())))
1926 | Nast.AFkvalue
(x
, _) ->
1929 and check_parent_construct
pos env el uel env_parent
=
1930 let check_not_abstract = false in
1931 let env, env_parent
= Phase.localize_with_self
env env_parent
in
1932 let env, parent
= new_object ~
check_not_abstract pos env CIparent el uel
in
1933 let env, _ = Type.unify
pos (Reason.URnone
) env env_parent parent
in
1934 env, (Reason.Rwitness
pos, Tprim Tvoid
)
1936 and call_parent_construct
pos env el uel
=
1937 let parent = Env.get_parent
env in
1940 check_parent_construct
pos env el uel
parent
1941 | _, (Tany
| Tmixed
| Tarray
(_, _) | Tgeneric
(_, _) | Toption
_ | Tprim
_
1942 | Tfun
_ | Ttuple
_ | Tshape
_ | Taccess
(_, _) | Tthis
1943 ) -> (* continue here *)
1944 let default = env, (Reason.Rnone
, Tany
) in
1945 match Env.get_self
env with
1946 | _, Tclass
((_, self
), _) ->
1947 (match Env.get_class
env self
with
1948 | Some
({tc_kind
= Ast.Ctrait
; _}
1950 (match trait_most_concrete_req_class trait
env with
1951 | None
-> Errors.parent_in_trait
pos; default
1952 | Some
(_, parent_ty
) ->
1953 check_parent_construct
pos env el uel parent_ty
1956 if not self_tc
.tc_members_fully_known
1957 then () (* Don't know the hierarchy, assume it's correct *)
1958 else Errors.undefined_parent
pos;
1960 | None
-> assert false)
1961 | _, (Tany
| Tmixed
| Tarraykind
_ | Toption
_
1962 | Tprim
_ | Tfun
_ | Ttuple
_ | Tshape
_ | Tvar
_
1963 | Tabstract
(_, _) | Tanon
(_, _) | Tunresolved
_ | Tobject
1965 Errors.parent_outside_class
pos; default
1967 (* parent::method() in a class definition invokes the specific parent
1968 * version of the method ... it better be callable *)
1969 and check_abstract_parent_meth mname
pos fty =
1970 if is_abstract_ft
fty then Errors.parent_abstract_call mname
pos (Reason.to_pos
(fst
fty)) ;
1973 and is_abstract_ft
fty = match fty with
1974 | _r, Tfun
{ ft_abstract
= true; _ } -> true
1975 | _r, (Tany
| Tmixed
| Tarraykind
_ | Toption
_ | Tprim
_
1976 | Tvar
_ | Tfun
_ | Tclass
(_, _) | Tabstract
(_, _) | Ttuple
_
1977 | Tanon
_ | Tunresolved
_ | Tobject
| Tshape
_
1981 (* Depending on the kind of expression we are dealing with
1982 * The typing of call is different.
1984 and dispatch_call
p env call_type
(fpos
, fun_expr
as e
) el uel
=
1986 | Id
(_, pseudo_func
) when pseudo_func
= SN.SpecialFunctions.echo
->
1987 let env, _ = lfold expr
env el
in
1988 env, (Reason.Rwitness
p, Tprim Tvoid
)
1989 | Id
(_, pseudo_func
)
1991 pseudo_func
= SN.PseudoFunctions.isset
1992 || pseudo_func
= SN.PseudoFunctions.empty
->
1993 let env, _ = lfold expr
env el
in
1995 Errors.unpacking_disallowed_builtin_function
p pseudo_func
;
1996 if Env.is_strict
env then
1997 Errors.isset_empty_in_strict
p pseudo_func
;
1998 env, (Reason.Rwitness
p, Tprim Tbool
)
1999 | Id
(_, pseudo_func
) when pseudo_func
= SN.PseudoFunctions.unset
->
2000 let env, _ = lfold expr
env el
in
2002 Errors.unpacking_disallowed_builtin_function
p pseudo_func
;
2003 let env = if Env.is_strict
env then
2005 | [(_, Array_get
(ea
, Some
_))], [] ->
2006 let env, ty = expr
env ea
in
2008 (fun () -> SubType.sub_type
2009 env (Reason.Rnone
, Tarraykind AKany
) ty)
2011 let env, (r, ety
) = Env.expand_type
env ty in
2012 Errors.unset_nonidx_in_strict
2014 (Reason.to_string
("This is " ^
Typing_print.error ety
) r);
2016 | _ -> Errors.unset_nonidx_in_strict
p []; env)
2019 | [(p, Obj_get
(_, _, OG_nullsafe
))] ->
2021 Errors.nullsafe_property_write_context
p;
2022 env, (Reason.Rwitness
p, Tany
)
2024 | _ -> env, (Reason.Rwitness
p, Tprim Tvoid
))
2025 | Id
(cp
, get_called_class
) when
2026 get_called_class
= SN.StdlibFunctions.get_called_class
2027 && el
= [] && uel
= [] ->
2028 (* get_called_class fetches the late-bound class *)
2029 if Env.is_outside_class
env then Errors.static_outside_class
p;
2030 class_const
env p (CIstatic
, (cp
, SN.Members.mClass
))
2031 | Id
((_, array_filter
) as id
)
2032 when array_filter
= SN.StdlibFunctions.array_filter
&& el
<> [] && uel
= [] ->
2033 (* dispatch the call to typecheck the arguments *)
2034 let env, fty = fun_type_of_id
env id
in
2035 let env, fty = Env.expand_type
env fty in
2036 let env, res
= call
p env fty el uel
in
2037 (* but ignore the result and overwrite it with custom return type *)
2038 let x = List.hd_exn el
in
2039 let env, ty = expr
env x in
2040 let explain_array_filter (r, t
) =
2041 (Reason.Rarray_filter
(p, r), t
) in
2042 let get_value_type env tv
=
2043 let env, tv
= if List.length el
> 1 then env, tv
else non_null
env tv
in
2044 env, explain_array_filter tv
in
2045 let rec get_array_filter_return_type env ty =
2046 let env, ety
= Env.expand_type
env ty in
2048 | (_, Tarraykind AKany
) as array_type
->
2050 | (r, Tarraykind
(AKvec tv
)) ->
2051 let env, tv
= get_value_type env tv
in
2052 env, (r, Tarraykind
(AKvec tv
))
2053 | (r, Tunresolved
x) ->
2054 let env, x = lmap
get_array_filter_return_type env x in
2055 env, (r, Tunresolved
x)
2059 let tk, tv
= Env.fresh_type
(), Env.fresh_type
() in
2062 let keyed_container = (
2065 (Pos.none
, SN.Collections.cKeyedContainer
), [tk; tv
]
2068 let env = SubType.sub_type
env keyed_container ety
in
2069 let env, tv
= get_value_type env tv
in
2070 env, (r, Tarraykind
(AKmap
(
2071 (explain_array_filter tk),
2074 (fun _ -> Errors.try_
2079 (Pos.none
, SN.Collections.cContainer
), [tv
]
2082 let env = SubType.sub_type
env container ety
in
2083 let env, tv
= get_value_type env tv
in
2084 env, (r, Tarraykind
(AKmap
(
2085 (explain_array_filter (r, Tprim Tarraykey
)),
2087 (fun _ -> env, res
)))
2088 in get_array_filter_return_type env ty
2089 | Id
(p, type_structure
)
2090 when type_structure
= SN.StdlibFunctions.type_structure
2091 && (List.length el
= 2) && uel
= [] ->
2095 | p, Nast.String cst
->
2096 (* find the class constant implicitly defined by the typeconst *)
2097 let cid = (match e1
with
2098 | _, Class_const
(cid, (_, x))
2099 | _, Class_get
(cid, (_, x)) when x = SN.Members.mClass
-> cid
2100 | _ -> Nast.CIexpr e1
) in
2101 class_const ~incl_tc
:true env p (cid, cst
)
2103 Errors.illegal_type_structure
p "second argument is not a string";
2104 env, (Reason.Rnone
, Tany
))
2105 | _ -> assert false)
2106 | Id
((_, array_map
) as x)
2107 when array_map
= SN.StdlibFunctions.array_map
&& el
<> [] && uel
= [] ->
2108 let env, fty = fun_type_of_id
env x in
2109 let env, fty = Env.expand_type
env fty in
2110 let env, fty = match fty, el
with
2111 | ((r_fty
, Tfun
fty), _::args
) when args
<> [] ->
2112 let arity = List.length args
in
2114 Builds a function with signature:
2116 function<T1, ..., Tn, Tr>(
2117 (function(T1, ..., Tn):Tr),
2123 where R is constructed by build_output_container applied to Tr
2125 let build_function build_output_container
=
2126 let vars = List.map args
(fun _ -> Env.fresh_type
()) in
2127 let tr = Env.fresh_type
() in
2131 ft_pos
= fty.ft_pos
;
2132 ft_deprecated
= None
;
2133 ft_abstract
= false;
2134 ft_arity
= Fstandard
(arity, arity); ft_tparams
= [];
2135 ft_params
= List.map
vars (fun x -> (None
, x));
2139 let containers = List.map
vars (fun var
->
2142 Tclass
((fty.ft_pos
, SN.Collections.cContainer
), [var
])
2146 (r_fty
, Tfun
{fty with
2147 ft_arity
= Fstandard
(arity+1, arity+1);
2148 ft_params
= f::containers;
2149 ft_ret
= build_output_container
tr;
2153 Takes a Container type and returns a function that can "pack" a type
2154 into an array of appropriate shape, preserving the key type, i.e.:
2155 array -> f, where f R = array
2156 array<X> -> f, where f R = array<R>
2157 array<X, Y> -> f, where f R = array<X, R>
2158 Vector<X> -> f where f R = array<R>
2159 KeyedContainer<X, Y> -> f, where f R = array<X, R>
2160 Container<X> -> f, where f R = array<arraykey, R>
2161 X -> f, where f R = Y
2163 let rec build_output_container
2164 (env:Env.env) (x:locl
ty) : (Env.env * (locl
ty -> locl
ty)) =
2165 let env, x = Env.expand_type
env x in (match x with
2166 | (_, Tarraykind AKany
) as array_type
->
2167 env, (fun _ -> array_type
)
2168 | (r, Tarraykind AKvec
_) ->
2169 env, (fun tr -> (r, Tarraykind
(AKvec
(tr))) )
2170 | ((_, Tany
) as any) ->
2172 | (r, Tunresolved
x) ->
2173 let env, x = lmap
build_output_container env x in
2174 env, (fun tr -> (r, Tunresolved
(List.map
x (fun f -> f tr))))
2176 let tk, tv
= Env.fresh_type
(), Env.fresh_type
() in
2177 let try_vector env =
2181 (fty.ft_pos
, SN.Collections.cConstVector
), [tv
]
2184 let env = SubType.sub_type
env vector x in
2185 env, (fun tr -> (r, Tarraykind
(
2188 let try_keyed_container env =
2189 let keyed_container = (
2192 (fty.ft_pos
, SN.Collections.cKeyedContainer
), [tk; tv
]
2195 let env = SubType.sub_type
env keyed_container x in
2196 env, (fun tr -> (r, Tarraykind
(AKmap
(
2200 let try_container env =
2204 (fty.ft_pos
, SN.Collections.cContainer
), [tv
]
2207 let env = SubType.sub_type
env container x in
2208 env, (fun tr -> (r, Tarraykind
(AKmap
(
2209 (r, Tprim Tarraykey
),
2214 (fun _ -> Errors.try_
2216 try_keyed_container env)
2217 (fun _ -> Errors.try_
2220 (fun _ -> env, (fun _ -> (Reason.Rwitness
p, Tany
)))))) in
2222 Single argument calls preserve the key type, multi argument
2223 calls always return an array<Tr>
2227 let env, x = expr
env x in
2228 let env, output_container
= build_output_container env x in
2229 env, build_function output_container
2231 env, build_function (fun tr ->
2232 (r_fty
, Tarraykind
(AKvec
(tr)))))
2234 call
p env fty el
[]
2235 | Id
((_, idx
) as id
) when idx
= SN.FB.idx
->
2236 (* Directly call get_fun so that we can muck with the type before
2237 * instantiation -- much easier to work in terms of Tgeneric Tk/Tv than
2238 * trying to figure out which Tvar is which. *)
2239 (match Env.get_fun
env (snd id
) with
2241 let param1, (name2
, (r2, _)), (name3
, (r3
, _)) =
2242 match fty.ft_params
with
2243 | [param1; param2
; param3
] -> param1, param2
, param3
2244 | _ -> assert false in
2245 let params, ret = match List.length el
with
2247 let param2 = (name2
, (r2, Toption
(r2, Tgeneric
("Tk", None
)))) in
2248 let rret = fst
fty.ft_ret
in
2249 let ret = (rret, Toption
(rret, Tgeneric
("Tv", None
))) in
2250 [param1; param2], ret
2252 let param2 = (name2
, (r2, Tgeneric
("Tk", None
))) in
2253 let param3 = (name3
, (r3
, Tgeneric
("Tv", None
))) in
2254 let ret = (fst
fty.ft_ret
, Tgeneric
("Tv", None
)) in
2255 [param1; param2; param3], ret
2256 | _ -> fty.ft_params
, fty.ft_ret
in
2257 let fty = { fty with ft_params
= params; ft_ret
= ret } in
2258 let ety_env = Phase.env_with_self
env in
2259 let env, fty = Phase.localize_ft ~
ety_env env fty in
2260 let tfun = Reason.Rwitness
fty.ft_pos
, Tfun
fty in
2261 call
p env tfun el
[]
2262 | None
-> unbound_name env id
)
2263 | Class_const
(CI
(_, shapes
) as class_id
, ((_, idx
) as method_id
))
2264 when shapes
= SN.Shapes.cShapes
&& idx
= SN.Shapes.idx
->
2265 overload_function
p env class_id method_id el uel
2266 begin fun env fty res el
-> match el
with
2268 let env, shape_ty
= expr
env shape
in
2269 Typing_shapes.idx
env fty shape_ty
field None
2270 | [shape
; field; default] ->
2271 let env, shape_ty
= expr
env shape
in
2272 let env, default_ty
= expr
env default in
2273 Typing_shapes.idx
env fty shape_ty
field
2274 (Some
((fst
default), default_ty
))
2277 | Class_const
(CI
(_, shapes
) as class_id
, ((_, key_exists
) as method_id
))
2278 when shapes
= SN.Shapes.cShapes
&& key_exists
= SN.Shapes.keyExists
->
2279 overload_function
p env class_id method_id el uel
2280 begin fun env fty res el
-> match el
with
2282 let env, shape_ty
= expr
env shape
in
2283 (* try acessing the field, to verify existence, but ignore
2284 * the returned type and keep the one coming from function
2285 * return type hint *)
2286 let env, _ = Typing_shapes.idx
env fty shape_ty
field None
in
2290 | Class_const
(CI
(_, shapes
) as class_id
, ((_, remove_key
) as method_id
))
2291 when shapes
= SN.Shapes.cShapes
&& remove_key
= SN.Shapes.removeKey
->
2292 overload_function
p env class_id method_id el uel
2293 begin fun env _ res el
-> match el
with
2294 | [shape
; field] -> begin match shape
with
2295 | (_, Lvar
(_, lvar
)) ->
2296 let env, shape_ty
= expr
env shape
in
2298 Typing_shapes.remove_key
p env shape_ty
field in
2299 let env, _ = set_valid_rvalue
p env lvar shape_ty
in
2302 Errors.invalid_shape_remove_key
(fst shape
);
2307 | Class_const
(CIparent
, (_, construct
))
2308 when construct
= SN.Members.__construct
->
2309 Typing_hooks.dispatch_parent_construct_hook
env p;
2310 call_parent_construct
p env el uel
2311 | Class_const
(CIparent
, m
) ->
2312 let env, ty1 = static_class_id
p env CIparent
in
2313 if Env.is_static
env
2315 (* in static context, you can only call parent::foo() on static
2317 let env, fty = class_get ~is_method
:true ~is_const
:false env ty1 m CIparent
in
2318 let env, fty = Env.expand_type
env fty in
2319 let fty = check_abstract_parent_meth
(snd m
) p fty in
2320 call
p env fty el uel
2323 (* in instance context, you can call parent:foo() on static
2324 * methods as well as instance methods *)
2325 (match class_contains_smethod
env ty1 m
with
2327 (* parent::nonStaticFunc() is really weird. It's calling a method
2328 * defined on the parent class, but $this is still the child class.
2329 * We can deal with this by hijacking the continuation that
2330 * calculates the SN.Typehints.this type *)
2331 let this_ty = ExprDepTy.make
env CIstatic
2332 (Reason.Rwitness fpos
, TUtils.this_of
(Env.get_self
env)) in
2333 let k_lhs _ = this_ty in
2334 let env, method_
, _ =
2335 obj_get_ ~is_method
:true ~
nullsafe:None
env ty1 CIparent m
2336 begin fun (env, fty, _) ->
2337 let env, fty = Env.expand_type
env fty in
2338 let fty = check_abstract_parent_meth
(snd m
) p fty in
2339 let env, method_
= call
p env fty el uel
in
2346 let env, fty = class_get ~is_method
:true ~is_const
:false env ty1 m CIparent
in
2347 let env, fty = Env.expand_type
env fty in
2348 let fty = check_abstract_parent_meth
(snd m
) p fty in
2349 call
p env fty el uel
2352 | Class_const
(e1
, m
) ->
2353 TUtils.process_static_find_ref e1 m
;
2354 let env, ty1 = static_class_id
p env e1
in
2355 let env, fty = class_get ~is_method
:true ~is_const
:false env ty1 m e1
in
2356 let env, fty = Env.expand_type
env fty in
2357 let () = match e1
with
2358 | CIself
when is_abstract_ft
fty ->
2359 (match Env.get_self
env with
2360 | _, Tclass
((_, self
), _) ->
2361 (* at runtime, self:: in a trait is a call to whatever
2362 * self:: is in the context of the non-trait "use"-ing
2363 * the trait's code *)
2364 (match Env.get_class
env self
with
2365 | Some
{ tc_kind
= Ast.Ctrait
; _ } -> ()
2366 | _ -> Errors.self_abstract_call
(snd m
) p (Reason.to_pos
(fst
fty))
2369 | CI
c when is_abstract_ft
fty ->
2370 Errors.classname_abstract_call
(snd
c) (snd m
) p (Reason.to_pos
(fst
fty))
2372 call
p env fty el uel
2373 | Obj_get
(e1
, (_, Id m
), nullflavor
) ->
2374 let is_method = call_type
= Cnormal
in
2375 let env, ty1 = expr
env e1
in
2377 (match nullflavor
with
2378 | OG_nullthrows
-> None
2379 | OG_nullsafe
-> Some
p
2381 let fn = (fun (env, fty, _) ->
2382 let env, fty = Env.expand_type
env fty in
2383 let env, method_
= call
p env fty el uel
in
2384 env, method_
, None
) in
2385 obj_get ~
is_method ~
nullsafe env ty1 (CIexpr e1
) m
fn
2388 Typing_hooks.dispatch_id_hook
x env;
2389 let env, fty = fun_type_of_id
env x in
2390 let env, fty = Env.expand_type
env fty in
2391 call
p env fty el uel
2393 let env, fty = expr
env e
in
2394 let env, fty = Env.expand_type
env fty in
2395 call
p env fty el uel
2397 and fun_type_of_id
env x =
2398 Typing_hooks.dispatch_fun_id_hook
x;
2400 match Env.get_fun
env (snd
x) with
2401 | None
-> unbound_name env x
2403 let ety_env = Phase.env_with_self
env in
2404 let env, fty = Phase.localize_ft ~
ety_env env fty in
2405 env, (Reason.Rwitness
fty.ft_pos
, Tfun
fty)
2409 (*****************************************************************************)
2410 (* Function type-checking expressions accessing an array (example: $x[...]).
2411 * The parameter is_lvalue is true when the expression is on the left hand
2412 * side of an assignment (example: $x[...] = 0).
2414 (*****************************************************************************)
2415 and array_get
is_lvalue p env ty1 ety1
e2 ty2 =
2416 (* This is a little weird -- we enforce the right arity when you use certain
2417 * collections, even in partial mode (where normally completely omitting the
2418 * type parameter list is admitted). Basically the "omit type parameter"
2419 * hole was for compatibility with certain interfaces like ArrayAccess, not
2420 * for collections! But it's hard to go back on now, so since we've always
2421 * errored (with an inscrutable error message) when you try to actually use
2422 * a collection with omitted type parameters, we can continue to error and
2423 * give a more useful error message. *)
2424 let arity_error (_, name) =
2425 Errors.array_get_arity
p name (Reason.to_pos
(fst
ty1))
2428 | Tunresolved tyl
->
2429 let env, tyl
= lfold
begin fun env ty1 ->
2430 let env, ety1
= Env.expand_type
env ty1 in
2431 array_get
is_lvalue p env ty1 ety1
e2 ty2
2434 env, (fst ety1
, Tunresolved tyl
)
2435 | Tarraykind
(AKvec
ty) ->
2436 let ty1 = Reason.Ridx
(fst
e2), Tprim Tint
in
2437 let env = Type.sub_type
p Reason.URarray_get
env ty1 ty2 in
2439 | Tclass
((_, cn
) as id
, argl
)
2440 when cn
= SN.Collections.cVector
->
2441 let ty = match argl
with
2443 | _ -> arity_error id
; Reason.Rwitness
p, Tany
in
2444 let ty1 = Reason.Ridx_vector
(fst
e2), Tprim Tint
in
2445 let env = Type.sub_type
p Reason.URvector_get
env ty1 ty2 in
2447 | Tclass
((_, cn
) as id
, argl
)
2448 when cn
= SN.Collections.cMap
2449 || cn
= SN.Collections.cStableMap
->
2450 let (k
, v
) = match argl
with
2454 let any = (Reason.Rwitness
p, Tany
) in
2457 let env, ty2 = TUtils.unresolved
env ty2 in
2458 let env, _ = Type.unify
p Reason.URmap_get
env k
ty2 in
2460 | Tclass
((_, cn
) as id
, argl
)
2461 when not
is_lvalue &&
2462 (cn
= SN.Collections.cConstMap
2463 || cn
= SN.Collections.cImmMap
) ->
2464 let (k
, v
) = match argl
with
2468 let any = (Reason.Rwitness
p, Tany
) in
2471 let env = Type.sub_type
p Reason.URmap_get
env k
ty2 in
2473 | Tclass
((_, cn
), _)
2475 (cn
= SN.Collections.cConstMap
|| cn
= SN.Collections.cImmMap
) ->
2476 error_const_mutation
env p ety1
2477 | Tclass
((_, cn
) as id
, argl
)
2478 when (cn
= SN.Collections.cIndexish
2479 || cn
= SN.Collections.cKeyedContainer
) ->
2480 let (k
, v
) = match argl
with
2484 let any = (Reason.Rwitness
p, Tany
) in
2487 let env = Type.sub_type
p Reason.URcontainer_get
env k
ty2 in
2489 | Tclass
((_, cn
) as id
, argl
)
2490 when not
is_lvalue &&
2491 (cn
= SN.Collections.cConstVector
|| cn
= SN.Collections.cImmVector
) ->
2492 let ty = match argl
with
2494 | _ -> arity_error id
; Reason.Rwitness
p, Tany
in
2495 let ty1 = Reason.Ridx
(fst
e2), Tprim Tint
in
2496 let ur = (match cn
with
2497 | x when x = SN.Collections.cConstVector
-> Reason.URconst_vector_get
2498 | x when x = SN.Collections.cImmVector
-> Reason.URimm_vector_get
2499 | _ -> failwith
("Unexpected collection name: " ^ cn
)) in
2500 let env, _ = Type.unify
p ur env ty2 ty1 in
2502 | Tclass
((_, cn
), _)
2504 (cn
= SN.Collections.cConstVector
|| cn
= SN.Collections.cImmVector
) ->
2505 error_const_mutation
env p ety1
2506 | Tarraykind
(AKmap
(k
, v
)) ->
2507 let env, ty2 = TUtils.unresolved
env ty2 in
2508 let env, _ = Type.unify
p Reason.URarray_get
env k
ty2 in
2509 (* The values in the array are not consistent
2510 * we just give up. Use Maps!
2512 let env, ev
= TUtils.fold_unresolved
env v
in
2513 let env, ev
= Env.expand_type
env ev
in
2515 | _, Tunresolved
_ -> env, (Reason.Rwitness
p, Tany
)
2516 | _, (Tany
| Tmixed
| Tarraykind
_ | Toption
_
2517 | Tprim
_ | Tvar
_ | Tfun
_ | Tclass
(_, _) | Tabstract
(_, _)
2518 | Ttuple
_ | Tanon
_ | Tobject
| Tshape
_) -> env, v
2520 | Tany
| Tarraykind AKany
-> env, (Reason.Rnone
, Tany
)
2522 let ty = Reason.Rwitness
p, Tprim Tstring
in
2523 let env, ty = Type.unify
p Reason.URnone
env ty1 ty in
2524 let int = Reason.Ridx
(fst
e2), Tprim Tint
in
2525 let env, _ = Type.unify
p Reason.URarray_get
env ty2 int in
2531 let idx = int_of_string
(snd n
) in
2532 let nth = List.nth_exn tyl
idx in
2535 Errors.typing_error
p (Reason.string_of_ureason
Reason.URtuple_get
);
2536 env, (Reason.Rwitness
p, Tany
)
2539 Errors.typing_error
p (Reason.string_of_ureason
Reason.URtuple_access
);
2540 env, (Reason.Rwitness
p, Tany
)
2542 | Tclass
((_, cn
) as id
, argl
) when cn
= SN.Collections.cPair
->
2543 let (ty1, ty2) = match argl
with
2544 | [ty1; ty2] -> (ty1, ty2)
2547 let any = (Reason.Rwitness
p, Tany
) in
2553 let idx = int_of_string
(snd n
) in
2554 let nth = List.nth_exn
[ty1; ty2] idx in
2557 Errors.typing_error
p (Reason.string_of_ureason
Reason.URpair_get
);
2558 env, (Reason.Rwitness
p, Tany
)
2561 Errors.typing_error
p (Reason.string_of_ureason
Reason.URpair_access
);
2562 env, (Reason.Rwitness
p, Tany
)
2564 | Tshape
(_, fdm
) ->
2566 let field = TUtils.shape_field_name
env p e2'
in
2567 (match ShapeMap.get
field fdm
with
2569 Errors.undefined_field
p (TUtils.get_printable_shape_field_name
field);
2570 env, (Reason.Rwitness
p, Tany
)
2571 | Some
ty -> env, ty
2574 Errors.null_container
p
2576 "This is what makes me believe it can be null"
2579 env, (Reason.Rwitness
p, Tany
)
2581 if Env.is_strict
env
2582 then error_array
env p ety1
2583 else env, (Reason.Rnone
, Tany
)
2584 | Tabstract
(_, Some
ty) ->
2585 let env, ety
= Env.expand_type
env ty in
2587 (fun () -> array_get
is_lvalue p env ty ety
e2 ty2)
2588 (fun _ -> error_array
env p ety1
)
2589 | Tmixed
| Tprim
_ | Tvar
_ | Tfun
_
2590 | Tabstract
(_, _) | Tclass
(_, _) | Tanon
(_, _) ->
2591 error_array
env p ety1
2593 and array_append
is_lvalue p env ty1 =
2594 let env, ty1 = TUtils.fold_unresolved
env ty1 in
2595 let env, ety1
= Env.expand_type
env ty1 in
2597 | Tany
| Tarraykind AKany
-> env, (Reason.Rnone
, Tany
)
2598 | Tclass
((_, n
), [ty])
2599 when n
= SN.Collections.cVector
|| n
= SN.Collections.cSet
->
2601 | Tclass
((_, n
), [])
2602 when n
= SN.Collections.cVector
|| n
= SN.Collections.cSet
->
2603 (* Handle the case where "Vector" or "Set" was used as a typehint
2604 without type parameters *)
2605 env, (Reason.Rnone
, Tany
)
2606 | Tclass
((_, n
), [tkey
; tvalue
]) when n
= SN.Collections.cMap
->
2607 (* You can append a pair to a map *)
2608 env, (Reason.Rmap_append
p, Tclass
((p, SN.Collections.cPair
), [tkey
; tvalue
]))
2609 | Tclass
((_, n
), []) when n
= SN.Collections.cMap
->
2610 (* Handle the case where "Map" was used as a typehint without
2612 env, (Reason.Rmap_append
p, Tclass
((p, SN.Collections.cPair
), []))
2613 | Tarraykind
(AKvec
ty) ->
2616 if Env.is_strict
env
2617 then error_array_append
env p ety1
2618 else env, (Reason.Rnone
, Tany
)
2619 | Tabstract
(_, Some
ty) ->
2621 (fun () -> array_append
is_lvalue p env ty)
2622 (fun _ -> error_array_append
env p ety1
)
2623 | Tmixed
| Tarraykind
_ | Toption
_ | Tprim
_
2624 | Tvar
_ | Tfun
_ | Tabstract
(_, _) | Tclass
(_, _) | Ttuple
_
2625 | Tanon
(_, _) | Tunresolved
_ | Tshape
_ ->
2626 error_array_append
env p ety1
2628 and error_array
env p (r, ty) =
2629 Errors.array_access
p (Reason.to_pos
r) (Typing_print.error
ty);
2630 env, (Reason.Rwitness
p, Tany
)
2632 and error_array_append
env p (r, ty) =
2633 Errors.array_append
p (Reason.to_pos
r) (Typing_print.error
ty);
2634 env, (Reason.Rwitness
p, Tany
)
2636 and error_const_mutation
env p (r, ty) =
2637 Errors.const_mutation
p (Reason.to_pos
r) (Typing_print.error
ty);
2638 env, (Reason.Rwitness
p, Tany
)
2641 * Checks if a class (given by cty) contains a given static method.
2643 * We could refactor this + class_get
2645 and class_contains_smethod
env cty
(_pos
, mid
) =
2647 | _, Tabstract
(_, Some
(_, Tclass
((_, c), _)))
2648 | _, Tclass
((_, c), _) ->
2649 let class_ = Env.get_class
env c in
2653 Env.get_static_member
true env class_ mid
2655 | _, (Tany
| Tmixed
| Tarraykind
_ | Toption
_ | Tprim
_
2656 | Tvar
_ | Tfun
_ | Tabstract
(_, _) | Ttuple
_ | Tanon
(_, _)
2657 | Tunresolved
_ | Tobject
| Tshape
_)-> None
2659 and class_get ~
is_method ~is_const ?
(incl_tc
=false) env cty
(p, mid
) cid =
2662 this_for_method
env cid cty
2666 type_expansions
= [];
2668 substs
= SMap.empty
;
2669 from_class
= Some
cid;
2671 class_get_ ~
is_method ~is_const ~
ety_env ~incl_tc
env cty
(p, mid
)
2673 and class_get_ ~
is_method ~is_const ~
ety_env ?
(incl_tc
=false) env cty
(p, mid
) =
2675 | _, Tabstract
(_, Some cty
) ->
2676 class_get_ ~
is_method ~is_const ~
ety_env ~incl_tc
env cty
(p, mid
)
2677 | _, Tclass
((_, c), paraml
) ->
2678 let class_ = Env.get_class
env c in
2680 | None
-> env, (Reason.Rnone
, Tany
)
2685 then Env.get_const
env class_ mid
2686 else (match Env.get_typeconst
env class_ mid
with
2688 Errors.illegal_typeconst_direct_access
p;
2691 Env.get_const
env class_ mid
))
2692 else Env.get_static_member
is_method env class_ mid
in
2693 if !Typing_defs.accumulate_method_calls
then
2694 Typing_defs.accumulate_method_calls_result
:=
2695 (p, (class_.tc_name^
"::"^mid
)) ::
2696 !Typing_defs.accumulate_method_calls_result
;
2697 Typing_hooks.dispatch_smethod_hook
2698 class_ (p, mid
) env ety_env.from_class ~
is_method;
2700 | None
when not
is_method ->
2701 smember_not_found
p ~is_const ~
is_method class_ mid
;
2702 env, (Reason.Rnone
, Tany
)
2704 (match Env.get_static_member
is_method env class_ SN.Members.__callStatic
with
2706 smember_not_found
p ~is_const ~
is_method class_ mid
;
2707 env, (Reason.Rnone
, Tany
)
2708 | Some
{ce_visibility
= vis
; ce_type
= (r, Tfun
ft); _} ->
2709 check_visibility
p env (Reason.to_pos
r, vis
)
2713 substs
= TSubst.make
class_.tc_tparams paraml
} in
2714 let env, ft = Phase.localize_ft ~
ety_env env ft in
2716 ft_arity
= Fellipsis
0;
2717 ft_tparams
= []; ft_params
= [];
2720 | _ -> assert false)
2721 | Some
{ ce_visibility
= vis
; ce_type
= method_
; _ } ->
2722 check_visibility
p env (Reason.to_pos
(fst method_
), vis
)
2726 substs
= TSubst.make
class_.tc_tparams paraml
} in
2728 Phase.localize ~
ety_env env method_
in
2731 | _, (Tany
| Tabstract
_) ->
2732 (match Env.get_mode
env with
2733 | FileInfo.Mstrict
-> Errors.expected_class
p
2734 | FileInfo.Mdecl
| FileInfo.Mpartial
-> ()
2736 env, (Reason.Rnone
, Tany
)
2737 | _, (Tmixed
| Tarraykind
_ | Toption
_ | Tprim
_ | Tvar
_
2738 | Tfun
_ | Ttuple
_ | Tanon
(_, _) | Tunresolved
_
2739 | Tobject
| Tshape
_) ->
2740 Errors.expected_class
p;
2741 env, (Reason.Rnone
, Tany
)
2743 and smember_not_found
pos ~is_const ~
is_method class_ member_name
=
2745 if is_const
then `class_constant
2746 else if is_method then `static_method
2747 else `class_variable
in
2749 let cid = (class_.tc_pos
, class_.tc_name
) in
2750 Errors.smember_not_found
kind pos cid member_name hint
2752 match Env.suggest_static_member
is_method class_ member_name
with
2754 (match Env.suggest_member
is_method class_ member_name
with
2755 | None
when not
class_.tc_members_fully_known
->
2756 (* no error in this case ... the member might be present
2757 * in one of the parents of class_ that the typing cannot see *)
2762 error (`closest
(pos2
, v
))
2765 error (`did_you_mean
(pos2
, v
))
2767 and member_not_found
pos ~
is_method class_ member_name
r =
2768 let kind = if is_method then `method_
else `member
in
2769 let cid = class_.tc_pos
, class_.tc_name
in
2770 let reason = Reason.to_string
2771 ("This is why I think it is an object of type "^strip_ns
class_.tc_name
) r
2774 Errors.member_not_found
kind pos cid member_name hint
reason in
2775 match Env.suggest_member
is_method class_ member_name
with
2777 (match Env.suggest_static_member
is_method class_ member_name
with
2778 | None
when not
class_.tc_members_fully_known
->
2779 (* no error in this case ... the member might be present
2780 * in one of the parents of class_ that the typing cannot see *)
2784 | Some
(def_pos
, v
) ->
2785 error (`closest
(def_pos
, v
))
2787 | Some
(def_pos
, v
) ->
2788 error (`did_you_mean
(def_pos
, v
))
2790 (* The type of the object member is passed into the continuation k. This is
2791 * useful for typing nullsafed method calls. Consider `$x?->f()`: obj_get will
2792 * pass `k` the type of f, and `k` will typecheck the method call and return
2793 * the method's return type. obj_get then wraps that type in a Toption. *)
2794 and obj_get ~
is_method ~
nullsafe env ty1 cid id k
=
2797 | Some
p when not
(type_could_be_null
env ty1) ->
2798 let env, (r, _) = Env.expand_type
env ty1 in
2799 Errors.nullsafe_not_needed
p
2801 "This is what makes me believe it cannot be null" r);
2804 let env, method_
, _ =
2805 obj_get_with_visibility ~
is_method ~
nullsafe env ty1 cid id k
in
2808 and obj_get_with_visibility ~
is_method ~
nullsafe env ty1
2810 obj_get_ ~
is_method ~
nullsafe env ty1 cid id k
(fun ty -> ty)
2812 and obj_get_ ~
is_method ~
nullsafe env ty1 cid (p, s
as id
)
2814 let env, ety1
= Env.expand_type
env ty1 in
2816 | _, Tunresolved tyl
->
2817 let (env, vis
), tyl
=
2819 (fun (env, vis
) ty ->
2821 obj_get_ ~
is_method ~
nullsafe env ty cid id k
k_lhs in
2822 (* There is one special case where we need to expose the
2823 * visibility outside of obj_get (checkout inst_meth special
2825 * We keep a witness of the "most restrictive" visibility
2826 * we encountered (position + visibility), to be able to
2827 * special case inst_meth.
2829 let vis = TUtils.min_vis_opt
vis vis'
in
2833 let env, method_
= TUtils.in_var
env (fst ety1
, Tunresolved
(tyl
)) in
2835 | p, Tabstract
(ak
, Some
ty) ->
2836 (* We probably don't want to rewrap new types for the 'this' closure *)
2837 let k_lhs'
ty = match ak
with
2838 | AKnewtype
(_, _) -> k_lhs ty
2839 | _ -> k_lhs (p, Tabstract
(ak
, Some
ty)) in
2840 obj_get_ ~
is_method ~
nullsafe env ty cid id k
k_lhs'
2841 | _, Toption
ty -> begin match nullsafe with
2843 let k'
(env, fty, x) = begin
2844 let env, method_
, x = k (env, fty, x) in
2845 let env, method_
= non_null
env method_
in
2846 env, (Reason.Rnullsafe_op
p1, Toption method_
), x
2848 obj_get_ ~
is_method ~
nullsafe env ty cid id
k'
k_lhs
2850 Errors.null_member s
p
2852 "This is what makes me believe it can be null"
2855 k (env, (fst ety1
, Tany
), None
)
2857 | _, (Tany
| Tmixed
| Tarraykind
_ | Tprim
_ | Tvar
_
2858 | Tfun
_ | Tabstract
(_, _) | Tclass
(_, _) | Ttuple
_ | Tanon
(_, _)
2859 | Tobject
| Tshape
_) -> k begin
2861 | Tclass
(x, paraml
) ->
2862 let class_ = Env.get_class
env (snd
x) in
2865 env, (Reason.Rnone
, Tany
), None
2866 | Some
class_ when not
is_method
2867 && not
(Env.is_strict
env)
2868 && class_.tc_name
= SN.Classes.cStdClass
->
2869 env, (Reason.Rnone
, Tany
), None
2872 if List.length
paraml = 0
2873 then List.map
class_.tc_tparams
(fun _ -> Reason.Rwitness
p, Tany
)
2876 let member_ = Env.get_member
is_method env class_ s
in
2877 if !Typing_defs.accumulate_method_calls
then
2878 Typing_defs.accumulate_method_calls_result
:=
2879 (p, (class_.tc_name^
"::"^s
)) ::
2880 !Typing_defs.accumulate_method_calls_result
;
2881 Typing_hooks.dispatch_cmethod_hook
2882 class_ (p, s
) env None ~
is_method;
2884 | None
when not
is_method ->
2885 if not
(SN.Members.is_special_xhp_attribute s
)
2886 then member_not_found
p ~
is_method class_ s
(fst ety1
);
2887 env, (Reason.Rnone
, Tany
), None
2889 (match Env.get_member
is_method env class_ SN.Members.__call
with
2891 member_not_found
p ~
is_method class_ s
(fst ety1
);
2892 env, (Reason.Rnone
, Tany
), None
2893 | Some
{ce_visibility
= vis; ce_type
= (r, Tfun
ft); _} ->
2894 let mem_pos = Reason.to_pos
r in
2895 check_visibility
p env (mem_pos, vis) None
;
2896 (* the return type of __call can depend on the
2897 * class params or be this *)
2898 let this_ty = k_lhs ety1
in
2900 type_expansions
= [];
2902 substs
= TSubst.make
class_.tc_tparams
paraml;
2903 from_class
= Some
cid;
2905 let env, ft = Phase.localize_ft ~
ety_env env ft in
2907 (* we change the params of the underlying
2908 * declaration to act as a variadic function
2909 * ... this transform cannot be done when
2910 * processing the declaration of call because
2911 * direct calls to $inst->__call are also
2914 ft_arity
= Fellipsis
0;
2915 ft_tparams
= []; ft_params
= [];
2917 let member_ = (r, Tfun
ft) in
2918 env, member_, Some
(mem_pos, vis)
2921 | Some
({ce_visibility
= vis; ce_type
= member_; _ } as member_ce
) ->
2922 let mem_pos = Reason.to_pos
(fst
member_) in
2923 check_visibility
p env (mem_pos, vis) None
;
2924 let member_ = Typing_enum.member_type
env member_ce
in
2925 let this_ty = k_lhs ety1
in
2927 type_expansions
= [];
2929 substs
= TSubst.make
class_.tc_tparams
paraml;
2930 from_class
= Some
cid;
2932 let env, member_ = Phase.localize ~
ety_env env member_ in
2933 env, member_, Some
(mem_pos, vis)
2937 | Tany
-> env, (fst ety1
, Tany
), None
2938 | (Tmixed
| Tarraykind
_ | Tprim
_ | Toption
_
2939 | Tvar
_ | Tabstract
(_, _) | Ttuple
_ | Tanon
(_, _)
2940 | Tfun
_ | Tunresolved
_ | Tshape
_) as ty ->
2941 Errors.non_object_member
2942 s
p (Typing_print.error ty) (Reason.to_pos
(fst ety1
));
2943 env, (fst ety1
, Tany
), None
2946 and type_could_be_null
env ty1 =
2947 let env, ety1
= Env.expand_type
env ty1 in
2948 match (snd ety1
) with
2949 | Tabstract
(_, Some
ty) -> type_could_be_null
env ty
2950 | Toption
_ | Tabstract
(_, None
) | Tunresolved
_ | Tmixed
| Tany
-> true
2951 | Tarraykind
_ | Tprim
_ | Tvar
_ | Tfun
_
2952 | Tclass
(_, _) | Ttuple
_ | Tanon
(_, _) | Tobject
2955 and class_id_for_new
p env cid =
2956 let env, ty = static_class_id
p env cid in
2957 (* Instantiation on an abstract class (e.g. from classname<T>) is via the
2958 * base type (to check contructor args), but the actual type `ty` must be
2960 match TUtils.get_base_type
ty with
2961 | _, Tclass
(sid
, _) ->
2962 let class_ = Env.get_class
env (snd sid
) in
2963 env, (match class_ with
2965 | Some
class_ -> Some
(sid
, class_, ty)
2967 | _, (Tany
| Tmixed
| Tarraykind
_ | Toption
_ | Tprim
_
2968 | Tvar
_ | Tfun
_ | Tabstract
(_, _) | Ttuple
_ | Tanon
(_, _)
2969 | Tunresolved
_ | Tobject
| Tshape
_) -> env, None
2971 (* To be a valid trait declaration, all of its 'require extends' must
2972 * match; since there's no multiple inheritance, it follows that all of
2973 * the 'require extends' must belong to the same inheritance hierarchy
2974 * and one of them should be the child of all the others *)
2975 and trait_most_concrete_req_class trait
env =
2976 SMap.fold
(fun name ty acc
->
2977 let keep = match acc
with
2978 | Some
(c, _ty
) -> SMap.mem
name c.tc_ancestors
2983 let class_ = Env.get_class
env name in
2986 | Some
{ tc_kind
= Ast.Cinterface
; _ } -> acc
2987 | Some
{ tc_kind
= Ast.Ctrait
; _ } ->
2988 (* this is an error case for which the nastCheck spits out
2989 * an error, but does *not* currently remove the offending
2990 * 'require extends' or 'require implements' *)
2992 | Some
c -> Some
(c, ty)
2994 ) trait
.tc_req_ancestors None
2996 (* When invoking a method the class_id is used to determine what class we
2997 * lookup the method in, but the type of 'this' will be the late bound type.
3001 * public static function get(): this { return new static(); }
3003 * public static function alias(): this { return self::get(); }
3006 * In C::alias, when we invoke self::get(), 'self' is resolved to the class
3007 * in the lexical scope (C), so call C::get. However the method is executed in
3008 * the current context, so static inside C::get will be resolved to the late
3009 * bound type (get_called_class() within C::alias).
3011 * This means when determining the type of this, CIparent and CIself should be
3012 * changed to CIstatic. For the other cases of C::get() or $c::get(), we only
3013 * look at the left hand side of the '::' and use the type type associated
3016 * Thus C::get() will return a type C, while $c::get() will return the same
3019 and this_for_method
env cid default_ty
= match cid with
3020 | CIparent
| CIself
| CIstatic
->
3021 let p = Reason.to_pos
(fst default_ty
) in
3022 let env, ty = static_class_id
p env CIstatic
in
3023 env, ExprDepTy.make
env CIstatic
ty
3027 and static_class_id
p env = function
3029 (match Env.get_self
env with
3030 | _, Tclass
((_, self
), _) ->
3031 (match Env.get_class
env self
with
3033 {tc_kind
= Ast.Ctrait
; _}
3035 (match trait_most_concrete_req_class trait
env with
3037 Errors.parent_in_trait
p;
3038 env, (Reason.Rwitness
p, Tany
)
3039 | Some
(_, parent_ty
) ->
3040 (* inside a trait, parent is SN.Typehints.this, but with the
3041 * type of the most concrete class that the trait has
3042 * "require extend"-ed *)
3043 let r = Reason.Rwitness
p in
3044 let env, parent_ty
= Phase.localize_with_self
env parent_ty
in
3045 env, (r, TUtils.this_of parent_ty
)
3048 let parent = Env.get_parent
env in
3049 let parent_defined = snd
parent <> Tany
in
3050 if not
parent_defined
3051 then Errors.parent_undefined
p;
3052 let r = Reason.Rwitness
p in
3053 let env, parent = Phase.localize_with_self
env parent in
3054 (* parent is still technically the same object. *)
3055 env, (r, TUtils.this_of
(r, snd
parent))
3057 | _, (Tany
| Tmixed
| Tarraykind
_ | Toption
_ | Tprim
_
3058 | Tfun
_ | Ttuple
_ | Tshape
_ | Tvar
_
3059 | Tanon
(_, _) | Tunresolved
_ | Tabstract
(_, _) | Tobject
3061 let parent = Env.get_parent
env in
3062 let parent_defined = snd
parent <> Tany
in
3063 if not
parent_defined
3064 then Errors.parent_undefined
p;
3065 let r = Reason.Rwitness
p in
3066 let env, parent = Phase.localize_with_self
env parent in
3067 (* parent is still technically the same object. *)
3068 env, (r, TUtils.this_of
(r, snd
parent))
3071 env, (Reason.Rwitness
p, TUtils.this_of
(Env.get_self
env))
3073 env, (Reason.Rwitness
p, snd
(Env.get_self
env))
3075 let class_ = Env.get_class
env (snd
c) in
3077 | None
-> env, (Reason.Rnone
, Tany
) (* Tobject *)
3079 let env, params = lfold
begin fun env _ ->
3080 TUtils.in_var
env (Reason.Rnone
, Tunresolved
[])
3081 end env class_.tc_tparams
in
3082 env, (Reason.Rwitness
(fst
c), Tclass
(c, params))
3085 let env, ty = expr
env e
in
3086 let rec resolve_ety = fun ty -> begin
3087 let env, ty = TUtils.fold_unresolved
env ty in
3088 let _, ty = Env.expand_type
env ty in
3089 match TUtils.get_base_type
ty with
3090 | _, Tabstract
(AKnewtype
(classname
, [the_cls
]), _) when
3091 classname
= SN.Classes.cClassname
->
3093 | _, Tabstract
(AKgeneric
_, _)
3095 | _, (Tany
| Tmixed
| Tarraykind
_ | Toption
_
3096 | Tprim
_ | Tvar
_ | Tfun
_ | Ttuple
_
3097 | Tabstract
((AKenum
_ | AKdependent
_ | AKnewtype
_), _)
3098 | Tanon
(_, _) | Tunresolved
_ | Tobject
| Tshape
_
3100 if Env.get_mode
env = FileInfo.Mstrict
3101 then Errors.dynamic_class
p;
3104 in env, resolve_ety ty
3106 and call_construct
p env class_ params el uel
cid =
3107 let cstr = Env.get_construct
env class_ in
3108 let mode = Env.get_mode
env in
3109 Typing_hooks.dispatch_constructor_hook
class_ env p;
3110 match (fst
cstr) with
3113 (mode = FileInfo.Mstrict
|| mode = FileInfo.Mpartial
) &&
3114 class_.tc_members_fully_known
3115 then Errors.constructor_no_args
p;
3116 fst
(lfold expr
env el
)
3117 | Some
{ ce_visibility
= vis; ce_type
= m
; _ } ->
3118 check_visibility
p env (Reason.to_pos
(fst m
), vis) None
;
3119 let cid = if cid = CIparent
then CIstatic
else cid in
3120 let env, cid_ty
= static_class_id
p env cid in
3122 type_expansions
= [];
3124 substs
= TSubst.make
class_.tc_tparams
params;
3125 from_class
= Some
cid;
3127 let env, m
= Phase.localize ~
ety_env env m
in
3128 fst
(call
p env m el uel
)
3130 and check_visibility
p env (p_vis
, vis) cid =
3131 match is_visible
env vis cid with
3133 | Some
(msg1
, msg2
) -> Errors.visibility
p msg1 p_vis msg2
3135 and is_visible
env vis cid =
3136 let self_id = Env.get_self_id
env in
3139 | Vprivate
_ when (Env.is_outside_class
env) ->
3140 Some
("You cannot access this member", "This member is private")
3144 let my_class = Env.get_class
env self_id in
3145 begin match my_class with
3146 | Some
{tc_final
= true; _} -> None
3148 ("Private members cannot be accessed with static:: since"
3149 ^
" a child class may also have an identically"
3150 ^
" named private member"),
3151 "This member is private")
3155 "You cannot access a private member with parent::",
3156 "This member is private")
3157 | Some CIself
-> None
3158 | Some
(CI
(_, called_ci
)) when x <> self_id ->
3159 (match Env.get_class
env called_ci
with
3160 | Some
{tc_kind
= Ast.Ctrait
; _} ->
3161 Some
("You cannot access private members"
3162 ^
" using the trait's name (did you mean to use self::?)",
3163 "This member is private")
3165 Some
("You cannot access this member", "This member is private"))
3166 | Some
(CIexpr e
) ->
3167 let env, ty = expr
env e
in
3168 let _, ty = Env.expand_type
env ty in
3169 begin match TUtils.get_base_type
ty with
3170 | _, Tclass
((_, c), _) ->
3171 (match Env.get_class
env c with
3172 | Some
{tc_final
= true; _} -> None
3174 ("Private members cannot be accessed dynamically. "
3175 ^
"Did you mean to use 'self::'?"),
3176 "This member is private"))
3177 | _, (Tany
| Tmixed
| Tarraykind
_ | Toption
_
3178 | Tprim
_ | Tvar
_ | Tfun
_ | Tabstract
(_, _) | Ttuple
_
3179 | Tanon
(_, _) | Tunresolved
_ | Tobject
3180 | Tshape
_) -> assert false
3182 | None
when x <> self_id ->
3183 Some
("You cannot access this member", "This member is private")
3186 | Vprotected
x when x = self_id -> None
3187 | Vprotected
_ when (Env.is_outside_class
env) ->
3188 Some
("You cannot access this member", "This member is protected")
3190 let my_class = Env.get_class
env self_id in
3191 let their_class = Env.get_class
env x in
3192 match cid, their_class with
3193 | Some CI
_, Some
{tc_kind
= Ast.Ctrait
; _} ->
3194 Some
("You cannot access protected members"
3195 ^
" using the trait's name (did you mean to use static:: or self::?)",
3196 "This member is protected")
3198 match my_class, their_class with
3199 | Some
my_class, Some
their_class ->
3200 (* Children can call parent's protected methods and
3201 * parents can call children's protected methods (like a
3203 if SSet.mem
x my_class.tc_extends
3204 || SSet.mem
self_id their_class.tc_extends
3205 || SSet.mem
x my_class.tc_req_ancestors_extends
3206 || not
my_class.tc_members_fully_known
3209 "Cannot access this protected member, you don't extend "^
3211 "This member is protected"
3216 and check_arity ?
(check_min
=true) pos pos_def
(arity:int) exp_arity
=
3217 let exp_min = (Typing_defs.arity_min exp_arity
) in
3218 if check_min
&& arity < exp_min then
3219 Errors.typing_too_few_args
pos pos_def
;
3220 match exp_arity
with
3221 | Fstandard
(_, exp_max
) ->
3222 if (arity > exp_max
)
3223 then Errors.typing_too_many_args
pos pos_def
;
3224 | Fvariadic
_ | Fellipsis
_ -> ()
3226 and check_deprecated
p { ft_pos
; ft_deprecated
; _ } =
3227 match ft_deprecated
with
3228 | Some s
-> Errors.deprecated_use
p ft_pos s
3231 (* The variadic capture argument is an array listing the passed
3232 * variable arguments for the purposes of the function body; callsites
3233 * should not unify with it *)
3234 and variadic_param
env ft =
3235 match ft.ft_arity
with
3236 | Fvariadic
(_, p_ty
) -> env, Some p_ty
3237 | Fellipsis
_ | Fstandard
_ -> env, None
3239 and call
pos env fty el uel
=
3240 let env, ty = call_
pos env fty el uel
in
3241 (* We need to solve the constraints after every single function call.
3242 * The type-checker is control-flow sensitive, the same value could
3243 * have different type depending on the branch that we are in.
3244 * When this is the case, a call could violate one of the constraints
3246 let env = fold_fun_list
env env.Env.todo
in
3249 (* Enforces that e is unpackable. If e is a tuple, appends its unpacked types
3250 * into the e_tyl returned.
3252 and unpack_expr
env e_tyl e
=
3253 let env, ety
= expr
env e
in
3256 (* Todo: Check that tuples are allowed - that is, disallow a tuple
3257 * unpacking after an array unpacking.
3259 let unpacked_e_tyl = List.map tyl
(fun ty -> e
, ty) in
3260 env, e_tyl
@ unpacked_e_tyl, true
3263 let unpack_r = Reason.Runpack_param
pos in
3264 let container_ty = (unpack_r, Tclass
((pos, SN.Collections.cContainer
),
3265 [unpack_r, Tany
])) in
3266 let env = Type.sub_type
pos Reason.URparam
env container_ty ety
in
3270 (* Unpacks uel. If tuples are found, unpacked types are appended to the
3273 and unpack_exprl
env e_tyl uel
=
3274 List.fold_left uel ~init
:(env, e_tyl
, false)
3275 ~
f: begin fun (env, e_tyl
, unpacked_tuple
) e
->
3276 let env, e_tyl
, is_tuple
= unpack_expr
env e_tyl e
in
3277 (env, e_tyl
, is_tuple
|| unpacked_tuple
)
3280 and call_
pos env fty el uel
=
3281 let env, efty
= Env.expand_type
env fty in
3283 | _, (Tany
| Tunresolved
[]) ->
3284 let el = el @ uel
in
3286 (fun env elt
-> begin
3287 let env, arg_ty
= expr
env elt
in
3288 let arg_ty = check_valid_rvalue
pos env arg_ty in
3291 Typing_hooks.dispatch_fun_call_hooks
[] (List.map
(el @ uel
) fst
) env;
3292 env, (Reason.Rnone
, Tany
)
3293 | r, Tunresolved tyl
->
3294 let env, retl
= lmap
(fun env ty -> call
pos env ty el uel
) env tyl
in
3295 TUtils.in_var
env (r, Tunresolved retl
)
3297 (* Typing of format string functions. It is dependent on the arguments (el)
3298 * so it cannot be done earlier.
3300 let env, ft = Typing_exts.retype_magic_func
env ft el in
3301 check_deprecated
pos ft;
3302 let pos_def = Reason.to_pos
r2 in
3303 let env, var_param
= variadic_param
env ft in
3304 let env, e_tyl
= lmap
begin fun env e
->
3305 let env, ty = expr
env e
in
3308 let env, e_tyl
, unpacked_tuple
= unpack_exprl
env e_tyl uel
in
3309 let arity = if unpacked_tuple
3310 then List.length e_tyl
3311 (* Each array unpacked corresponds with at least 1 param. *)
3312 else List.length
el + List.length uel
in
3313 (* If we unpacked an array, we don't check arity exactly. Since each
3314 * unpacked array consumes 1 or many parameters, it is nonsensical to say
3315 * that not enough args were passed in (so we don't do the min check).
3317 let () = check_arity ~check_min
:(uel
= [] || unpacked_tuple
)
3318 pos pos_def arity ft.ft_arity
in
3319 let todos = ref [] in
3320 let env = wfold_left_default (call_param
todos) (env, var_param
)
3321 ft.ft_params e_tyl
in
3322 let env = fold_fun_list
env !todos in
3323 Typing_hooks.dispatch_fun_call_hooks
3324 ft.ft_params
(List.map
(el @ uel
) fst
) env;
3326 | r2, Tanon
(arity, id
) when uel
= [] ->
3327 let env, tyl
= lmap expr
env el in
3328 let anon = Env.get_anonymous
env id
in
3329 let fpos = Reason.to_pos
r2 in
3332 Errors.anonymous_recursive_call
pos;
3333 env, (Reason.Rnone
, Tany
)
3335 let () = check_arity
pos fpos (List.length tyl
) arity in
3336 let tyl = List.map
tyl (fun x -> None
, x) in
3338 | _, Tarraykind
_ when not
(Env.is_strict
env) ->
3339 (* Relaxing call_user_func to work with an array in partial mode *)
3340 env, (Reason.Rnone
, Tany
)
3343 env, (Reason.Rnone
, Tany
)
3346 and call_param
todos env (name, x) ((pos, _ as e
), arg_ty) =
3349 | Some
name -> Typing_suggest.save_param
name env x arg_ty
3351 let arg_ty = check_valid_rvalue
pos env arg_ty in
3353 (* When checking params the type 'x' may be expression dependent. Since
3354 * we store the expression id in the local env for Lvar, we want to apply
3357 let dep_ty = match snd e
with
3358 | Lvar
_ -> ExprDepTy.make
env (CIexpr e
) arg_ty
3360 (* We solve for Tanon types after all the other params because we want to
3361 * typecheck the lambda bodies with as much type information as possible. For
3362 * example, in array_map(fn, x), we might be able to use the type of x to
3363 * infer the type of fn, but if we call sub_type on fn first, we end up
3364 * typechecking its body without the benefit of knowing its full type. If
3365 * fn is typehinted but not x, we could use fn to infer the type of x, but
3366 * in practice the reverse situation is more likely. This rearrangement is
3367 * particularly useful since higher-order functions usually put fn before x.
3371 todos := (fun env ->
3372 Type.sub_type
pos Reason.URparam
env x arg_ty) :: !todos;
3374 | _, (Tany
| Tmixed
| Tarraykind
_ | Toption
_ | Tprim
_
3375 | Tvar
_ | Tfun
_ | Tabstract
(_, _) | Tclass
(_, _) | Ttuple
_
3376 | Tunresolved
_ | Tobject
| Tshape
_) ->
3377 Type.sub_type
pos Reason.URparam
env x dep_ty
3380 Errors.bad_call
p (Typing_print.error ty)
3382 and unop
p env uop
ty =
3385 Async.enforce_not_awaitable
env p ty;
3386 (* !$x (logical not) works with any type, so we just return Tbool *)
3387 env, (Reason.Rlogic_ret
p, Tprim Tbool
)
3389 (* ~$x (bitwise not) only works with int *)
3390 Type.unify
p Reason.URnone
env (Reason.Rarith
p, Tprim Tint
) ty
3397 (* math operators work with int or floats, so we call sub_type *)
3398 let env = Type.sub_type
p Reason.URnone
env (Reason.Rarith
p, Tprim Tnum
) ty in
3401 (* We basically just ignore references *)
3404 and binop in_cond
p env bop
p1 ty1 p2 ty2 =
3405 let expand_num_type env p ty =
3406 let env, ty = TUtils.fold_unresolved
env ty in
3407 let env = Type.sub_type
p Reason.URnone
env (Reason.Rarith
p, Tprim Tnum
) ty in
3408 let env, ety
= Env.expand_type
env ty in
3412 let env, ty1 = TUtils.fold_unresolved
env ty1 in
3413 let env, ty2 = TUtils.fold_unresolved
env ty2 in
3414 let env, ety1
= Env.expand_type
env ty1 in
3415 let env, ety2
= Env.expand_type
env ty2 in
3416 (match ety1
, ety2
with
3417 (* For array<V1>+array<V2> and array<K1,V1>+array<K2,V2>, allow
3418 * the addition to produce a supertype. (We could also handle
3419 * when they have mismatching annotations, but we get better error
3420 * messages if we just let those get unified in the next case. *)
3421 | (_, Tarraykind
(AKmap
_ as ak
)), (_, Tarraykind
(AKmap
_))
3422 | (_, Tarraykind
(AKvec
_ as ak
)), (_, Tarraykind
(AKvec
_)) ->
3423 let env, a_sup
= TUtils.in_var
env (Reason.Rnone
, Tunresolved
[]) in
3424 let env, b_sup
= TUtils.in_var
env (Reason.Rnone
, Tunresolved
[]) in
3425 let res_ty = Reason.Rarray_plus_ret
p, Tarraykind
(
3427 | AKvec
_ -> AKvec a_sup
3428 | AKmap
_ -> AKmap
(a_sup
, b_sup
)
3431 let env = Type.sub_type
p1 Reason.URnone
env res_ty ety1
in
3432 let env = Type.sub_type
p2 Reason.URnone
env res_ty ety2
in
3434 | (_, Tarraykind
_), (_, Tarraykind
_)
3435 | (_, Tany
), (_, Tarraykind
_)
3436 | (_, Tarraykind
_), (_, Tany
) ->
3437 let env, ty = Type.unify
p Reason.URnone
env ty1 ty2 in
3439 | (_, (Tany
| Tmixed
| Tarraykind
_ | Toption
_
3440 | Tprim
_ | Tvar
_ | Tfun
_ | Tabstract
(_, _) | Tclass
(_, _)
3441 | Ttuple
_ | Tanon
(_, _) | Tunresolved
_ | Tobject
| Tshape
_
3443 ), _ -> binop in_cond
p env Ast.Minus
p1 ty1 p2 ty2
3445 | Ast.Minus
| Ast.Star
->
3446 let env, ty1 = TUtils.fold_unresolved
env ty1 in
3447 let env, ty2 = TUtils.fold_unresolved
env ty2 in
3448 let env = Type.sub_type
p1 Reason.URnone
env
3449 (Reason.Rarith
p1, Tprim Tnum
) ty1 in
3450 let env = Type.sub_type
p2 Reason.URnone
env
3451 (Reason.Rarith
p2, Tprim Tnum
) ty2 in
3452 let env, ety1
= Env.expand_type
env ty1 in
3453 let env, ety2
= Env.expand_type
env ty2 in
3454 (match ety1
, ety2
with
3455 | (r, Tprim Tfloat
), _ | _, (r, Tprim Tfloat
) ->
3456 (* if either side is a float then float: 1.0 - 1 -> float *)
3457 env, (r, Tprim Tfloat
)
3458 | (r, Tprim Tnum
), _ | _, (r, Tprim Tnum
) ->
3459 (* if either side is a num, then num: (3 / x) - 1 -> num *)
3460 env, (r, Tprim Tnum
)
3461 | (_, Tprim Tint
), (_, Tprim Tint
) ->
3462 (* Both sides are integers, then integer: 1 - 1 -> int *)
3463 env, (Reason.Rarith_ret
p, Tprim Tint
)
3464 | (_, (Tany
| Tmixed
| Tarraykind
_ | Toption
_
3465 | Tprim
_ | Tvar
_ | Tfun
_ | Tabstract
(_, _) | Tclass
(_, _)
3466 | Ttuple
_ | Tanon
(_, _) | Tunresolved
_ | Tobject
| Tshape
_
3469 (* Either side is unknown, unknown *)
3470 (* TODO um, what? This seems very wrong, particularly where "newtype
3472 * This also causes issues with primitive constraints on generics.
3473 * See test/typecheck/generic_primitive_invariant.php as an example *)
3476 let env, ety1
= expand_num_type env p1 ty1 in
3477 let env, ety2
= expand_num_type env p2 ty2 in
3478 (match ety1
, ety2
with
3479 | (r, Tprim Tfloat
), _ | _, (r, Tprim Tfloat
) -> env, (r, Tprim Tfloat
)
3480 | (_, (Tany
| Tmixed
| Tarraykind
_ | Toption
_
3481 | Tprim
_ | Tvar
_ | Tfun
_ | Tabstract
(_, _) | Tclass
(_, _)
3482 | Ttuple
_ | Tanon
(_, _) | Tunresolved
_ | Tobject
| Tshape
_
3484 ), _ -> env, (Reason.Rret_div
p, Tprim Tnum
)
3487 let env, ety1
= expand_num_type env p1 ty1 in
3488 let env, ety2
= expand_num_type env p2 ty2 in
3489 (match ety1
, ety2
with
3490 | (r, Tprim Tfloat
), _ | _, (r, Tprim Tfloat
) -> env, (r, Tprim Tfloat
)
3491 | (_, (Tany
| Tmixed
| Tarraykind
_ | Toption
_
3492 | Tprim
_ | Tvar
_ | Tfun
_ | Tabstract
(_, _) | Tclass
(_, _)
3493 | Ttuple
_ | Tanon
(_, _) | Tunresolved
_ | Tobject
| Tshape
_
3495 ), _ -> env, (Reason.Rarith_ret
p, Tprim Tnum
)
3498 let env = Type.sub_type
p Reason.URnone
env (Reason.Rarith
p1, Tprim Tint
) ty1 in
3499 let env = Type.sub_type
p Reason.URnone
env (Reason.Rarith
p1, Tprim Tint
) ty2 in
3500 env, (Reason.Rarith_ret
p, Tprim Tint
)
3502 let env, ty1 = TUtils.fold_unresolved
env ty1 in
3503 let env, ty2 = TUtils.fold_unresolved
env ty2 in
3504 let env, ety1
= Env.expand_type
env ty1 in
3505 let env, ety2
= Env.expand_type
env ty2 in
3506 (match ety1
, ety2
with
3507 | (_, Tprim Tbool
), _ | _, (_, Tprim Tbool
) ->
3508 let env, _ = Type.unify
p Reason.URnone
env ty1 (Reason.Rlogic_ret
p1, Tprim Tbool
) in
3509 let env, _ = Type.unify
p Reason.URnone
env ty2 (Reason.Rlogic_ret
p1, Tprim Tbool
) in
3510 env, (Reason.Rlogic_ret
p, Tprim Tbool
)
3511 | (_, (Tany
| Tmixed
| Tarraykind
_ | Toption
_
3512 | Tprim
_ | Tvar
_ | Tfun
_ | Tabstract
(_, _) | Tclass
(_, _)
3513 | Ttuple
_ | Tanon
(_, _) | Tunresolved
_ | Tobject
| Tshape
_
3516 let env, _ = Type.unify
p Reason.URnone
env ty1 (Reason.Rarith
p1, Tprim Tint
) in
3517 let env, _ = Type.unify
p Reason.URnone
env ty2 (Reason.Rarith
p1, Tprim Tint
) in
3518 env, (Reason.Rarith_ret
p, Tprim Tint
)
3520 | Ast.Eqeq
| Ast.Diff
->
3521 env, (Reason.Rcomp
p, Tprim Tbool
)
3522 | Ast.EQeqeq
| Ast.Diff2
->
3524 then TypingEqualityCheck.assert_nontrivial
p bop
env ty1 ty2;
3525 env, (Reason.Rcomp
p, Tprim Tbool
)
3526 | Ast.Lt
| Ast.Lte
| Ast.Gt
| Ast.Gte
->
3527 let ty_num = (Reason.Rcomp
p, Tprim
Nast.Tnum
) in
3528 let ty_string = (Reason.Rcomp
p, Tprim
Nast.Tstring
) in
3530 (Reason.Rcomp
p, Tclass
((p, SN.Classes.cDateTime
), [])) in
3532 SubType.is_sub_type
env ty ty1 && SubType.is_sub_type
env ty ty2 in
3533 if both_sub ty_num || both_sub ty_string || both_sub ty_datetime
3534 then env, (Reason.Rcomp
p, Tprim Tbool
)
3536 (* TODO this is questionable; PHP's semantics for conversions with "<"
3537 * are pretty crazy and we may want to just disallow this? *)
3538 let env, _ = Type.unify
p Reason.URnone
env ty1 ty2 in
3539 env, (Reason.Rcomp
p, Tprim Tbool
)
3541 let env = SubType.sub_string
p1 env ty1 in
3542 let env = SubType.sub_string
p2 env ty2 in
3543 env, (Reason.Rconcat_ret
p, Tprim Tstring
)
3546 env, (Reason.Rlogic_ret
p, Tprim Tbool
)
3547 | Ast.Amp
| Ast.Bar
| Ast.Ltlt
| Ast.Gtgt
->
3548 let env = Type.sub_type
p Reason.URnone
env (Reason.Rbitwise
p1, Tprim Tint
) ty1 in
3549 let env = Type.sub_type
p Reason.URnone
env (Reason.Rbitwise
p2, Tprim Tint
) ty2 in
3550 env, (Reason.Rbitwise_ret
p, Tprim Tint
)
3554 and non_null ?expanded
:(expanded
=ISet.empty
) env ty =
3555 let env, expanded
, ty = Env.expand_type_recorded
env expanded
ty in
3558 let env, expanded
, ty = Env.expand_type_recorded
env expanded
ty in
3559 (* When "??T" appears in the typing environment due to implicit
3560 * typing, the recursion here ensures that it's treated as
3561 * isomorphic to "?T"; that is, all nulls are created equal.
3563 non_null ~expanded
env ty
3564 | r, Tunresolved
tyl ->
3565 let env, tyl = lfold
(non_null ~expanded
) env tyl in
3566 (* We need to flatten the unresolved types, otherwise we could
3567 * end up with "Tunresolved[Tunresolved _]" which is not supposed
3570 let tyl = List.fold_right
tyl ~
f:begin fun ty tyl ->
3572 | _, Tunresolved l
-> l
@ tyl
3575 env, (r, Tunresolved
tyl)
3576 | r, Tabstract
(ak
, Some
ty) ->
3577 let env, ty = non_null ~expanded
env ty in
3578 env, (r, Tabstract
(ak
, Some
ty))
3579 | _, (Tany
| Tmixed
| Tarraykind
_ | Tprim
_ | Tvar
_
3580 | Tabstract
(_, _) | Tclass
(_, _) | Ttuple
_ | Tanon
(_, _) | Tfun
_
3581 | Tobject
| Tshape
_) ->
3584 and condition_var_non_null
env = function
3586 let env, x_ty
= Env.get_local
env x in
3587 let env, x_ty
= Env.expand_type
env x_ty
in
3588 let env, x_ty
= non_null
env x_ty
in
3589 Env.set_local
env x x_ty
3590 | p, Class_get
(cname
, (_, member_name
)) as e
->
3591 let env, ty = expr
env e
in
3592 let env, local = Env.FakeMembers.make_static
p env cname member_name
in
3593 let env = Env.set_local
env local ty in
3594 let local = p, Lvar
(p, local) in
3595 condition_var_non_null
env local
3596 | p, Obj_get
((_, This
| _, Lvar
_ as obj
),
3597 (_, Id
(_, member_name
)),
3599 let env, ty = expr
env e
in
3600 let env, local = Env.FakeMembers.make
p env obj member_name
in
3601 let env = Env.set_local
env local ty in
3602 let local = p, Lvar
(p, local) in
3603 condition_var_non_null
env local
3606 and condition_isset
env = function
3607 | _, Array_get
(x, _) -> condition_isset
env x
3608 | v
-> condition_var_non_null
env v
3611 * Build an environment for the true or false branch of
3612 * conditional statements.
3614 and condition
env tparamet
=
3615 let expr = raw_expr ~in_cond
:true in function
3616 | _, Expr_list
[] -> env
3617 | _, Expr_list
[x] ->
3618 let env, _ = expr env x in
3619 condition
env tparamet
x
3620 | r, Expr_list
(x::xs
) ->
3621 let env, _ = expr env x in
3622 condition
env tparamet
(r, Expr_list xs
)
3623 | _, Call
(Cnormal
, (_, Id
(_, func
)), [param], [])
3624 when SN.PseudoFunctions.isset
= func
&& tparamet
&&
3625 not
(Env.is_strict
env) ->
3626 condition_isset
env param
3627 | _, Call
(Cnormal
, (_, Id
(_, func
)), [e
], [])
3628 when not tparamet
&& SN.StdlibFunctions.is_null
= func
->
3629 condition_var_non_null
env e
3630 | r, Binop
((Ast.Eqeq
| Ast.EQeqeq
as bop
),
3632 | r, Binop
((Ast.Eqeq
| Ast.EQeqeq
as bop
),
3633 e
, (_, Null
)) when not tparamet
->
3634 let env, x_ty
= expr env e
in
3635 let env, x_ty
= Env.expand_type
env x_ty
in
3637 if bop
== Ast.Eqeq
then check_null_wtf
env r x_ty
else env in
3638 condition_var_non_null
env e
3639 | (p, (Lvar
_ | Obj_get
_ | Class_get
_) as e
) ->
3640 let env, ty = expr env e
in
3641 let env, ety
= Env.expand_type
env ty in
3643 | _, Tarraykind AKany
3644 | _, Tprim Tbool
-> env
3645 | _, (Tany
| Tmixed
| Tarraykind
_ | Toption
_
3646 | Tprim
_ | Tvar
_ | Tfun
_ | Tabstract
(_, _) | Tclass
(_, _)
3647 | Ttuple
_ | Tanon
(_, _) | Tunresolved
_ | Tobject
| Tshape
_
3649 condition
env (not tparamet
) (p, Binop
(Ast.Eqeq
, e
, (p, Null
))))
3650 | r, Binop
(Ast.Eq None
, var
, e
) when tparamet
->
3651 let env, e_ty
= expr env e
in
3652 let env, e_ty
= Env.expand_type
env e_ty
in
3653 let env = check_null_wtf
env r e_ty
in
3654 condition_var_non_null
env var
3655 | p1, Binop
(Ast.Eq None
, (_, (Lvar
_ | Obj_get
_) as lv
), (p2, _)) ->
3656 let env, _ = expr env (p1, Binop
(Ast.Eq None
, lv
, (p2, Null
))) in
3657 condition
env tparamet lv
3658 | p, Binop
((Ast.Diff
| Ast.Diff2
as op
), e1
, e2) ->
3659 let op = if op = Ast.Diff
then Ast.Eqeq
else Ast.EQeqeq
in
3660 condition
env (not tparamet
) (p, Binop
(op, e1
, e2))
3661 | _, Binop
(Ast.AMpamp
, e1
, e2) when tparamet
->
3662 let env = condition
env true e1
in
3663 let env = condition
env true e2 in
3665 | _, Binop
(Ast.BArbar
, e1
, e2) when not tparamet
->
3666 let env = condition
env false e1
in
3667 let env = condition
env false e2 in
3669 | _, Call
(Cnormal
, (_, Id
(_, f)), [lv
], [])
3670 when tparamet
&& f = SN.StdlibFunctions.is_array
->
3672 | _, Call
(Cnormal
, (_, Id
(_, f)), [lv
], [])
3673 when tparamet
&& f = SN.StdlibFunctions.is_int
->
3675 | _, Call
(Cnormal
, (_, Id
(_, f)), [lv
], [])
3676 when tparamet
&& f = SN.StdlibFunctions.is_bool
->
3677 is_type
env lv Tbool
3678 | _, Call
(Cnormal
, (_, Id
(_, f)), [lv
], [])
3679 when tparamet
&& f = SN.StdlibFunctions.is_float
->
3680 is_type
env lv Tfloat
3681 | _, Call
(Cnormal
, (_, Id
(_, f)), [lv
], [])
3682 when tparamet
&& f = SN.StdlibFunctions.is_string
->
3683 is_type
env lv Tstring
3684 | _, Call
(Cnormal
, (_, Id
(_, f)), [lv
], [])
3685 when tparamet
&& f = SN.StdlibFunctions.is_resource
->
3686 is_type
env lv Tresource
3687 | _, Unop
(Ast.Unot
, e
) ->
3688 condition
env (not tparamet
) e
3689 | p, InstanceOf
(ivar
, cid) when tparamet
&& is_instance_var ivar
->
3690 let env, (ivar_pos
, x) = get_instance_var
env ivar
in
3691 let env, x_ty
= Env.get_local
env x in
3692 let env, x_ty
= Env.expand_type
env x_ty
in (* We don't want to modify x *)
3693 (* XXX the position p here is not really correct... it's the position
3694 * of the instanceof expression, not the class id. But we don't store
3695 * position data for the latter. *)
3696 let env, obj_ty = static_class_id
p env cid in
3698 | _, Tabstract
(AKgeneric
_, _) ->
3699 Env.set_local
env x obj_ty
3700 | _, Tabstract
(AKdependent
(`this
, []), Some
(_, Tclass
_)) ->
3702 (* Technically instanceof static is not strong enough to prove
3703 * that a type is exactly the same as the late bound type.
3704 * For now we allow this lie to exist. To solve
3705 * this we either need to create a new type that means
3706 * subtype of static or provide a way of specifying exactly
3707 * the late bound type i.e. $x::class === static::class
3709 if cid = CIstatic
then
3710 ExprDepTy.make
env CIstatic
obj_ty
3713 let env = Env.set_local
env x obj_ty in
3715 | _, Tclass
((_, cid as _c
), _) ->
3716 let class_ = Env.get_class
env cid in
3718 | None
-> Env.set_local
env x (Reason.Rwitness ivar_pos
, Tobject
)
3720 if SubType.is_sub_type
env obj_ty x_ty
3722 (* If the right side of the `instanceof` object is
3723 * a super type of what we already knew. In this case,
3724 * since we already have a more specialized object, we
3725 * don't touch the original object. Check out the unit
3726 * test srecko.php if this is unclear.
3728 * Note that if x_ty is Tany, no amount of subtype
3729 * checking will be able to specify it
3730 * further. This is arguably desirable to maintain
3731 * the invariant that removing annotations gets rid
3732 * of typing errors in partial mode (See also
3735 else Env.set_local
env x obj_ty
3737 | _, (Tany
| Tmixed
| Tarraykind
_ | Toption
_
3738 | Tprim
_ | Tvar
_ | Tfun
_ | Tabstract
(_, _) | Ttuple
_
3739 | Tanon
(_, _) | Tunresolved
_ | Tobject
3740 | Tshape
_) -> Env.set_local
env x (Reason.Rwitness ivar_pos
, Tobject
)
3742 | _, Binop
((Ast.Eqeq
| Ast.EQeqeq
), e
, (_, Null
))
3743 | _, Binop
((Ast.Eqeq
| Ast.EQeqeq
), (_, Null
), e
) ->
3744 let env, _ = expr env e
in
3747 let env, _ = expr env e
in
3750 and is_instance_var
= function
3751 | _, (Lvar
_ | This
) -> true
3752 | _, Obj_get
((_, This
), (_, Id
_), _) -> true
3753 | _, Obj_get
((_, Lvar
_), (_, Id
_), _) -> true
3754 | _, Class_get
(_, _) -> true
3757 and get_instance_var
env = function
3758 | p, Class_get
(cname
, (_, member_name
)) ->
3759 let env, local = Env.FakeMembers.make_static
p env cname member_name
in
3761 | p, Obj_get
((_, This
| _, Lvar
_ as obj
), (_, Id
(_, member_name
)), _) ->
3762 let env, local = Env.FakeMembers.make
p env obj member_name
in
3764 | _, Lvar
(p, x) -> env, (p, x)
3765 | p, This
-> env, (p, this
)
3766 | _ -> failwith
"Should only be called when is_instance_var is true"
3768 and check_null_wtf
env p ty =
3769 if not
(Env.is_strict
env) then env else
3770 let env, ty = TUtils.fold_unresolved
env ty in
3771 let env, ety
= Env.expand_type
env ty in
3774 let env, ty = Env.expand_type
env ty in
3778 Errors.sketchy_null_check
p
3780 Errors.sketchy_null_check_primitive
p
3781 | _, (Tarraykind
_ | Toption
_ | Tvar
_ | Tfun
_
3782 | Tabstract
(_, _) | Tclass
(_, _) | Ttuple
_ | Tanon
(_, _)
3783 | Tunresolved
_ | Tobject
| Tshape
_ ) -> ());
3785 | _, (Tany
| Tmixed
| Tarraykind
_ | Tprim
_ | Tvar
_
3786 | Tfun
_ | Tabstract
(_, _) | Tclass
(_, _) | Ttuple
_ | Tanon
(_, _)
3787 | Tunresolved
_ | Tobject
| Tshape
_ ) -> env
3789 and is_type
env e tprim
=
3791 | p, Class_get
(cname
, (_, member_name
)) ->
3792 let env, local = Env.FakeMembers.make_static
p env cname member_name
in
3793 Env.set_local
env local (Reason.Rwitness
p, Tprim tprim
)
3794 | p, Obj_get
((_, This
| _, Lvar
_ as obj
), (_, Id
(_, member_name
)), _) ->
3795 let env, local = Env.FakeMembers.make
p env obj member_name
in
3796 Env.set_local
env local (Reason.Rwitness
p, Tprim tprim
)
3798 Env.set_local
env x (Reason.Rwitness
p, Tprim tprim
)
3801 and is_array
env = function
3802 | p, Class_get
(cname
, (_, member_name
)) ->
3803 let env, local = Env.FakeMembers.make_static
p env cname member_name
in
3804 Env.set_local
env local (Reason.Rwitness
p, Tarraykind AKany
)
3805 | p, Obj_get
((_, This
| _, Lvar
_ as obj
), (_, Id
(_, member_name
)), _) ->
3806 let env, local = Env.FakeMembers.make
p env obj member_name
in
3807 Env.set_local
env local (Reason.Rwitness
p, Tarraykind AKany
)
3809 Env.set_local
env x (Reason.Rwitness
p, Tarraykind AKany
)
3812 and string2
env idl
=
3813 List.fold_left idl ~init
:env ~
f:begin fun env x ->
3814 let env, ty = expr env x in
3816 let env = SubType.sub_string
p env ty in
3820 (* If the current class inherits from classes that take type arguments, we need
3821 * to check that the arguments provided are consistent with the constraints on
3822 * the type parameters. *)
3823 and check_implements_tparaml
(env: Env.env) ht
=
3824 let _r, (p, c), paraml = Typing_hint.open_class_hint ht
in
3825 let class_ = Env.get_class_dep
env c in
3828 (* The class lives in PHP land *)
3831 let size1 = List.length
class_.tc_tparams
in
3832 let size2 = List.length
paraml in
3833 if size1 <> size2 then Errors.class_arity
p class_.tc_pos
c size1;
3834 let subst = Inst.make_subst
class_.tc_tparams
paraml in
3835 iter2_shortest
begin fun (_, (p, _), cstr_opt
) ty ->
3838 | Some
(Ast.Constraint_as
, cstr) ->
3839 let cstr = snd
(Inst.instantiate
subst env cstr) in
3840 ignore
(Type.sub_type_decl
p Reason.URnone
env cstr ty);
3841 | Some
(Ast.Constraint_super
, cstr) ->
3842 let cstr = snd
(Inst.instantiate
subst env cstr) in
3843 ignore
(Type.sub_type_decl
p Reason.URnone
env ty cstr);
3844 end class_.tc_tparams
paraml
3846 (* In order to type-check a class, we need to know what "parent"
3847 * refers to. Sometimes people write "parent::", when that happens,
3848 * we need to know the type of parent.
3850 and class_def_parent
env class_def class_type
=
3851 match class_def
.c_extends
with
3852 | (_, Happly
((_, x), _) as parent_ty
) :: _ ->
3853 let parent_type = Env.get_class_dep
env x in
3854 (match parent_type with
3855 | Some
parent_type -> check_parent class_def class_type
parent_type
3857 let env, parent_ty
= Typing_hint.hint
env parent_ty
in
3858 env, Some
x, parent_ty
3859 (* The only case where we have more than one parent class is when
3860 * dealing with interfaces and interfaces cannot use parent.
3863 | _ -> env, None
, (Reason.Rnone
, Tany
)
3865 and check_parent class_def class_type
parent_type =
3866 let position = fst class_def
.c_name
in
3867 (* Are all the parents in Hack? Do we know all their methods?
3868 * If so, let's check that the abstract methods have been implemented.
3870 if class_type
.tc_members_fully_known
3871 then check_parent_abstract
position parent_type class_type
;
3872 if parent_type.tc_final
3873 then Errors.extend_final
position parent_type.tc_pos
parent_type.tc_name
3876 and check_parent_abstract
position parent_type class_type
=
3877 let is_final = class_type
.tc_final
in
3878 if parent_type.tc_kind
= Ast.Cabstract
&&
3879 (class_type
.tc_kind
<> Ast.Cabstract
|| is_final)
3881 check_extend_abstract_meth ~
is_final position class_type
.tc_methods
;
3882 check_extend_abstract_meth ~
is_final position class_type
.tc_smethods
;
3883 check_extend_abstract_const ~
is_final position class_type
.tc_consts
;
3884 check_extend_abstract_typeconst
3885 ~
is_final position class_type
.tc_typeconsts
;
3888 and class_def env_up nenv
_ c =
3889 let c = Naming.class_meth_bodies nenv
c in
3890 if not
!auto_complete
then begin
3891 NastCheck.class_ env_up
c;
3892 NastInitCheck.class_ env_up
c;
3894 let env_tmp = Env.set_root env_up
(Dep.Class
(snd
c.c_name
)) in
3895 let tc = Env.get_class
env_tmp (snd
c.c_name
) in
3898 (* This can happen if there was an error during the declaration
3901 | Some
tc -> class_def_ env_up
c tc
3903 and get_self_from_c
env c =
3904 let _, tparams
= lfold
type_param env (fst
c.c_tparams
) in
3905 let tparams = List.map
tparams begin fun (_, (p, s
), param) ->
3906 Reason.Rwitness
p, Tgeneric
(s
, param)
3908 let ret = Reason.Rwitness
(fst
c.c_name
), Tapply
(c.c_name
, tparams) in
3911 and class_def_ env_up
c tc =
3912 Typing_hooks.dispatch_enter_class_def_hook
c tc;
3913 let env = Env.set_self_id env_up
(snd
c.c_name
) in
3914 let env = Env.set_mode
env c.c_mode
in
3915 let env = Env.set_root
env (Dep.Class
(snd
c.c_name
)) in
3916 let pc, _ = c.c_name
in
3918 lmap
Typing_hint.hint
env (c.c_extends
@ c.c_implements
@ c.c_uses
) in
3919 let env = Typing_hint.check_tparams_instantiable
env (fst
c.c_tparams
) in
3920 Typing_variance.class_ (snd
c.c_name
) tc impl
;
3921 let self = get_self_from_c
env c in
3922 List.iter impl
(check_implements_tparaml
env);
3923 let env, parent_id
, parent = class_def_parent
env c tc in
3924 let is_final = tc.tc_final
in
3925 if (tc.tc_kind
= Ast.Cnormal
|| is_final) && tc.tc_members_fully_known
3927 check_extend_abstract_meth ~
is_final pc tc.tc_methods
;
3928 check_extend_abstract_meth ~
is_final pc tc.tc_smethods
;
3929 check_extend_abstract_const ~
is_final pc tc.tc_consts
;
3930 check_extend_abstract_typeconst ~
is_final pc tc.tc_typeconsts
;
3932 (* For enums, localize makes self:: into an abstract type, which we don't
3934 let env, self = match c.c_kind
with
3935 | Ast.Cenum
-> env, (fst
self, Tclass
(c.c_name
, []))
3936 | Ast.Cinterface
| Ast.Cabstract
| Ast.Ctrait
3937 | Ast.Cnormal
-> Phase.localize_with_self
env self in
3938 let env = Env.set_self
env self in
3939 let env = Env.set_parent
env parent in
3940 let env = match parent_id
with
3942 | Some parent_id
-> Env.set_parent_id
env parent_id
in
3943 if tc.tc_final
then begin
3945 | Ast.Cinterface
-> Errors.interface_final
(fst
c.c_name
)
3946 | Ast.Cabstract
-> ()
3947 | Ast.Ctrait
-> Errors.trait_final
(fst
c.c_name
)
3948 | Ast.Cenum
-> (* the parser won't let enums be final *) assert false
3951 List.iter impl
(class_implements_type
env c);
3952 List.iter
c.c_vars
(class_var_def
env false c);
3953 List.iter
c.c_methods
(method_def
env);
3954 List.iter
c.c_typeconsts
(typeconst_def
env);
3955 let const_types = List.map
c.c_consts
(class_const_def
env) in
3956 let env = Typing_enum.enum_class_check
env tc c.c_consts
const_types in
3957 class_constr_def
env c;
3958 let env = Env.set_static
env in
3959 List.iter
c.c_static_vars
(class_var_def
env true c);
3960 List.iter
c.c_static_methods
(method_def
env);
3961 Typing_hooks.dispatch_exit_class_def_hook
c tc
3963 and check_extend_abstract_meth ~
is_final p smap
=
3964 SMap.iter
begin fun x ce
->
3965 match ce
.ce_type
with
3966 | r, Tfun
{ ft_abstract
= true; _ } ->
3967 Errors.implement_abstract ~
is_final p (Reason.to_pos
r) "method" x
3968 | _, (Tany
| Tmixed
| Tarray
(_, _) | Tgeneric
(_,_) | Toption
_ | Tprim
_
3969 | Tfun
_ | Tapply
(_, _) | Ttuple
_ | Tshape
_ | Taccess
(_, _)
3974 (* Type constants must be bound to a concrete type for non-abstract classes.
3976 and check_extend_abstract_typeconst ~
is_final p smap
=
3977 SMap.iter
begin fun x tc ->
3978 if tc.ttc_type
= None
then
3979 Errors.implement_abstract ~
is_final p (fst
tc.ttc_name
) "type constant" x
3982 and check_extend_abstract_const ~
is_final p smap
=
3983 SMap.iter
begin fun x ce
->
3984 match ce
.ce_type
with
3985 | r, Tgeneric
_ when not ce
.ce_synthesized
->
3986 Errors.implement_abstract ~
is_final p (Reason.to_pos
r) "constant" x
3987 | _, (Tany
| Tmixed
| Tarray
(_, _) | Toption
_ | Tprim
_ | Tfun
_
3988 | Tapply
(_, _) | Ttuple
_ | Tshape
_ | Taccess
(_, _) | Tthis
3992 and typeconst_def
env {
3993 c_tconst_name
= (pos, _);
3994 c_tconst_constraint
;
3997 let env, cstr = opt
Typing_hint.hint_locl
env c_tconst_constraint
in
3998 let env, ty = opt
Typing_hint.hint_locl
env c_tconst_type
in
4000 Option.map2
cstr ty ~
f:(Type.sub_type
pos Reason.URtypeconst_cstr
env)
4003 and class_const_def
env (h
, id
, e
) =
4006 | None
-> env, Env.fresh_type
()
4008 Typing_hint.hint_locl
env h
4012 let env, ty'
= expr env e
in
4013 ignore
(Type.sub_type
(fst id
) Reason.URhint
env ty ty'
);
4017 and class_constr_def
env c =
4018 match c.c_constructor
with
4023 and class_implements_type
env c1 ctype2
=
4024 let env, params = lfold
begin fun env (_, (p, s
), param) ->
4025 let env, param = match param with
4027 let env, ty = Typing_hint.hint
env h
in
4029 | None
-> env, None
in
4030 env, (Reason.Rwitness
p, Tgeneric
(s
, param))
4031 end env (fst c1
.c_tparams
)
4033 let r = Reason.Rwitness
(fst c1
.c_name
) in
4034 let ctype1 = r, Tapply
(c1
.c_name
, params) in
4035 Typing_extends.check_implements
env ctype2
ctype1;
4038 and class_var_def
env is_static
c cv
=
4040 match cv
.cv_expr
with
4041 | None
-> env, Env.fresh_type
()
4042 | Some e
-> expr env e
in
4043 match cv
.cv_type
with
4044 | None
when Env.is_strict
env ->
4045 Errors.add_a_typehint
(fst cv
.cv_id
)
4047 let pos, name = cv
.cv_id
in
4048 let name = if is_static
then "$"^
name else name in
4049 let var_type = Reason.Rwitness
pos, Tany
in
4050 (match cv
.cv_expr
with
4052 Typing_suggest.uninitialized_member
(snd
c.c_name
) name env var_type ty;
4055 Typing_suggest.save_member
name env var_type ty;
4058 | Some
(p, _ as cty
) ->
4060 (* If this is an XHP attribute and we're in strict mode,
4061 relax to partial mode to allow the use of the "array"
4062 annotation without specifying type parameters. Until
4063 recently HHVM did not allow "array" with type parameters
4064 in XHP attribute declarations, so this is a temporary
4065 hack to support existing code for now. *)
4066 (* Task #5815945: Get rid of this Hack *)
4067 if cv
.cv_is_xhp
&& (Env.is_strict
env)
4068 then Env.set_mode
env FileInfo.Mpartial
4070 let env, cty
= Typing_hint.hint_locl ~ensure_instantiable
:true env cty
in
4071 let _ = Type.sub_type
p Reason.URhint
env cty
ty in
4074 and method_def
env m
=
4075 (* reset the expression dependent display ids for each method body *)
4076 Reason.expr_display_id_map
:= IMap.empty
;
4077 Typing_hooks.dispatch_enter_method_def_hook m
;
4078 let env = { env with Env.lenv = Env.empty_local
} in
4079 let env = Env.set_local
env this
(Env.get_self
env) in
4080 let env, ret = match m
.m_ret
with
4081 | None
-> env, (Reason.Rwitness
(fst m
.m_name
), Tany
)
4083 let env, ret = Typing_hint.hint ~ensure_instantiable
:true env ret in
4084 (* If a 'this' type appears it needs to be compatiable with the
4088 { (Phase.env_with_self
env) with
4089 from_class
= Some CIstatic
} in
4090 Phase.localize ~
ety_env env ret in
4091 let m_params = match m
.m_variadic
with
4092 | FVvariadicArg
param -> param :: m
.m_params
4095 let env = Typing_hint.check_params_instantiable
env m_params in
4096 let env = Typing_hint.check_tparams_instantiable
env m
.m_tparams
in
4098 lfold
make_param_local_ty env m_params in
4099 if Env.is_strict
env then begin
4100 List.iter2_exn ~
f:(check_param
env) m_params params;
4102 if Attributes.mem
SN.UserAttributes.uaMemoize m
.m_user_attributes
then
4103 List.iter2_exn ~
f:(check_memoizable
env) m_params params;
4104 let env = List.fold2_exn ~
f:bind_param ~init
:env params m_params in
4105 let nb = Nast.assert_named_body m
.m_body
in
4106 let env = fun_ ~abstract
:m
.m_abstract
env ret (fst m
.m_name
) nb m
.m_fun_kind
in
4108 List.fold_left
(Env.get_todo
env) ~
f:(fun env f -> f env) ~init
:env in
4110 | None
when Env.is_strict
env && snd m
.m_name
<> SN.Members.__destruct
->
4111 (* if we are in strict mode, the only case where we don't want to enforce
4112 * a return type is when the method is a destructor
4114 suggest_return env (fst m
.m_name
) ret
4117 Typing_hooks.dispatch_exit_method_def_hook m
4119 and typedef_def env_up typedef
=
4120 NastCheck.typedef env_up typedef
;
4123 t_constraint
= tcstr
;
4125 t_user_attributes
= _;
4127 ignore
(Typing_hint.hint_locl ~ensure_instantiable
:true env_up hint
);
4128 ignore
(Option.map tcstr
(Typing_hint.check_instantiable env_up
));
4130 | pos, Hshape fdm
->
4131 ignore
(check_shape_keys_validity env_up
pos (ShapeMap.keys fdm
))
4134 (* Calls the method of a class, but allows the f callback to override the
4135 * return value type *)
4136 and overload_function
p env class_id method_id
el uel
f =
4137 let env, ty = static_class_id
p env class_id
in
4138 let env, fty = class_get ~
is_method:true ~is_const
:false env ty
4139 method_id class_id
in
4140 (* call the function as declared to validate arity and input types,
4141 but ignore the result and overwrite with custom one *)
4142 let (env, res
), has_error
= Errors.try_with_error
4143 (fun () -> call
p env fty el uel
, false)
4144 (fun () -> (env, (Reason.Rwitness
p, Tany
)), true) in
4145 (* if there are errors already stop here - going forward would
4146 * report them twice *)
4147 if has_error
then env, res
4148 else f env fty res
el