2 * Copyright (c) 2014, 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
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 r, Tarray
(Some
arr_values, None
)
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 ~localize
:(fun env ty -> env, ty)
203 (* For params we want to resolve to "static" or "$this" depending on the
204 * context. Even though we are passing in CIstatic, resolve_with_class_id
205 * is smart enough to know what to do. Why do this? Consider the following
208 * abstract const type T;
210 * private this::T $val;
212 * final public function __construct(this::T $x) {
216 * public static function create(this::T $x): this {
217 * return new static($x);
221 * class D extends C { const type T = int; }
223 * In __construct() we want to be able to assign $x to $this->val. The type of
224 * $this->val will expand to '$this::T', so we need $x to also be '$this::T'.
225 * We can do this soundly because when we construct a new class such as,
226 * 'new D(0)' we can determine the late static bound type (D) and resolve
227 * 'this::T' to 'D::T' which is int.
229 * A similar line of reasoning is applied for the static method create.
231 let make_param_local_ty env param
=
233 { (Phase.env_with_self
env) with
234 from_class
= Some CIstatic
; } in
238 ~default
:Env.fresh_type
239 ~localize
:(Phase.localize ~
ety_env)
242 let rec fun_decl nenv f
=
243 let tcopt = Naming.typechecker_options nenv
in
244 let env = Env.empty
tcopt (Pos.filename
(fst f
.f_name
)) in
245 let env = Env.set_mode
env f
.f_mode
in
246 let env = Env.set_root
env (Dep.Fun
(snd f
.f_name
)) in
247 let _, ft
= fun_decl_in_env
env f
in
248 Env.add_fun
(snd f
.f_name
) ft
;
251 and ret_from_fun_kind pos kind
=
252 let ty_any = (Reason.Rwitness pos
, Tany
) in
255 let r = Reason.Rret_fun_kind
(pos
, kind
) in
256 r, Tapply
((pos
, SN.Classes.cGenerator
), [ty_any ; ty_any ; ty_any])
257 | Ast.FAsyncGenerator
->
258 let r = Reason.Rret_fun_kind
(pos
, kind
) in
259 r, Tapply
((pos
, SN.Classes.cAsyncGenerator
), [ty_any ; ty_any ; ty_any])
261 let r = Reason.Rret_fun_kind
(pos
, kind
) in
262 r, Tapply
((pos
, SN.Classes.cAwaitable
), [ty_any])
263 | Ast.FSync
-> ty_any
265 and fun_decl_in_env
env f
=
266 let mandatory_init = true in
267 let env, arity_min
, params
= make_params
env mandatory_init 0 f
.f_params
in
268 let env, ret_ty
= match f
.f_ret
with
269 | None
-> env, ret_from_fun_kind
(fst f
.f_name
) f
.f_fun_kind
270 | Some
ty -> Typing_hint.hint
env ty in
271 let env, arity
= match f
.f_variadic
with
272 | FVvariadicArg param
->
273 assert param
.param_is_variadic
;
274 assert (param
.param_expr
= None
);
275 let env, (p_name
, p_ty
) = make_param_ty env Reason.Rnone param
in
276 env, Fvariadic
(arity_min
, (p_name
, p_ty
))
277 | FVellipsis
-> env, Fellipsis
(arity_min
)
278 | FVnonVariadic
-> env, Fstandard
(arity_min
, List.length f
.f_params
)
280 let env, tparams
= lfold type_param
env f
.f_tparams
in
282 ft_pos
= fst f
.f_name
;
284 Attributes.deprecated ~kind
:"function" f
.f_name f
.f_user_attributes
;
287 ft_tparams
= tparams
;
293 and type_param
env (variance
, x
, cstr
) =
294 let env, cstr
= match cstr
with
296 let env, ty = Typing_hint.hint
env h
in
298 | None
-> env, None
in
299 env, (variance
, x
, cstr
)
301 and check_default pos mandatory e
=
302 if not mandatory
&& e
= None
303 then Errors.previous_default pos
306 (* Functions building the types for the parameters of a function *)
307 (* It's not completely trivial because of optional arguments *)
308 and make_param
env mandatory arity param
=
309 let env, ty = make_param_ty env Reason.Rnone param
in
311 if param
.param_is_variadic
then begin
312 assert(param
.param_expr
= None
);
315 check_default
(fst param
.param_id
) mandatory param
.param_expr
;
316 mandatory && param
.param_expr
= None
319 let arity = if mandatory then arity + 1 else arity in
320 env, arity, mandatory, ty
322 and make_params
env mandatory arity paraml
=
324 | [] -> env, arity, []
326 let env, arity, mandatory, ty = make_param
env mandatory arity param
in
327 let env, arity, rest
= make_params
env mandatory arity rl
in
328 env, arity, ty :: rest
330 (* In strict mode, we force you to give a type declaration on a parameter *)
331 (* But the type checker is nice: it makes a suggestion :-) *)
332 and check_param
env param
(_, ty) =
333 match (param
.param_hint
) with
334 | None
-> suggest env (fst param
.param_id
) ty
337 and bind_param
env (_, ty1
) param
=
338 let env, ty2
= opt expr
env param
.param_expr
in
339 Option.iter param
.param_expr
Typing_sequencing.sequence_check_expr
;
340 let ty2 = match ty2 with
341 | None
-> Reason.none
, Tany
344 Typing_suggest.save_param
(param
.param_name
) env ty1
ty2;
345 let env = Type.sub_type
(fst param
.param_id
) Reason.URhint
env ty1
ty2 in
346 Env.set_local
env (snd param
.param_id
) ty1
348 and check_memoizable
env param
(pname
, ty) =
349 let env, ty = Env.expand_type
env ty in
350 let p, _ = param
.param_id
in
352 | _, Tprim
(Tarraykey
| Tbool
| Tint
| Tfloat
| Tstring
| Tnum
)
356 | _, Tprim
(Tvoid
| Tresource
| Tnoreturn
) ->
357 let ty_str = Typing_print.error
(snd
ty) in
358 let msgl = Reason.to_string
("This is "^
ty_str) (fst
ty) in
359 Errors.invalid_memoized_param
p msgl
361 check_memoizable
env param
(pname
, ty)
362 | _, Tshape
(_, fdm
) ->
363 ShapeMap.iter
begin fun name
_ ->
364 match ShapeMap.get name fdm
with
365 | Some
ty -> check_memoizable
env param
(pname
, ty)
367 let ty_str = Typing_print.error
(snd
ty) in
368 let msgl = Reason.to_string
("This is "^
ty_str) (fst
ty) in
369 Errors.invalid_memoized_param
p msgl;
372 List.iter tyl
begin fun ty ->
373 check_memoizable
env param
(pname
, ty)
375 | _, Tabstract
(AKenum
_, _) ->
377 | _, Tabstract
(AKnewtype
(_, _), _) ->
379 let ety_env = Phase.env_with_self
env in
380 Typing_tdef.force_expand_typedef ~
ety_env env ty in
381 check_memoizable
env param
(pname
, t'
)
382 (* Just accept all generic types for now. Stricter checks to come later. *)
383 | _, Tabstract
(AKgeneric
_, _) ->
385 (* For parameter type 'this::TID' defined by 'type const TID as Bar' checks
388 | _, Tabstract
(AKdependent
_, Some
ty) ->
389 check_memoizable
env param
(pname
, ty)
390 (* Allow unconstrined dependent type `abstract type const TID` just as we
391 * allow unconstrained generics. *)
392 | _, Tabstract
(AKdependent
_, None
) ->
394 (* Handling Tunresolved case here for completeness, even though it
395 * shouldn't be possible to have an unresolved type when checking
396 * the method declaration. No corresponding test case for this.
398 | _, Tunresolved tyl
->
399 List.iter tyl
begin fun ty ->
400 check_memoizable
env param
(pname
, ty)
402 (* Allow untyped arrays. *)
403 | _, Tarray
(None
, None
) ->
405 | _, Tarray
(Some
ty, _)
406 | _, Tarray
(_, Some
ty) ->
407 check_memoizable
env param
(pname
, ty)
408 | _, Tclass
(_, _) ->
409 let type_param = Env.fresh_type
() in
412 Tclass
((Pos.none
, SN.Collections.cContainer
), [type_param]) in
413 let env, is_container
=
416 SubType.sub_type
env container_type ty, true)
417 (fun _ -> env, false) in
419 check_memoizable
env param
(pname
, type_param)
422 let memoizable_type =
423 r, Tclass
((Pos.none
, SN.Classes.cIMemoizeParam
), []) in
424 if SubType.is_sub_type
env memoizable_type ty
427 let ty_str = Typing_print.error
(snd
ty) in
428 let msgl = Reason.to_string
("This is "^
ty_str) (fst
ty) in
429 Errors.invalid_memoized_param
p msgl;
434 let ty_str = Typing_print.error
(snd
ty) in
435 let msgl = Reason.to_string
("This is "^
ty_str) (fst
ty) in
436 Errors.invalid_memoized_param
p msgl
438 (*****************************************************************************)
439 (* Now we are actually checking stuff! *)
440 (*****************************************************************************)
441 and fun_def
env nenv
_ f
=
442 (* reset the expression dependent display ids for each function body *)
443 Reason.expr_display_id_map
:= IMap.empty
;
444 Typing_hooks.dispatch_enter_fun_def_hook f
;
445 let nb = Naming.func_body nenv f
in
446 NastCheck.fun_
env f
nb;
447 (* Fresh type environment is actually unnecessary, but I prefer to
448 * have a guarantee that we are using a clean typing environment. *)
451 let env = { env_up
with Env.lenv
= Env.empty_local
} in
452 let env = Env.set_mode
env f
.f_mode
in
453 let env = Env.set_root
env (Dep.Fun
(snd f
.f_name
)) in
456 | None
-> env, (Reason.Rwitness
(fst f
.f_name
), Tany
)
458 Typing_hint.hint_locl ~ensure_instantiable
:true env ret
in
459 let f_params = match f
.f_variadic
with
460 | FVvariadicArg param
-> param
:: f
.f_params
463 let env = Typing_hint.check_params_instantiable
env f_params in
464 let env = Typing_hint.check_tparams_instantiable
env f
.f_tparams
in
466 lfold
make_param_local_ty env f_params in
467 let env = List.fold2_exn ~f
:bind_param ~init
:env params
f_params in
468 let env = fun_
env hret
(fst f
.f_name
) nb f
.f_fun_kind
in
469 let env = fold_fun_list
env env.Env.todo
in
470 if Env.is_strict
env then begin
471 List.iter2_exn
f_params params
(check_param
env);
473 | None
-> suggest_return env (fst f
.f_name
) hret
477 Typing_hooks.dispatch_exit_fun_def_hook f
479 (*****************************************************************************)
480 (* function used to type closures, functions and methods *)
481 (*****************************************************************************)
483 and fun_ ?
(abstract
=false) env hret pos named_body f_kind
=
484 Env.with_return
env begin fun env ->
485 debug_last_pos := pos
;
486 let env = Env.set_return
env hret
in
487 let env = Env.set_fn_kind
env f_kind
in
488 let env = block
env named_body
.fnb_nast
in
489 Typing_sequencing.sequence_check_block named_body
.fnb_nast
;
490 let ret = Env.get_return
env in
492 if Nast_terminality.Terminal.block
env named_body
.fnb_nast
||
494 named_body
.fnb_unsafe
||
497 else fun_implicit_return
env pos
ret named_body
.fnb_nast f_kind
in
498 debug_last_pos := Pos.none
;
502 and fun_implicit_return
env pos
ret _b
= function
503 | Ast.FGenerator
| Ast.FAsyncGenerator
-> env
505 (* A function without a terminal block has an implicit return; the
507 let rty = Reason.Rno_return pos
, Tprim
Nast.Tvoid
in
508 Typing_suggest.save_return
env ret rty;
509 Type.sub_type pos
Reason.URreturn
env ret rty
511 (* An async function without a terminal block has an implicit return;
512 * the Awaitable<void> type *)
513 let r = Reason.Rno_return_async pos
in
514 let rty = r, Tclass
((pos
, SN.Classes.cAwaitable
), [r, Tprim
Nast.Tvoid
]) in
515 Typing_suggest.save_return
env ret rty;
516 Type.sub_type pos
Reason.URreturn
env ret rty
519 List.fold_left stl ~f
:stmt ~init
:env
521 and stmt
env = function
526 let env, ty = expr
env e
in
527 (* NB: this check does belong here and not in expr, even though it only
528 * applies to expressions -- we actually want to perform the check on
529 * statements that are expressions, e.g., "foo();" we want to check, but
530 * "return foo();" we do not even though the expression "foo()" is a
531 * subexpression of the statement "return foo();". *)
533 | Nast.Binop
(Ast.Eq
_, _, _) -> ()
534 | _ -> Async.enforce_not_awaitable
env (fst e
) ty);
537 let env, ty = expr
env e
in
538 Async.enforce_not_awaitable
env (fst e
) ty;
539 let parent_lenv = env.Env.lenv
in
540 let env = condition
env true e
in
541 let env = block
env b1
in
542 let lenv1 = env.Env.lenv
in
543 let env = { env with Env.lenv
= parent_lenv } in
544 let env = condition
env false e
in
545 let env = block
env b2
in
546 let lenv2 = env.Env.lenv
in
547 let terminal1 = Nast_terminality.Terminal.block
env b1
in
548 let terminal2 = Nast_terminality.Terminal.block
env b2
in
549 if terminal1 && terminal2
551 let env = LEnv.integrate
env parent_lenv lenv1 in
552 let env = LEnv.integrate
env env.Env.lenv
lenv2 in
553 LEnv.integrate
env env.Env.lenv
parent_lenv
556 let env = LEnv.integrate
env parent_lenv lenv1 in
557 LEnv.integrate
env env.Env.lenv
lenv2
561 let env = LEnv.integrate
env parent_lenv lenv2 in
562 LEnv.integrate
env env.Env.lenv
lenv1
564 else LEnv.intersect
env parent_lenv lenv1 lenv2
565 | Return
(p, None
) ->
566 let rty = match Env.get_fn_kind
env with
567 | Ast.FSync
-> (Reason.Rwitness
p, Tprim Tvoid
)
569 | Ast.FAsyncGenerator
-> any (* Return type checked against the "yield". *)
570 | Ast.FAsync
-> (Reason.Rwitness
p, Tclass
((p, SN.Classes.cAwaitable
), [(Reason.Rwitness
p, Tprim Tvoid
)])) in
571 let expected_return = Env.get_return
env in
572 Typing_suggest.save_return
env expected_return rty;
573 let env = Type.sub_type
p Reason.URreturn
env expected_return rty in
575 | Return
(p, Some e
) ->
577 let env, rty = expr
env e
in
578 let rty = match Env.get_fn_kind
env with
581 | Ast.FAsyncGenerator
-> any (* Is an error, but caught in NastCheck. *)
582 | Ast.FAsync
-> (Reason.Rwitness
p), Tclass
((p, SN.Classes.cAwaitable
), [rty]) in
583 let expected_return = Env.get_return
env in
584 (match snd
(Env.expand_type
env expected_return) with
586 (* Yell about returning a value from a void function. This catches
587 * more issues than just unifying with void would do -- in particular
588 * just unifying allows you to return a Tany from a void function,
589 * which is clearly wrong. Note this check is best-effort; if the
590 * function returns a generic type which later ends up being Tvoid
591 * then there's not much we can do here. *)
592 Errors.return_in_void
p (Reason.to_pos
r);
594 | _, Tunresolved
_ ->
595 (* we allow return types to grow for anonymous functions *)
596 let env, rty = TUtils.unresolved
env rty in
597 let env, _ = Type.unify
pos Reason.URreturn
env expected_return rty in
599 | _, (Tany
| Tmixed
| Tarray
(_,_) | Toption
_ | Tprim
_
600 | Tvar
_ | Tfun
_ | Tabstract
(_, _) | Tclass
(_, _) | Ttuple
_
601 | Tanon
(_, _) | Tobject
| Tshape
_) ->
602 Typing_suggest.save_return
env expected_return rty;
603 let env = Type.sub_type
pos Reason.URreturn
env expected_return rty in
607 (* NOTE: leaks scope as currently implemented; this matches
608 the behavior in naming (cf. `do_stmt` in naming/naming.ml).
610 let parent_lenv = env.Env.lenv
in
611 let env = Env.freeze_local_env
env in
612 let env = block
env b
in
613 let env, ty = expr
env e
in
614 Async.enforce_not_awaitable
env (fst e
) ty;
615 let after_block = env.Env.lenv
in
617 if env.Env.in_loop
then 1 else Typing_alias.get_depth st
in
618 let env = Env.in_loop
env begin
619 iter_n_acc
alias_depth begin fun env ->
620 let env = condition
env true e
in
621 let env = block
env b
in
626 if NastVisitor.HasContinue.block b
627 then LEnv.fully_integrate
env parent_lenv
629 let env = LEnv.integrate
env parent_lenv env.Env.lenv
in
630 let env = { env with Env.lenv
= after_block } in
633 condition
env false e
634 | While
(e
, b
) as st
->
635 let env, ty = expr
env e
in
636 Async.enforce_not_awaitable
env (fst e
) ty;
637 let parent_lenv = env.Env.lenv
in
638 let env = Env.freeze_local_env
env in
640 if env.Env.in_loop
then 1 else Typing_alias.get_depth st
in
641 let env = Env.in_loop
env begin
642 iter_n_acc
alias_depth begin fun env ->
643 let env = condition
env true e
in
644 let env = block
env b
in
648 let env = LEnv.fully_integrate
env parent_lenv in
649 condition
env false e
650 | For
(e1
, e2
, e3
, b
) as st
->
651 (* For loops leak their initalizer, but nothing that's defined in the
654 let (env, _) = expr
env e1
in (* initializer *)
655 let (env, _) = expr
env e2
in
656 let parent_lenv = env.Env.lenv
in
657 let env = Env.freeze_local_env
env in
659 if env.Env.in_loop
then 1 else Typing_alias.get_depth st
in
660 let env = Env.in_loop
env begin
661 iter_n_acc
alias_depth begin fun env ->
662 let env = condition
env true e2
in (* iteration 0 *)
663 let env = block
env b
in
664 let (env, _) = expr
env e3
in
668 let env = LEnv.fully_integrate
env parent_lenv in
669 condition
env false e2
671 Nast_terminality.SafeCase.check
(fst e
) env cl
;
672 let env, ty = expr
env e
in
673 Async.enforce_not_awaitable
env (fst e
) ty;
674 let env = check_exhaustiveness
env (fst e
) ty cl
in
675 let parent_lenv = env.Env.lenv
in
676 let env, cl
= case_list
parent_lenv ty env cl
in
677 LEnv.intersect_list
env parent_lenv cl
678 | Foreach
(e1
, e2
, b
) as st
->
679 let env, ty1
= expr
env e1
in
680 let env, ty1
= TUtils.fold_unresolved
env ty1
in
681 let env, ety1
= Env.expand_type
env ty1
in
682 let parent_lenv = env.Env.lenv
in
683 let env = Env.freeze_local_env
env in
684 let env, ty2 = as_expr
env (fst e1
) e2
in
685 let env = Type.sub_type
(fst e1
) Reason.URforeach
env ty2 ety1
in
687 if env.Env.in_loop
then 1 else Typing_alias.get_depth st
in
688 let env = Env.in_loop
env begin
689 iter_n_acc
alias_depth begin fun env ->
690 let env = bind_as_expr
env ty2 e2
in
691 let env = block
env b
in
695 let env = LEnv.fully_integrate
env parent_lenv in
697 | Try
(tb
, cl
, fb
) ->
698 let env = try_catch
(tb
, cl
) env in
699 let env = block
env fb
in
702 let env = List.fold_left el ~f
:begin fun env e
->
704 | _, Binop
(Ast.Eq
_, (_, Lvar
(p, x
)), _) ->
705 Env.add_todo
env (TGen.no_generic
p x
)
708 let env, _ = lfold expr
env el
in
712 let env, ty = expr
env e
in
713 exception_ty
p env ty
717 and check_exhaustiveness
env pos ty caselist
=
718 (* Right now we only do exhaustiveness checking for enums. *)
719 let env, (_, ty) = Env.expand_type
env ty in
722 List.fold_left tyl ~init
:env ~f
:begin fun env ty ->
723 check_exhaustiveness
env pos ty caselist
725 | Tabstract
(AKenum id
, _) ->
726 let tc = unsafe_opt
@@ Env.get_enum id
in
727 Typing_enum.check_enum_exhaustiveness
pos tc caselist
;
729 | Tany
| Tmixed
| Tarray
(_, _) | Tclass
_ | Toption
_ | Tprim
_
730 | Tvar
_ | Tfun
_ | Tabstract
(_, _) | Ttuple
_ | Tanon
(_, _)
731 | Tobject
| Tshape
_ -> env
733 and case_list
parent_lenv ty env cl
=
734 let env = { env with Env.lenv
= parent_lenv } in
735 case_list_
parent_lenv ty env cl
737 and try_catch
(tb
, cl
) env =
738 let parent_lenv = env.Env.lenv
in
739 let env = Env.freeze_local_env
env in
740 let env = block
env tb
in
741 let after_try = env.Env.lenv
in
742 let env, term_lenv_l
= lmap
begin fun env (_, _, b
as catch_block
) ->
743 let env, lenv
= catch
parent_lenv after_try env catch_block
in
744 let term = Nast_terminality.Terminal.block
env b
in
748 (Nast_terminality.Terminal.block
env tb
, after_try) :: term_lenv_l in
749 LEnv.intersect_list
env parent_lenv term_lenv_l
751 and case_list_
parent_lenv ty env = function
754 (* TODO this is wrong, should continue on to the other cases, but it
755 * doesn't matter in practice since our parser won't parse default
756 * anywhere but in the last position :) Should fix all of this as well
757 * as totality detection for switch. *)
758 let env = block
env b
in
759 env, [Nast_terminality.Terminal.case
env (Default b
), env.Env.lenv
]
760 | Case
(e
, b
) :: rl
->
761 (* TODO - we should consider handling the comparisons the same
762 * way as Binop Ast.EqEq, since case statements work using ==
763 * comparison rules *)
765 (* The way we handle terminal/nonterminal here is not quite right, you
766 * can still break the type system with things like P3131824. *)
767 let ty_num = (Reason.Rnone
, Tprim
Nast.Tnum
) in
768 let ty_arraykey = (Reason.Rnone
, Tprim
Nast.Tarraykey
) in
769 let both_are_sub_types env tprim ty1
ty2 =
770 (SubType.is_sub_type
env tprim ty1
) &&
771 (SubType.is_sub_type
env tprim
ty2) in
772 if Nast_terminality.Terminal.block
env b
then
773 let env, ty2 = expr
env e
in
775 if (both_are_sub_types env ty_num ty ty2) ||
776 (both_are_sub_types env ty_arraykey ty ty2)
778 else Type.unify
(fst e
) Reason.URnone
env ty ty2 in
779 let env = block
env b
in
780 let lenv = env.Env.lenv in
781 let env, rl
= case_list
parent_lenv ty env rl
in
782 env, (Nast_terminality.Terminal.case
env (Case
(e
, b
)), lenv) :: rl
784 let env, ty2 = expr
env e
in
786 if (both_are_sub_types env ty_num ty ty2) ||
787 (both_are_sub_types env ty_arraykey ty ty2)
789 else Type.unify
(fst e
) Reason.URnone
env ty ty2 in
790 (* Since this block is not terminal we will end up falling through to the
791 * next block. This means the lenv will include what our current
792 * environment is, intersected (or integrated?) with the environment
793 * after executing the block. Example:
795 * $x = 0; // $x = int
798 * $x = ''; // $x = string
801 * $x; // $x = int & string
804 let lenv1 = env.Env.lenv in
805 let env = block
env b
in
806 (* PERF: If the case is empty or a Noop then we do not need to intersect
807 * the lenv since they will be the same.
809 * This saves the cost of intersecting the lenv for the common pattern of
815 let env = match b
with
817 | _ -> LEnv.intersect
env parent_lenv lenv1 env.Env.lenv in
818 case_list_
parent_lenv ty env rl
820 and catch
parent_lenv after_try env (ety
, exn
, b
) =
821 let env = { env with Env.lenv = after_try } in
822 let env = LEnv.fully_integrate
env parent_lenv in
824 let ety_p = (fst ety
) in
825 let env, _ = instantiable_cid
ety_p env cid in
826 let env, ety
= static_class_id
ety_p env cid in
827 let env = exception_ty
ety_p env ety
in
828 let env = Env.set_local
env (snd exn
) ety
in
829 let env = block
env b
in
830 (* Only keep the local bindings if this catch is non-terminal *)
833 and as_expr
env pe
= function
835 let ty = Env.fresh_type
() in
836 let tvector = Tclass
((pe
, SN.Collections.cTraversable
), [ty]) in
837 env, (Reason.Rforeach pe
, tvector)
839 let ty1 = Env.fresh_type
() in
840 let ty2 = Env.fresh_type
() in
841 let tmap = Tclass
((pe
, SN.Collections.cKeyedTraversable
), [ty1; ty2]) in
842 env, (Reason.Rforeach pe
, tmap)
844 let ty = Env.fresh_type
() in
845 let tvector = Tclass
((pe
, SN.Classes.cAsyncIterator
), [ty]) in
846 env, (Reason.Rasyncforeach pe
, tvector)
848 let ty1 = Env.fresh_type
() in
849 let ty2 = Env.fresh_type
() in
850 let tmap = Tclass
((pe
, SN.Classes.cAsyncKeyedIterator
), [ty1; ty2]) in
851 env, (Reason.Rasyncforeach pe
, tmap)
853 and bind_as_expr
env ty aexpr
=
854 let env, ety
= Env.expand_type
env ty in
856 | _, Tclass
((p, _), [ty2]) ->
859 | Await_as_v
(_, ev
) -> fst
(assign
p env ev
ty2)
860 | As_kv
((_, Lvar
(_, k
)), ev
)
861 | Await_as_kv
(_, (_, Lvar
(_, k
)), ev
) ->
862 let env, _ = set_valid_rvalue
p env k
(Reason.Rnone
, Tmixed
) in
863 fst
(assign
p env ev
ty2)
864 | _ -> (* TODO Probably impossible, should check that *)
867 | _, Tclass
((p, _), [ty1; ty2]) ->
870 | Await_as_v
(_, ev
) -> fst
(assign
p env ev
ty2)
871 | As_kv
((_, Lvar
(_, k
)), ev
)
872 | Await_as_kv
(_, (_, Lvar
(_, k
)), ev
) ->
873 let env, _ = set_valid_rvalue
p env k
ty1 in
874 fst
(assign
p env ev
ty2)
875 | _ -> (* TODO Probably impossible, should check that *)
878 | _, (Tany
| Tmixed
| Tarray
(_, _) | Toption
_ | Tprim
_
879 | Tvar
_ | Tfun
_ | Tabstract
(_, _) | Tclass
(_, _) | Ttuple
_
880 | Tanon
(_, _) | Tunresolved
_ | Tobject
| Tshape
_
884 raw_expr ~in_cond
:false env e
886 and raw_expr ~in_cond
env e
=
887 debug_last_pos := fst e
;
888 let valkind = `other
in
889 let env, ty = expr_ ~in_cond ~
valkind env e
in
890 let () = match !expr_hook with
891 | Some f
-> f e
(Typing_expand.fully_expand
env ty)
893 Typing_hooks.dispatch_infer_ty_hook
(LoclTy
ty) (fst e
) env;
897 let valkind = `lvalue
in
898 expr_ ~in_cond
:false ~
valkind env e
900 and expr_ ~in_cond ~
(valkind: [> `lvalue
| `rvalue
| `other
]) env (p, e
) =
902 | Any
-> env, (Reason.Rwitness
p, Tany
)
903 | Array
[] -> env, (Reason.Rwitness
p, Tarray
(None
, None
))
904 | Array
(x
:: rl
as l
) ->
905 check_consistent_fields x rl
;
906 let env, value = TUtils.in_var
env (Reason.Rnone
, Tunresolved
[]) in
908 fold_left_env
(apply_for_env_fold array_field_value
) env [] l
in
909 let has_unknown = List.exists values
(fun (_, ty) -> ty = Tany
) in
911 fold_left_env
(apply_for_env_fold
TUtils.unresolved
) env [] values
in
912 let unify_value = Type.unify
p Reason.URarray_value
in
914 if has_unknown (* If one of the values comes from PHP land,
915 * we have to be conservative and consider that
916 * we don't know what the type of the values are.
918 then env, (Reason.Rnone
, Tany
)
919 else fold_left_env
unify_value env value values
923 env, (Reason.Rwitness
p, Tarray
(Some
value, None
))
925 let env, key
= TUtils.in_var
env (Reason.Rnone
, Tunresolved
[]) in
927 fold_left_env
(apply_for_env_fold array_field_key
) env [] l
in
929 fold_left_env
(apply_for_env_fold
TUtils.unresolved
) env [] keys
in
930 let unify_key = Type.unify
p Reason.URarray_key
in
931 let env, key
= fold_left_env
unify_key env key keys
in
932 env, (Reason.Rwitness
p, Tarray
(Some key
, Some
value))
934 | ValCollection
(name
, el
) ->
935 let env, x
= TUtils.in_var
env (Reason.Rnone
, Tunresolved
[]) in
936 let env, tyl
= lmap expr
env el
in
937 let env, tyl
= lmap
Typing_env.unbind
env tyl
in
938 let env, tyl
= lfold
TUtils.unresolved
env tyl
in
939 let env, v
= fold_left_env
(Type.unify
p Reason.URvector
) env x tyl
in
940 let tvector = Tclass
((p, name
), [v
]) in
941 let ty = Reason.Rwitness
p, tvector in
943 | KeyValCollection
(name
, l
) ->
944 let kl, vl
= List.unzip l
in
945 let env, kl = lfold expr
env kl in
946 let env, kl = lmap
Typing_env.unbind
env kl in
947 let env, vl
= lfold expr
env vl
in
948 let env, vl
= lmap
Typing_env.unbind
env vl
in
949 let env, k
= TUtils.in_var
env (Reason.Rnone
, Tunresolved
[]) in
950 let env, v
= TUtils.in_var
env (Reason.Rnone
, Tunresolved
[]) in
951 let env, kl = lfold
TUtils.unresolved
env kl in
952 let env, k
= fold_left_env
(Type.unify
p Reason.URkey
) env k
kl in
953 let env, vl
= lfold
TUtils.unresolved
env vl
in
954 let env, v
= fold_left_env
(Type.unify
p Reason.URvalue
) env v vl
in
955 let ty = Tclass
((p, name
), [k
; v
])
957 env, (Reason.Rwitness
p, ty)
958 | Clone e
-> expr
env e
959 | This
when Env.is_static
env ->
960 Errors.this_in_static
p;
961 env, (Reason.Rwitness
p, Tany
)
962 | This
when valkind = `lvalue
->
963 Errors.this_lvalue
p;
964 env, (Reason.Rwitness
p, Tany
)
966 let r, _ = Env.get_self
env in
968 then Errors.this_var_outside_class
p;
969 let env, (_, ty) = Env.get_local
env this
in
970 let r = Reason.Rwitness
p in
972 let ty = r, TUtils.this_of
ty in
973 (* '$this' always refers to the late bound static type *)
974 env, ExprDepTy.make
env CIstatic
ty
975 | Assert
(AE_assert e
) ->
976 let env = condition
env true e
in
977 env, (Reason.Rwitness
p, Tprim Tvoid
)
980 env, (Reason.Rwitness
p, Tprim Tbool
)
982 env, (Reason.Rwitness
p, Tprim Tint
)
984 env, (Reason.Rwitness
p, Tprim Tfloat
)
986 let ty = Env.fresh_type
() in
987 env, (Reason.Rwitness
p, Toption
ty)
989 env, (Reason.Rwitness
p, Tprim Tstring
)
991 let env = string2
env idl
in
992 env, (Reason.Rwitness
p, Tprim Tstring
)
994 Typing_hooks.dispatch_id_hook x
env;
995 let env, fty
= fun_type_of_id
env x
in
997 | _, Tfun fty
-> check_deprecated
(fst x
) fty
;
1001 | Id
((cst_pos
, cst_name
) as id
) ->
1002 Typing_hooks.dispatch_id_hook id
env;
1003 (match Env.get_gconst
env cst_name
with
1004 | None
when Env.is_strict
env ->
1005 Errors.unbound_global cst_pos
;
1006 env, (Reason.Rwitness cst_pos
, Tany
)
1008 env, (Reason.Rnone
, Tany
)
1010 Phase.localize_with_self
env ty
1012 | Method_id
(instance
, meth
) ->
1013 (* Method_id is used when creating a "method pointer" using the magic
1014 * inst_meth function.
1016 * Typing this is pretty simple, we just need to check that instance->meth
1017 * is public+not static and then return its type.
1019 let env, ty1 = expr
env instance
in
1020 let env, result
, vis
=
1021 obj_get_with_visibility ~is_method
:true ~nullsafe
:None
env ty1
1022 (CIexpr instance
) meth
(fun x
-> x
) in
1023 let has_lost_info = Env.FakeMembers.is_invalid
env instance
(snd meth
) in
1026 let name = "the method "^snd meth
in
1027 let env, result
= Env.lost_info
name ISet.empty
env result
in
1032 | _, Tfun fty
-> check_deprecated
p fty
1035 | Some
(method_pos
, Vprivate
_) ->
1036 Errors.private_inst_meth method_pos
p
1037 | Some
(method_pos
, Vprotected
_) ->
1038 Errors.protected_inst_meth method_pos
p
1043 | Method_caller
((pos, class_name
) as pos_cname
, meth_name
) ->
1044 (* meth_caller('X', 'foo') desugars to:
1047 let class_ = Env.get_class
env class_name
in
1049 | None
-> unbound_name env pos_cname
1051 (* Create a class type for the given object instantiated with unresolved
1052 * types for its type parameters.
1054 let env, tvarl
= lfold
TUtils.unresolved_tparam
env class_.tc_tparams
in
1055 let params = List.map
class_.tc_tparams
begin fun (_, (p, n
), cstr
) ->
1056 Reason.Rwitness
p, Tgeneric
(n
, cstr
)
1058 let obj_type = Reason.Rwitness
p, Tapply
(pos_cname
, params) in
1060 (Phase.env_with_self
env) with
1061 substs
= TSubst.make
class_.tc_tparams tvarl
;
1063 let env, local_obj_ty
= Phase.localize ~
ety_env env obj_type in
1065 obj_get ~is_method
:true ~nullsafe
:None
env local_obj_ty
1066 (CI
(pos, class_name
)) meth_name
(fun x
-> x
) in
1068 | reason
, Tfun fty
->
1069 check_deprecated
p fty
;
1070 (* We are creating a fake closure:
1071 * function<T as Class>(T $x): return_type_of(Class:meth_name)
1073 let tparam = Ast.Invariant
, pos_cname
, Some
(Ast.Constraint_as
, obj_type) in
1074 let env, tvar
= TUtils.unresolved_tparam
env tparam in
1075 let param = Reason.Rwitness
pos,
1076 Tgeneric
(class_name
, Some
(Ast.Constraint_as
, obj_type)) in
1079 substs
= TSubst.make
(tparam :: class_.tc_tparams
) (tvar
:: tvarl
)
1081 let env, param = Phase.localize ~
ety_env env param in
1082 let fty = { fty with
1083 ft_params
= (None
, param) :: fty.ft_params
} in
1084 let fun_arity = match fty.ft_arity
with
1085 | Fstandard
(min
, max
) -> Fstandard
(min
+ 1, max
+ 1)
1086 | Fvariadic
(min
, x
) -> Fvariadic
(min
+ 1, x
)
1087 | Fellipsis min
-> Fellipsis
(min
+ 1) in
1090 ft_deprecated
= None
;
1091 ft_abstract
= false;
1092 ft_arity
= fun_arity;
1093 ft_tparams
= fty.ft_tparams
;
1094 ft_params
= fty.ft_params
;
1095 ft_ret
= fty.ft_ret
;
1097 env, (reason
, Tfun
caller)
1099 (* This can happen if the method lives in PHP *)
1100 env, (Reason.Rwitness
pos, Tany
)
1103 | Smethod_id
(c
, meth
) ->
1104 (* Smethod_id is used when creating a "method pointer" using the magic
1105 * class_meth function.
1107 * Typing this is pretty simple, we just need to check that c::meth is
1108 * public+static and then return its type.
1110 let class_ = Env.get_class
env (snd c
) in
1113 (* The class given as a static string was not found. *)
1116 let smethod = Env.get_static_member
true env class_ (snd meth
) in
1118 | None
-> (* The static method wasn't found. *)
1119 smember_not_found
p ~is_const
:false ~is_method
:true class_ (snd meth
);
1120 env, (Reason.Rnone
, Tany
)
1123 let env, cid_ty
= static_class_id
(fst c
) env cid in
1125 type_expansions
= [];
1126 substs
= SMap.empty
;
1128 from_class
= Some
cid;
1130 let env, smethod_type
= Phase.localize ~
ety_env env smethod.ce_type
in
1131 (match smethod_type
with
1132 | _, Tfun
fty -> check_deprecated
p fty
1134 (match smethod_type
, smethod.ce_visibility
with
1135 | (r, (Tfun
_ as ty)), Vpublic
->
1137 | (r, Tfun
_), Vprivate
_ ->
1138 Errors.private_class_meth
(Reason.to_pos
r) p;
1140 | (r, Tfun
_), Vprotected
_ ->
1141 Errors.protected_class_meth
(Reason.to_pos
r) p;
1144 (* If this assert fails, we have a method which isn't callable. *)
1150 let r = Reason.Rplaceholder
p in
1151 let ty = r, Tprim Tvoid
in
1153 | Lvar
((_, x
) as id
) ->
1154 Typing_hooks.dispatch_lvar_hook id
env;
1155 let env, x
= Env.get_local
env x
in
1158 let env, tyl
= lmap expr
env el
in
1159 let ty = Reason.Rwitness
p, Ttuple tyl
in
1162 let env, ty1 = expr
env e1
in
1163 let env, ty2 = expr
env e2
in
1164 let ty = Reason.Rwitness
p, Tclass
((p, SN.Collections.cPair
), [ty1; ty2]) in
1167 let env, tyl
= lmap expr
env el
in
1168 let ty = Reason.Rwitness
p, Ttuple tyl
in
1170 | Array_get
(e
, None
) ->
1171 let env, ty1 = expr
env e
in
1172 let is_lvalue = (valkind == `lvalue
) in
1173 array_append
is_lvalue p env ty1
1174 | Array_get
(e1
, Some e2
) ->
1175 let env, ty1 = expr
env e1
in
1176 let env, ty1 = TUtils.fold_unresolved
env ty1 in
1177 let env, ety1
= Env.expand_type
env ty1 in
1178 let env, ty2 = expr
env e2
in
1179 let is_lvalue = (valkind == `lvalue
) in
1180 array_get
is_lvalue p env ty1 ety1 e2
ty2
1181 | Call
(Cnormal
, (_, Id
(_, hh_show
)), [x
], [])
1182 when hh_show
= SN.PseudoFunctions.hh_show
->
1183 let env, ty = expr
env x
in
1185 env, Env.fresh_type
()
1186 | Call
(call_type
, e
, el
, uel
) ->
1187 let env, result
= dispatch_call
p env call_type e el uel
in
1188 let env = Env.forget_members
env p in
1190 | Binop
(Ast.Eq
(Some op
), e1
, e2
) ->
1191 let e2 = p, Binop
(op
, e1
, e2) in
1192 let env, ty = raw_expr in_cond
env (p, Binop
(Ast.Eq None
, e1
, e2)) in
1194 | Binop
(Ast.Eq None
, e1
, e2) ->
1195 let env, ty2 = raw_expr in_cond
env e2 in
1196 let env, ty = assign
p env e1
ty2 in
1197 (* If we are assigning a local variable to another local variable then
1198 * the expression ID associated with e2 is transferred to e1
1201 | (_, Lvar
(_, x1
)), (_, Lvar
(_, x2
)) ->
1202 let eid2 = Env.get_local_expr_id
env x2
in
1206 ~f
:(Env.set_local_expr_id
env x1
) in
1210 | Binop
((Ast.AMpamp
| Ast.BArbar
as bop
), e1
, e2) ->
1211 let c = bop
= Ast.AMpamp
in
1212 let lenv = env.Env.lenv in
1213 let env, ty1 = expr
env e1
in
1214 let env = condition
env c e1
in
1215 let env, ty2 = raw_expr in_cond
env e2 in
1216 let env = { env with Env.lenv = lenv } in
1217 Typing_hooks.dispatch_binop_hook
p bop
ty1 ty2;
1218 env, (Reason.Rlogic_ret
p, Tprim Tbool
)
1219 | Binop
(bop
, e1
, e2) ->
1220 let env, ty1 = raw_expr in_cond
env e1
in
1221 let env, ty2 = raw_expr in_cond
env e2 in
1222 let env, ty = binop in_cond
p env bop
(fst e1
) ty1 (fst
e2) ty2 in
1223 Typing_hooks.dispatch_binop_hook
p bop
ty1 ty2;
1226 let env, ty = raw_expr in_cond
env e
in
1228 | Eif
(c, e1
, e2) ->
1229 let env, tyc
= raw_expr in_cond
env c in
1230 Async.enforce_not_awaitable
env (fst
c) tyc
;
1231 let _, parent_locals
as lenv = env.Env.lenv in
1232 let env = condition
env true c in
1233 let env, ty1 = match e1
with
1239 let fake1, _locals1
= env.Env.lenv in
1240 let env = { env with Env.lenv = lenv } in
1241 let env = condition
env false c in
1242 let env, ty2 = expr
env e2 in
1243 let fake2, _locals2
= env.Env.lenv in
1244 let fake_members = LEnv.intersect_fake
fake1 fake2 in
1245 (* we restore the locals to their parent state so as not to leak the
1246 * effects of the `condition` calls above *)
1247 let env = { env with Env.lenv = fake_members, parent_locals
} in
1248 (* This is a shortened form of what we do in Typing_lenv.intersect. The
1249 * latter takes local environments as arguments, but our types here
1250 * aren't assigned to local variables in an environment *)
1251 let env, ty1 = TUtils.unresolved
env ty1 in
1252 let env, ty2 = TUtils.unresolved
env ty2 in
1253 Unify.unify
env ty1 ty2
1254 | Class_const
(cid, mid
) -> class_const
env p (cid, mid
)
1255 | Class_get
(x
, (_, y
))
1256 when Env.FakeMembers.get_static
env x y
<> None
->
1257 let env, local
= Env.FakeMembers.make_static
p env x y
in
1258 let local = p, Lvar
(p, local) in
1260 | Class_get
(cid, mid
) ->
1261 TUtils.process_static_find_ref
cid mid
;
1262 let env, cty
= static_class_id
p env cid in
1263 let env, cty
= Env.expand_type
env cty
in
1264 let env, ty = class_get ~is_method
:false ~is_const
:false env cty mid
cid in
1265 if Env.FakeMembers.is_static_invalid
env cid (snd mid
)
1267 let fake_name = Env.FakeMembers.make_static_id
cid (snd mid
) in
1268 let env, ty = Env.lost_info
fake_name ISet.empty
env ty in
1271 | Obj_get
(e
, (_, Id
(_, y
)), _)
1272 when Env.FakeMembers.get
env e y
<> None
->
1273 let env, local = Env.FakeMembers.make
p env e y
in
1274 let local = p, Lvar
(p, local) in
1276 | Obj_get
(e1
, (_, Id m
), nullflavor
) ->
1278 (match nullflavor
with
1279 | OG_nullthrows
-> None
1280 | OG_nullsafe
-> Some
p
1282 let env, ty1 = expr
env e1
in
1284 obj_get ~is_method
:false ~
nullsafe env ty1 (CIexpr e1
) m
(fun x
-> x
) in
1285 let has_lost_info = Env.FakeMembers.is_invalid
env e1
(snd m
) in
1288 let name = "the member "^snd m
in
1289 let env, result
= Env.lost_info
name ISet.empty
env result
in
1292 | Obj_get
(e1
, _, _) ->
1293 let env, _ = expr
env e1
in
1294 env, (Reason.Rwitness
p, Tany
)
1296 env, (Reason.Rwitness
p, Tany
)
1298 let env, key
= yield_field_key
env af
in
1299 let env, value = yield_field_value
env af
in
1300 let send = Env.fresh_type
() in
1301 let rty = match Env.get_fn_kind
env with
1303 Reason.Ryield_gen
p,
1304 Tclass
((p, SN.Classes.cGenerator
), [key
; value; send])
1305 | Ast.FAsyncGenerator
->
1306 Reason.Ryield_asyncgen
p,
1307 Tclass
((p, SN.Classes.cAsyncGenerator
), [key
; value; send])
1308 | Ast.FSync
| Ast.FAsync
->
1309 failwith
"Parsing should never allow this" in
1311 Type.sub_type
p (Reason.URyield
) env (Env.get_return
env) rty in
1312 let env = Env.forget_members
env p in
1313 env, (Reason.Ryield_send
p, Toption
send)
1315 let env, rty = expr
env e
in
1316 Async.overload_extract_from_awaitable
env p rty
1317 | Special_func func
-> special_func
env p func
1318 | New
(c, el
, uel
) ->
1319 Typing_hooks.dispatch_new_id_hook
c env p;
1320 TUtils.process_static_find_ref
c (p, SN.Members.__construct
);
1321 let check_not_abstract = true in
1322 let env, ty = new_object ~
check_not_abstract p env c el uel
in
1323 let env = Env.forget_members
env p in
1324 env, ExprDepTy.make
env c ty
1325 | Cast
((_, Harray
(None
, None
)), _) when Env.is_strict
env ->
1326 Errors.array_cast
p;
1327 env, (Reason.Rwitness
p, Tany
)
1329 let env, _ = expr
env e
in
1330 Typing_hint.hint_locl
env ty
1331 | InstanceOf
(e
, cid) ->
1332 let env, _ = expr
env e
in
1333 let env, _class
= instantiable_cid
p env cid in
1334 env, (Reason.Rwitness
p, Tprim Tbool
)
1336 NastCheck.fun_
env f
(Nast.assert_named_body f
.f_body
);
1337 let env, ft = fun_decl_in_env
env f
in
1338 (* When creating a closure, the 'this' type will mean the late bound type
1339 * of the current enclosing class
1342 { (Phase.env_with_self
env) with from_class
= Some CIstatic
} in
1343 let env, ft = Phase.localize_ft ~
ety_env env ft in
1344 (* check for recursive function calls *)
1345 let anon = anon_make
env p f
in
1346 let env, anon_id
= Env.add_anonymous
env anon in
1347 let env = Errors.try_with_error
1349 ignore
(anon env ft.ft_params
); env)
1351 (* If the anonymous function declaration has errors itself, silence
1352 them in any subsequent usages. *)
1353 let anon env fun_params
=
1354 Errors.ignore_
(fun () -> (anon env fun_params
)) in
1355 Env.set_anonymous
env anon_id
anon) in
1356 env, (Reason.Rwitness
p, Tanon
(ft.ft_arity
, anon_id
))
1357 | Xml
(sid
, attrl
, el
) ->
1359 let env, obj
= expr
env (fst sid
, New
(cid, [], [])) in
1360 let env, attr_ptyl
= lmap
begin fun env attr
->
1361 (* Typecheck the expressions - this just checks that the expressions are
1362 * valid, not that they match the declared type for the attribute *)
1363 let namepstr, valexpr
= attr
in
1364 let valp, _ = valexpr
in
1365 let env, valty
= expr
env valexpr
in
1366 env, (namepstr, (valp, valty
))
1368 let env, _body
= lfold expr
env el
in
1369 let env, class_ = class_id_for_new
p env cid in
1371 | None
-> env, (Reason.Runknown_class
p, Tobject
)
1372 | Some
(_, class_, _) ->
1373 if TypecheckerOptions.unsafe_xhp
(Env.get_options
env) then
1376 (* Check that the declared type of the XHP attribute matches the
1377 * expression type *)
1379 SMap.filter
(fun _ prop
-> prop
.ce_is_xhp_attr
) class_.tc_props
in
1380 let env = List.fold_left attr_ptyl ~f
:begin fun env attr
->
1381 let namepstr, valpty
= attr
in
1382 let valp, valty
= valpty
in
1383 (* We pretend that XHP attributes are stored as member variables,
1384 * prefixed with a colon.
1386 * This converts the member name to an attribute name. *)
1387 let name = ":" ^
(snd
namepstr) in
1388 let elt_option = SMap.get
name attrdec in
1389 (match elt_option with
1391 let env, declty
= Phase.localize_with_self
env elt
.ce_type
in
1392 let env = Type.sub_type
valp Reason.URxhp
env declty valty
in
1394 | None
when SN.Members.is_special_xhp_attribute
name -> env
1395 (* Special attributes are valid even if they're not declared - eg
1396 * any 'data-' attribute *)
1398 let r = (Reason.Rwitness
p) in
1399 member_not_found
(fst
namepstr) ~is_method
:false class_ name r;
1407 let env, fdm
= ShapeMap.map_env expr
env fdm
in
1408 (* allow_inter adds a type-variable *)
1409 let env, fdm
= ShapeMap.map_env
TUtils.unresolved
env fdm
in
1410 let env = check_shape_keys_validity
env p (ShapeMap.keys fdm
) in
1411 (* Fields are fully known, because this shape is constructed
1412 * using shape keyword and we know exactly what fields are set. *)
1413 env, (Reason.Rwitness
p, Tshape
(FieldsFullyKnown
, fdm
))
1415 and class_const ?
(incl_tc
=false) env p (cid, mid
) =
1416 TUtils.process_static_find_ref
cid mid
;
1417 let env, cty
= static_class_id
p env cid in
1418 let env, cty
= Env.expand_type
env cty
in
1420 class_get ~is_method
:false ~is_const
:true ~incl_tc
env cty mid
cid in
1422 | r, Tabstract
(AKgeneric
(n
, _), _) ->
1423 let () = match cid with
1424 | CIstatic
| CIexpr
_ -> ();
1425 | _ -> Errors.abstract_const_usage
p (Reason.to_pos
r) n
; ()
1430 (*****************************************************************************)
1431 (* Anonymous functions. *)
1432 (*****************************************************************************)
1434 and anon_bind_param
params env (param_name
, ty as pname_ty
) =
1437 (* This code cannot be executed normally, because the arity is wrong
1438 * and it will error later. Bind as many parameters as we can and carry
1441 | param :: paraml
->
1443 match param.param_hint
with
1445 let env, h
= Typing_hint.hint_locl
env h
in
1446 let pos = Reason.to_pos
(fst
ty) in
1447 let env = Type.sub_type
pos Reason.URparam
env h
ty in
1448 (* Closures are allowed to have explicit type-hints. When
1449 * that is the case we should check that the argument passed
1450 * is compatible with the type-hint.
1451 * The body of the function should be type-checked with the
1452 * hint and not the type of the argument passed.
1453 * Otherwise it leads to strange results where
1454 * foo(?string $x = null) is called with a string and fails to
1455 * type-check. If $x is a string instead of ?string, null is not
1456 * subtype of string ...
1458 bind_param
env (param_name
, h
) param
1459 | None
-> bind_param
env pname_ty
param
1461 and anon_bind_opt_param
env param =
1462 match param.param_expr
with
1464 let ty = Reason.Rnone
, Tany
in
1465 bind_param
env (None
, ty) param
1467 let env, ty = expr
env default
in
1468 Typing_sequencing.sequence_check_expr default
;
1469 bind_param
env (None
, ty) param
1471 and anon_check_param
env param =
1472 match param.param_hint
with
1475 let env, hty
= Typing_hint.hint_locl
env hty
in
1476 let env, paramty
= Env.get_local
env (snd
param.param_id
) in
1477 let hint_pos = Reason.to_pos
(fst hty
) in
1478 let env = Type.sub_type
hint_pos Reason.URhint
env hty paramty
in
1481 and anon_make tenv
p f
=
1482 let anon_lenv = tenv
.Env.lenv in
1483 let is_typing_self = ref false in
1484 let nb = Nast.assert_named_body f
.f_body
in
1488 Errors.anonymous_recursive
p;
1489 env, (Reason.Rwitness
p, Tany
)
1492 is_typing_self := true;
1493 Env.anon anon_lenv env begin fun env ->
1494 let params = ref f
.f_params in
1495 let env = List.fold_left ~f
:(anon_bind_param
params) ~init
:env tyl
in
1496 let env = List.fold_left ~f
:anon_bind_opt_param ~init
:env !params in
1497 let env = List.fold_left ~f
:anon_check_param ~init
:env f
.f_params in
1500 | None
-> TUtils.in_var
env (Reason.Rnone
, Tunresolved
[])
1503 Typing_hint.hint ~ensure_instantiable
:true env x
in
1504 (* If a 'this' type appears it needs to be compatible with the
1508 { (Phase.env_with_self
env) with
1509 from_class
= Some CIstatic
} in
1510 Phase.localize ~
ety_env env ret in
1511 let env = Env.set_return
env hret
in
1512 let env = Env.set_fn_kind
env f
.f_fun_kind
in
1513 let env = block
env nb.fnb_nast
in
1515 if Nast_terminality.Terminal.block tenv
nb.fnb_nast
1516 || nb.fnb_unsafe
|| !auto_complete
1518 else fun_implicit_return
env p hret
nb.fnb_nast f
.f_fun_kind
1520 is_typing_self := false;
1525 (*****************************************************************************)
1526 (* End of anonymous functions. *)
1527 (*****************************************************************************)
1529 and special_func
env p func
=
1530 let env, ty = (match func
with
1532 let env, ety
= expr
env e
in
1533 Async.gena
env p ety
1535 let env, etyl
= lmap expr
env el
in
1536 Async.genva
env p etyl
1537 | Gen_array_rec e
->
1538 let env, ety
= expr
env e
in
1539 Async.gen_array_rec
env p ety
1541 env, (Reason.Rwitness
p, Tclass
((p, SN.Classes.cAwaitable
), [ty]))
1543 and requires_consistent_construct
= function
1550 and new_object ~
check_not_abstract p env c el uel
=
1551 let env, class_ = instantiable_cid
p env c in
1554 let _ = lmap expr
env el
in
1555 let _ = lmap expr
env uel
in
1556 env, (Reason.Runknown_class
p, Tobject
)
1557 | Some
(cname
, class_, c_ty
) ->
1558 if check_not_abstract && class_.tc_abstract
1559 && not
(requires_consistent_construct
c)
1560 then Errors.uninstantiable_class
p class_.tc_pos
class_.tc_name
;
1561 let env, params = lfold
begin fun env _ ->
1562 TUtils.in_var
env (Reason.Rnone
, Tunresolved
[])
1563 end env class_.tc_tparams
in
1565 if SSet.mem
"XHP" class_.tc_extends
then env else
1566 let env = call_construct
p env class_ params el uel
c in
1569 let r_witness = Reason.Rwitness
p in
1570 let obj_ty = r_witness, Tclass
(cname
, params) in
1571 if not
(snd
class_.tc_construct
) then
1573 | CIstatic
-> Errors.new_inconsistent_construct
p cname `static
1574 | CIexpr
_ -> Errors.new_inconsistent_construct
p cname `classname
1578 env, (r_witness, TUtils.this_of
obj_ty)
1580 (match (fst
class_.tc_construct
) with
1583 type_expansions
= [];
1584 substs
= SMap.empty
;
1588 let _, ce_type
= Phase.localize ~
ety_env env ce
.ce_type
in
1589 ignore
(check_abstract_parent_meth
SN.Members.__construct
p ce_type
)
1592 | CI
_ | CIself
-> env, obj_ty
1594 let c_ty = r_witness, snd
c_ty in
1595 (* When constructing from a (classname) variable, the variable
1596 * dictates what the constructed object is going to be. This allows
1597 * for generic and dependent types to be correctly carried
1598 * through the 'new $foo()' iff the constructed obj_ty is a
1599 * supertype of the variable-dictated c_ty *)
1600 let env = SubType.sub_type
env obj_ty c_ty in
1604 (* FIXME: we need to separate our instantiability into two parts. Currently,
1605 * all this function is doing is checking if a given type is inhabited --
1606 * that is, whether there are runtime values of type T. However,
1607 * instantiability should be the stricter notion that T has a runtime
1608 * constructor; that is, `new T()` should be valid. In particular, interfaces
1609 * are inhabited, but not instantiable.
1610 * To make this work with classname, we likely need to add something like
1611 * concrete_classname<T>, where T cannot be an interface.
1613 and instantiable_cid
p env cid =
1614 let env, class_id
= class_id_for_new
p env cid in
1615 (match class_id
with
1616 | Some
((pos, name), class_, _) when
1617 class_.tc_kind
= Ast.Ctrait
|| class_.tc_kind
= Ast.Cenum
->
1619 | CI
_ | CIexpr
_ ->
1620 Errors.uninstantiable_class
pos class_.tc_pos
name;
1622 | CIstatic
| CIparent
| CIself
-> env, class_id
1624 | Some
((pos, name), class_, _) when
1625 class_.tc_kind
= Ast.Cabstract
&& class_.tc_final
->
1626 Errors.uninstantiable_class
pos class_.tc_pos
name;
1628 | None
| Some
_ -> env, class_id
)
1630 and exception_ty
pos env ty =
1631 let exn_ty = Reason.Rthrow
pos, Tclass
((pos, SN.Classes.cException
), []) in
1632 Type.sub_type
pos (Reason.URthrow
) env exn_ty ty
1634 (* While converting code from PHP to Hack, some arrays are used
1635 * as tuples. Example: array('', 0). Since the elements have
1636 * incompatible types, it should be a tuple. However, while migrating
1637 * code, it is more flexible to allow it in partial.
1639 * This probably isn't a good idea and should just use ty2 in all cases, but
1640 * FB www has about 50 errors if you just use ty2 -- not impossible to clean
1641 * up but more work right now than I want to do. Also it probably affects open
1642 * source code too, so this may be a nice small test case for our upcoming
1643 * migration/upgrade strategy.
1645 and convert_array_as_tuple
env ty2 =
1647 if not
(Env.is_strict
env) && TUtils.is_array_as_tuple
env ty2
1648 then env, (r2, Tany
)
1651 and shape_field_pos
= function
1653 | SFclass_const
((cls_pos
, _), (member_pos
, _)) -> Pos.btw cls_pos member_pos
1655 and check_shape_keys_validity
env pos keys
=
1656 (* If the key is a class constant, get its class name and type. *)
1657 let get_field_info env key
=
1658 let key_pos = shape_field_pos key
in
1659 (* Empty strings or literals that start with numbers are not
1660 permitted as shape field names. *)
1662 | SFlit
(_, key_name
) ->
1663 if (String.length key_name
= 0) then
1664 (Errors.invalid_shape_field_name_empty
key_pos)
1665 else if (key_name
.[0] >= '
0'
&& key_name
.[0] <='
9'
) then
1666 (Errors.invalid_shape_field_name_number
key_pos);
1668 | SFclass_const
(_, cls
as x
, y
) ->
1669 let env, ty = class_const
env pos (CI x
, y
) in
1670 let env = Typing_enum.check_valid_array_key_type
1671 Errors.invalid_shape_field_type ~allow_any
:false
1673 env, key_pos, Some
(cls
, ty))
1676 let check_field witness_pos witness_info
env key
=
1677 let env, key_pos, key_info
= get_field_info env key
in
1678 (match witness_info
, key_info
with
1680 Errors.invalid_shape_field_literal
key_pos witness_pos
; env
1682 Errors.invalid_shape_field_const
key_pos witness_pos
; env
1684 | Some
(cls1
, ty1), Some
(cls2
, ty2) ->
1685 if cls1
<> cls2
then
1686 Errors.shape_field_class_mismatch
1687 key_pos witness_pos
(strip_ns cls2
) (strip_ns cls1
);
1688 (* We want to use our own error message here instead of the normal
1689 * unification one. *)
1691 (fun () -> Unify.iunify
env ty1 ty2)
1693 Errors.shape_field_type_mismatch
1695 (Typing_print.error
(snd
ty2)) (Typing_print.error
(snd
ty1));
1699 (* Sort the keys by their positions since the error messages will make
1700 * more sense if we take the one that appears first as canonical and if
1701 * they are processed in source order. *)
1702 let cmp_keys x y
= Pos.compare
(shape_field_pos x
) (shape_field_pos y
) in
1703 let keys = List.sort
cmp_keys keys in
1707 | witness
:: rest_keys
->
1708 let env, pos, info
= get_field_info env witness
in
1709 List.fold_left ~f
:(check_field pos info
) ~init
:env rest_keys
1711 and check_valid_rvalue
p env ty =
1712 let env, folded_ty
= TUtils.fold_unresolved
env ty in
1713 let _deliberately_discarded_env, folded_ety
=
1714 Env.expand_type
env folded_ty
in
1715 match folded_ety
with
1716 | r, Tprim Tnoreturn
->
1717 let () = Errors.noreturn_usage
p
1718 (Reason.to_string
"A noreturn function always throws or exits" r)
1721 let () = Errors.void_usage
p
1722 (Reason.to_string
"A void function doesn't return a value" r)
1726 and set_valid_rvalue
p env x
ty =
1727 let ty = check_valid_rvalue
p env ty in
1728 let env = Env.set_local
env x
ty in
1729 (* We are assigning a new value to the local variable, so we need to
1730 * generate a new expression id
1732 let env = Env.set_local_expr_id
env x
(Ident.tmp
()) in
1735 and assign
p env e1
ty2 =
1736 let env, ty2 = convert_array_as_tuple
env ty2 in
1738 | (_, Lvar
(_, x
)) ->
1739 set_valid_rvalue
p env x
ty2
1740 | (_, Lplaceholder
_) ->
1741 let placeholder_ty = Reason.Rplaceholder
p, (Tprim Tvoid
) in
1744 let env, folded_ty2
= TUtils.fold_unresolved
env ty2 in
1745 let env, folded_ety2
= Env.expand_type
env folded_ty2
in
1746 (match folded_ety2
with
1747 | _, Tclass
((_, x
), [elt_type
])
1748 when x
= SN.Collections.cVector
1749 || x
= SN.Collections.cImmVector
1750 || x
= SN.Collections.cConstVector
->
1751 let env, _ = lfold
begin fun env e
->
1752 assign
(fst e
) env e elt_type
1755 | _, Tarray
(Some elt_type
, None
) ->
1756 let env, _ = lfold
begin fun env e
->
1757 assign
(fst e
) env e elt_type
1760 | r, Tarray
(None
, None
)
1762 let env, _ = lfold
begin fun env e
->
1763 assign
(fst e
) env e
(r, Tany
)
1766 | r, Tclass
((_, coll
), [ty1; ty2]) when coll
= SN.Collections.cPair
->
1769 let env, _ = assign
p env x1
ty1 in
1770 let env, _ = assign
p env x2
ty2 in
1771 env, (Reason.Rwitness
(fst e1
), Tprim Tvoid
)
1773 Errors.pair_arity
p;
1777 let size1 = List.length el
in
1778 let size2 = List.length tyl
in
1780 let p2 = Reason.to_pos
r in
1783 Errors.tuple_arity
p2 size2 p1 size1;
1787 let env = List.fold2_exn el tyl ~f
:begin fun env lvalue
ty2 ->
1788 fst
(assign
p env lvalue
ty2)
1790 env, (Reason.Rwitness
p1, Tprim Tvoid
)
1791 | _, Tabstract
(_, Some
ty2) -> assign
p env e1
ty2
1792 | _, (Tmixed
| Tarray
(_, _) | Toption
_ | Tprim
_
1793 | Tvar
_ | Tfun
_ | Tabstract
(_, _) | Tanon
(_, _)
1794 | Tunresolved
_ | Tclass
(_, _) | Tobject
| Tshape
_) ->
1795 assign_simple
p env e1
ty2
1799 let _, locals
as lenv = env.Env.lenv in
1800 let no_fakes = Env.empty_fake_members
, locals
in
1801 (* In this section, we check that the assignment is compatible with
1802 * the real type of a member. Remember that members can change
1803 * type (cf fake_members). But when we assign a value to $this->x,
1804 * we want to make sure that the type assign to $this->x is compatible
1805 * with the actual type hint. In this portion of the code, type-check
1806 * the assignment in an environment without fakes, and therefore
1807 * check that the assignment is compatible with the type of
1810 let env, real_type
= lvalue
{ env with Env.lenv = no_fakes } e1
in
1811 let env, exp_real_type
= Env.expand_type
env real_type
in
1812 let env = { env with Env.lenv = lenv } in
1813 let env, ety2
= Env.expand_type
env ty2 in
1814 let real_type_list =
1815 match exp_real_type
with
1816 | _, Tunresolved tyl
-> tyl
1819 let env = List.fold_left
real_type_list ~f
:begin fun env real_type
->
1820 Type.sub_type
p (Reason.URassign
) env real_type ety2
1823 | _, Obj_get
((_, This
| _, Lvar
_ as obj
),
1824 (_, Id
(_, member_name
)),
1826 let env, local = Env.FakeMembers.make
p env obj member_name
in
1827 let () = (match obj
with
1829 Typing_suggest.save_member member_name
env exp_real_type
ty2
1832 set_valid_rvalue
p env local ty2
1833 | _, Class_get
(x
, (_, y
)) ->
1834 let env, local = Env.FakeMembers.make_static
p env x y
in
1835 let env, ty3
= set_valid_rvalue
p env local ty2 in
1839 Typing_suggest.save_member y
env exp_real_type
ty2;
1844 | _, Array_get
((_, Lvar
(_, lvar
)) as shape
, Some
(p1, (String
_ as e
)))
1845 | _, Array_get
((_, Lvar
(_, lvar
)) as shape
,
1846 Some
(p1, (Class_const
(CI
_, _) as e
))) ->
1847 (* In the case of an assignment of the form $x['new_field'] = ...;
1848 * $x could be a shape where the field 'new_field' is not yet defined.
1849 * When that is the case we want to add the field to its type.
1851 let env, shape_ty
= expr
env shape
in
1852 let field = TUtils.shape_field_name
p1 e
in
1854 Typing_shapes.grow_shape
p e1
field (Env.fresh_type
()) env shape_ty
in
1855 let env, _ty
= set_valid_rvalue
p env lvar shape_ty
in
1857 (* We still need to call assign_simple in order to bind the freshly
1858 * created variable in added shape field. Moreover, it's needed because
1859 * shape_ty could be more than just a shape. It could be an unresolved
1860 * type where some elements are shapes and some others are not.
1862 assign_simple
p env e1
ty2
1864 Errors.this_lvalue
p;
1865 env, (Reason.Rwitness
p, Tany
)
1866 | _, Unop
(Ast.Uref
, e1'
) ->
1867 (* references can be "lvalues" in foreach bindings *)
1868 assign
p env e1'
ty2
1870 assign_simple
p env e1
ty2
1872 and assign_simple
pos env e1
ty2 =
1873 let env, ty1 = lvalue
env e1
in
1875 let ty2 = check_valid_rvalue
pos env ty2 in
1877 let env, ty2 = TUtils.unresolved
env ty2 in
1878 let env = Type.sub_type
pos (Reason.URassign
) env ty1 ty2 in
1881 and array_field_value
env = function
1883 | Nast.AFkvalue
(_, x
) ->
1884 let env, ty = expr
env x
in
1885 Typing_env.unbind
env ty
1887 and yield_field_value
env x
= array_field_value
env x
1889 and array_field_key
env = function
1890 | Nast.AFvalue
(p, _) ->
1891 env, (Reason.Rwitness
p, Tprim Tint
)
1892 | Nast.AFkvalue
(x
, _) ->
1893 let env, ty = expr
env x
in
1894 Typing_env.unbind
env ty
1896 and yield_field_key
env = function
1897 | Nast.AFvalue
(p, _) ->
1898 env, (match Env.get_fn_kind
env with
1900 | Ast.FAsync
-> assert false
1902 (Reason.Rwitness
p, Tprim Tint
)
1903 | Ast.FAsyncGenerator
->
1904 (Reason.Ryield_asyncnull
p, Toption
(Env.fresh_type
())))
1905 | Nast.AFkvalue
(x
, _) ->
1908 and check_parent_construct
pos env el uel env_parent
=
1909 let check_not_abstract = false in
1910 let env, env_parent
= Phase.localize_with_self
env env_parent
in
1911 let env, parent
= new_object ~
check_not_abstract pos env CIparent el uel
in
1912 let env, _ = Type.unify
pos (Reason.URnone
) env env_parent parent
in
1913 env, (Reason.Rwitness
pos, Tprim Tvoid
)
1915 and call_parent_construct
pos env el uel
=
1916 let parent = Env.get_parent
env in
1919 check_parent_construct
pos env el uel
parent
1920 | _, (Tany
| Tmixed
| Tarray
(_, _) | Tgeneric
(_, _) | Toption
_ | Tprim
_
1921 | Tfun
_ | Ttuple
_ | Tshape
_ | Taccess
(_, _) | Tthis
1922 ) -> (* continue here *)
1923 let default = env, (Reason.Rnone
, Tany
) in
1924 match Env.get_self
env with
1925 | _, Tclass
((_, self
), _) ->
1926 (match Env.get_class
env self
with
1927 | Some
({tc_kind
= Ast.Ctrait
; _}
1929 (match trait_most_concrete_req_class trait
env with
1930 | None
-> Errors.parent_in_trait
pos; default
1931 | Some
(_, parent_ty
) ->
1932 check_parent_construct
pos env el uel parent_ty
1935 if not self_tc
.tc_members_fully_known
1936 then () (* Don't know the hierarchy, assume it's correct *)
1937 else Errors.undefined_parent
pos;
1939 | None
-> assert false)
1940 | _, (Tany
| Tmixed
| Tarray
(_, _) | Toption
_
1941 | Tprim
_ | Tfun
_ | Ttuple
_ | Tshape
_ | Tvar
_
1942 | Tabstract
(_, _) | Tanon
(_, _) | Tunresolved
_ | Tobject
1944 Errors.parent_outside_class
pos; default
1946 (* parent::method() in a class definition invokes the specific parent
1947 * version of the method ... it better be callable *)
1948 and check_abstract_parent_meth mname
pos fty =
1949 if is_abstract_ft
fty then Errors.parent_abstract_call mname
pos (Reason.to_pos
(fst
fty)) ;
1952 and is_abstract_ft
fty = match fty with
1953 | _r, Tfun
{ ft_abstract
= true; _ } -> true
1954 | _r, (Tany
| Tmixed
| Tarray
(_, _) | Toption
_ | Tprim
_
1955 | Tvar
_ | Tfun
_ | Tclass
(_, _) | Tabstract
(_, _) | Ttuple
_
1956 | Tanon
_ | Tunresolved
_ | Tobject
| Tshape
_
1960 (* Depending on the kind of expression we are dealing with
1961 * The typing of call is different.
1963 and dispatch_call
p env call_type
(fpos
, fun_expr
as e
) el uel
=
1965 | Id
(_, pseudo_func
) when pseudo_func
= SN.SpecialFunctions.echo
->
1966 let env, _ = lfold expr
env el
in
1967 env, (Reason.Rwitness
p, Tprim Tvoid
)
1968 | Id
(_, pseudo_func
)
1970 pseudo_func
= SN.PseudoFunctions.isset
1971 || pseudo_func
= SN.PseudoFunctions.empty
->
1972 let env, _ = lfold expr
env el
in
1974 Errors.unpacking_disallowed_builtin_function
p pseudo_func
;
1975 if Env.is_strict
env then
1976 Errors.isset_empty_in_strict
p pseudo_func
;
1977 env, (Reason.Rwitness
p, Tprim Tbool
)
1978 | Id
(_, pseudo_func
) when pseudo_func
= SN.PseudoFunctions.unset
->
1979 let env, _ = lfold expr
env el
in
1981 Errors.unpacking_disallowed_builtin_function
p pseudo_func
;
1982 let env = if Env.is_strict
env then
1984 | [(_, Array_get
(ea
, Some
_))], [] ->
1985 let env, ty = expr
env ea
in
1987 (fun () -> SubType.sub_type
1988 env (Reason.Rnone
, Tarray
(None
, None
)) ty)
1990 let env, (r, ety
) = Env.expand_type
env ty in
1991 Errors.unset_nonidx_in_strict
1993 (Reason.to_string
("This is " ^
Typing_print.error ety
) r);
1995 | _ -> Errors.unset_nonidx_in_strict
p []; env)
1998 | [(p, Obj_get
(_, _, OG_nullsafe
))] ->
2000 Errors.nullsafe_property_write_context
p;
2001 env, (Reason.Rwitness
p, Tany
)
2003 | _ -> env, (Reason.Rwitness
p, Tprim Tvoid
))
2004 | Id
(cp
, get_called_class
) when
2005 get_called_class
= SN.StdlibFunctions.get_called_class
2006 && el
= [] && uel
= [] ->
2007 (* get_called_class fetches the late-bound class *)
2008 if Env.is_outside_class
env then Errors.static_outside_class
p;
2009 class_const
env p (CIstatic
, (cp
, SN.Members.mClass
))
2010 | Id
((_, array_filter
) as id
)
2011 when array_filter
= SN.StdlibFunctions.array_filter
&& el
<> [] && uel
= [] ->
2012 (* dispatch the call to typecheck the arguments *)
2013 let env, fty = fun_type_of_id
env id
in
2014 let env, fty = Env.expand_type
env fty in
2015 let env, res
= call
p env fty el uel
in
2016 (* but ignore the result and overwrite it with custom return type *)
2017 let x = List.hd_exn el
in
2018 let env, ty = expr
env x in
2019 let explain_array_filter (r, t
) =
2020 (Reason.Rarray_filter
(p, r), t
) in
2021 let get_value_type env tv
=
2022 let env, tv
= if List.length el
> 1 then env, tv
else non_null
env tv
in
2023 env, explain_array_filter tv
in
2024 let rec get_array_filter_return_type env ty =
2025 let env, ety
= Env.expand_type
env ty in
2027 | (_, Tarray
(None
, None
)) as array_type
->
2029 | (r, Tarray
(Some tv
, None
)) ->
2030 let env, tv
= get_value_type env tv
in
2031 env, (r, Tarray
(Some tv
, None
))
2032 | (r, Tunresolved
x) ->
2033 let env, x = lmap
get_array_filter_return_type env x in
2034 env, (r, Tunresolved
x)
2038 let tk, tv
= Env.fresh_type
(), Env.fresh_type
() in
2041 let keyed_container = (
2044 (Pos.none
, SN.Collections.cKeyedContainer
), [tk; tv
]
2047 let env = SubType.sub_type
env keyed_container ety
in
2048 let env, tv
= get_value_type env tv
in
2050 Some
(explain_array_filter tk),
2053 (fun _ -> Errors.try_
2058 (Pos.none
, SN.Collections.cContainer
), [tv
]
2061 let env = SubType.sub_type
env container ety
in
2062 let env, tv
= get_value_type env tv
in
2064 Some
(explain_array_filter (r, Tprim Tarraykey
)),
2066 (fun _ -> env, res
)))
2067 in get_array_filter_return_type env ty
2068 | Id
(p, type_structure
)
2069 when type_structure
= SN.StdlibFunctions.type_structure
2070 && (List.length el
= 2) && uel
= [] ->
2074 | p, Nast.String cst
->
2075 (* find the class constant implicitly defined by the typeconst *)
2076 let cid = (match e1
with
2077 | _, Class_const
(cid, (_, x))
2078 | _, Class_get
(cid, (_, x)) when x = SN.Members.mClass
-> cid
2079 | _ -> Nast.CIexpr e1
) in
2080 class_const ~incl_tc
:true env p (cid, cst
)
2082 Errors.illegal_type_structure
p "second argument is not a string";
2083 env, (Reason.Rnone
, Tany
))
2084 | _ -> assert false)
2085 | Id
((_, array_map
) as x)
2086 when array_map
= SN.StdlibFunctions.array_map
&& el
<> [] && uel
= [] ->
2087 let env, fty = fun_type_of_id
env x in
2088 let env, fty = Env.expand_type
env fty in
2089 let env, fty = match fty, el
with
2090 | ((r_fty
, Tfun
fty), _::args
) when args
<> [] ->
2091 let arity = List.length args
in
2093 Builds a function with signature:
2095 function<T1, ..., Tn, Tr>(
2096 (function(T1, ..., Tn):Tr),
2102 where R is constructed by build_output_container applied to Tr
2104 let build_function build_output_container
=
2105 let vars = List.map args
(fun _ -> Env.fresh_type
()) in
2106 let tr = Env.fresh_type
() in
2110 ft_pos
= fty.ft_pos
;
2111 ft_deprecated
= None
;
2112 ft_abstract
= false;
2113 ft_arity
= Fstandard
(arity, arity); ft_tparams
= [];
2114 ft_params
= List.map
vars (fun x -> (None
, x));
2118 let containers = List.map
vars (fun var
->
2121 Tclass
((fty.ft_pos
, SN.Collections.cContainer
), [var
])
2125 (r_fty
, Tfun
{fty with
2126 ft_arity
= Fstandard
(arity+1, arity+1);
2127 ft_params
= f::containers;
2128 ft_ret
= build_output_container
tr;
2132 Takes a Container type and returns a function that can "pack" a type
2133 into an array of appropriate shape, preserving the key type, i.e.:
2134 array -> f, where f R = array
2135 array<X> -> f, where f R = array<R>
2136 array<X, Y> -> f, where f R = array<X, R>
2137 Vector<X> -> f where f R = array<R>
2138 KeyedContainer<X, Y> -> f, where f R = array<X, R>
2139 Container<X> -> f, where f R = array<arraykey, R>
2140 X -> f, where f R = Y
2142 let rec build_output_container
2143 (env:Env.env) (x:locl
ty) : (Env.env * (locl
ty -> locl
ty)) =
2144 let env, x = Env.expand_type
env x in (match x with
2145 | (_, Tarray
(None
, None
)) as array_type
->
2146 env, (fun _ -> array_type
)
2147 | (r, Tarray
(_, None
)) ->
2148 env, (fun tr -> (r, Tarray
(Some
(tr), None
)) )
2149 | ((_, Tany
) as any) ->
2151 | (r, Tunresolved
x) ->
2152 let env, x = lmap
build_output_container env x in
2153 env, (fun tr -> (r, Tunresolved
(List.map
x (fun f -> f tr))))
2155 let tk, tv
= Env.fresh_type
(), Env.fresh_type
() in
2156 let try_vector env =
2160 (fty.ft_pos
, SN.Collections.cConstVector
), [tv
]
2163 let env = SubType.sub_type
env vector x in
2164 env, (fun tr -> (r, Tarray
(
2168 let try_keyed_container env =
2169 let keyed_container = (
2172 (fty.ft_pos
, SN.Collections.cKeyedContainer
), [tk; tv
]
2175 let env = SubType.sub_type
env keyed_container x in
2176 env, (fun tr -> (r, Tarray
(
2180 let try_container env =
2184 (fty.ft_pos
, SN.Collections.cContainer
), [tv
]
2187 let env = SubType.sub_type
env container x in
2188 env, (fun tr -> (r, Tarray
(
2189 Some
((r, Tprim Tarraykey
)),
2194 (fun _ -> Errors.try_
2196 try_keyed_container env)
2197 (fun _ -> Errors.try_
2200 (fun _ -> env, (fun _ -> (Reason.Rwitness
p, Tany
)))))) in
2202 Single argument calls preserve the key type, multi argument
2203 calls always return an array<Tr>
2207 let env, x = expr
env x in
2208 let env, output_container
= build_output_container env x in
2209 env, build_function output_container
2211 env, build_function (fun tr ->
2212 (r_fty
, Tarray
(Some
(tr), None
))))
2214 call
p env fty el
[]
2215 | Id
((_, idx
) as id
) when idx
= SN.FB.idx
->
2216 (* Directly call get_fun so that we can muck with the type before
2217 * instantiation -- much easier to work in terms of Tgeneric Tk/Tv than
2218 * trying to figure out which Tvar is which. *)
2219 (match Env.get_fun
env (snd id
) with
2221 let param1, (name2
, (r2, _)), (name3
, (r3
, _)) =
2222 match fty.ft_params
with
2223 | [param1; param2
; param3
] -> param1, param2
, param3
2224 | _ -> assert false in
2225 let params, ret = match List.length el
with
2227 let param2 = (name2
, (r2, Toption
(r2, Tgeneric
("Tk", None
)))) in
2228 let rret = fst
fty.ft_ret
in
2229 let ret = (rret, Toption
(rret, Tgeneric
("Tv", None
))) in
2230 [param1; param2], ret
2232 let param2 = (name2
, (r2, Tgeneric
("Tk", None
))) in
2233 let param3 = (name3
, (r3
, Tgeneric
("Tv", None
))) in
2234 let ret = (fst
fty.ft_ret
, Tgeneric
("Tv", None
)) in
2235 [param1; param2; param3], ret
2236 | _ -> fty.ft_params
, fty.ft_ret
in
2237 let fty = { fty with ft_params
= params; ft_ret
= ret } in
2238 let ety_env = Phase.env_with_self
env in
2239 let env, fty = Phase.localize_ft ~
ety_env env fty in
2240 let tfun = Reason.Rwitness
fty.ft_pos
, Tfun
fty in
2241 call
p env tfun el
[]
2242 | None
-> unbound_name env id
)
2243 | Class_const
(CI
(_, shapes
) as class_id
, ((_, idx
) as method_id
))
2244 when shapes
= SN.Shapes.cShapes
&& idx
= SN.Shapes.idx
->
2245 overload_function
p env class_id method_id el uel
2246 begin fun env fty res el
-> match el
with
2248 let env, shape_ty
= expr
env shape
in
2249 Typing_shapes.idx
env fty shape_ty
field None
2250 | [shape
; field; default] ->
2251 let env, shape_ty
= expr
env shape
in
2252 let env, default_ty
= expr
env default in
2253 Typing_shapes.idx
env fty shape_ty
field
2254 (Some
((fst
default), default_ty
))
2257 | Class_const
(CI
(_, shapes
) as class_id
, ((_, key_exists
) as method_id
))
2258 when shapes
= SN.Shapes.cShapes
&& key_exists
= SN.Shapes.keyExists
->
2259 overload_function
p env class_id method_id el uel
2260 begin fun env fty res el
-> match el
with
2262 let env, shape_ty
= expr
env shape
in
2263 (* try acessing the field, to verify existence, but ignore
2264 * the returned type and keep the one coming from function
2265 * return type hint *)
2266 let env, _ = Typing_shapes.idx
env fty shape_ty
field None
in
2270 | Class_const
(CI
(_, shapes
) as class_id
, ((_, remove_key
) as method_id
))
2271 when shapes
= SN.Shapes.cShapes
&& remove_key
= SN.Shapes.removeKey
->
2272 overload_function
p env class_id method_id el uel
2273 begin fun env _ res el
-> match el
with
2274 | [shape
; field] -> begin match shape
with
2275 | (_, Lvar
(_, lvar
)) ->
2276 let env, shape_ty
= expr
env shape
in
2278 Typing_shapes.remove_key
p env shape_ty
field in
2279 let env, _ = set_valid_rvalue
p env lvar shape_ty
in
2282 Errors.invalid_shape_remove_key
(fst shape
);
2287 | Class_const
(CIparent
, (_, construct
))
2288 when construct
= SN.Members.__construct
->
2289 call_parent_construct
p env el uel
2290 | Class_const
(CIparent
, m
) ->
2291 let env, ty1 = static_class_id
p env CIparent
in
2292 if Env.is_static
env
2294 (* in static context, you can only call parent::foo() on static
2296 let env, fty = class_get ~is_method
:true ~is_const
:false env ty1 m CIparent
in
2297 let env, fty = Env.expand_type
env fty in
2298 let fty = check_abstract_parent_meth
(snd m
) p fty in
2299 call
p env fty el uel
2302 (* in instance context, you can call parent:foo() on static
2303 * methods as well as instance methods *)
2304 (match class_contains_smethod
env ty1 m
with
2306 (* parent::nonStaticFunc() is really weird. It's calling a method
2307 * defined on the parent class, but $this is still the child class.
2308 * We can deal with this by hijacking the continuation that
2309 * calculates the SN.Typehints.this type *)
2310 let this_ty = ExprDepTy.make
env CIstatic
2311 (Reason.Rwitness fpos
, TUtils.this_of
(Env.get_self
env)) in
2312 let k_lhs _ = this_ty in
2313 let env, method_
, _ =
2314 obj_get_ ~is_method
:true ~
nullsafe:None
env ty1 CIparent m
2315 begin fun (env, fty, _) ->
2316 let env, fty = Env.expand_type
env fty in
2317 let fty = check_abstract_parent_meth
(snd m
) p fty in
2318 let env, method_
= call
p env fty el uel
in
2325 let env, fty = class_get ~is_method
:true ~is_const
:false env ty1 m CIparent
in
2326 let env, fty = Env.expand_type
env fty in
2327 let fty = check_abstract_parent_meth
(snd m
) p fty in
2328 call
p env fty el uel
2331 | Class_const
(e1
, m
) ->
2332 TUtils.process_static_find_ref e1 m
;
2333 let env, ty1 = static_class_id
p env e1
in
2334 let env, fty = class_get ~is_method
:true ~is_const
:false env ty1 m e1
in
2335 let env, fty = Env.expand_type
env fty in
2336 let () = match e1
with
2337 | CIself
when is_abstract_ft
fty ->
2338 (match Env.get_self
env with
2339 | _, Tclass
((_, self
), _) ->
2340 (* at runtime, self:: in a trait is a call to whatever
2341 * self:: is in the context of the non-trait "use"-ing
2342 * the trait's code *)
2343 (match Env.get_class
env self
with
2344 | Some
{ tc_kind
= Ast.Ctrait
; _ } -> ()
2345 | _ -> Errors.self_abstract_call
(snd m
) p (Reason.to_pos
(fst
fty))
2348 | CI
c when is_abstract_ft
fty ->
2349 Errors.classname_abstract_call
(snd
c) (snd m
) p (Reason.to_pos
(fst
fty))
2351 call
p env fty el uel
2352 | Obj_get
(e1
, (_, Id m
), nullflavor
) ->
2353 let is_method = call_type
= Cnormal
in
2354 let env, ty1 = expr
env e1
in
2356 (match nullflavor
with
2357 | OG_nullthrows
-> None
2358 | OG_nullsafe
-> Some
p
2360 let fn = (fun (env, fty, _) ->
2361 let env, fty = Env.expand_type
env fty in
2362 let env, method_
= call
p env fty el uel
in
2363 env, method_
, None
) in
2364 obj_get ~
is_method ~
nullsafe env ty1 (CIexpr e1
) m
fn
2367 Typing_hooks.dispatch_id_hook
x env;
2368 let env, fty = fun_type_of_id
env x in
2369 let env, fty = Env.expand_type
env fty in
2370 call
p env fty el uel
2372 let env, fty = expr
env e
in
2373 let env, fty = Env.expand_type
env fty in
2374 call
p env fty el uel
2376 and fun_type_of_id
env x =
2377 Typing_hooks.dispatch_fun_id_hook
x;
2379 match Env.get_fun
env (snd
x) with
2380 | None
-> unbound_name env x
2382 let ety_env = Phase.env_with_self
env in
2383 let env, fty = Phase.localize_ft ~
ety_env env fty in
2384 env, (Reason.Rwitness
fty.ft_pos
, Tfun
fty)
2388 (*****************************************************************************)
2389 (* Function type-checking expressions accessing an array (example: $x[...]).
2390 * The parameter is_lvalue is true when the expression is on the left hand
2391 * side of an assignment (example: $x[...] = 0).
2393 (*****************************************************************************)
2394 and array_get
is_lvalue p env ty1 ety1
e2 ty2 =
2395 (* This is a little weird -- we enforce the right arity when you use certain
2396 * collections, even in partial mode (where normally completely omitting the
2397 * type parameter list is admitted). Basically the "omit type parameter"
2398 * hole was for compatibility with certain interfaces like ArrayAccess, not
2399 * for collections! But it's hard to go back on now, so since we've always
2400 * errored (with an inscrutable error message) when you try to actually use
2401 * a collection with omitted type parameters, we can continue to error and
2402 * give a more useful error message. *)
2403 let arity_error (_, name) =
2404 Errors.array_get_arity
p name (Reason.to_pos
(fst
ty1))
2407 | Tunresolved tyl
->
2408 let env, tyl
= lfold
begin fun env ty1 ->
2409 let env, ety1
= Env.expand_type
env ty1 in
2410 array_get
is_lvalue p env ty1 ety1
e2 ty2
2413 env, (fst ety1
, Tunresolved tyl
)
2414 | Tarray
(Some
ty, None
) ->
2415 let ty1 = Reason.Ridx
(fst
e2), Tprim Tint
in
2416 let env, _ = Type.unify
p Reason.URarray_get
env ty2 ty1 in
2418 | Tclass
((_, cn
) as id
, argl
)
2419 when cn
= SN.Collections.cVector
->
2420 let ty = match argl
with
2422 | _ -> arity_error id
; Reason.Rwitness
p, Tany
in
2423 let ty1 = Reason.Ridx_vector
(fst
e2), Tprim Tint
in
2424 let env, _ = Type.unify
p Reason.URvector_get
env ty2 ty1 in
2426 | Tclass
((_, cn
) as id
, argl
)
2427 when cn
= SN.Collections.cMap
2428 || cn
= SN.Collections.cStableMap
->
2429 let (k
, v
) = match argl
with
2433 let any = (Reason.Rwitness
p, Tany
) in
2436 let env, ty2 = TUtils.unresolved
env ty2 in
2437 let env, _ = Type.unify
p Reason.URmap_get
env k
ty2 in
2439 | Tclass
((_, cn
) as id
, argl
)
2440 when not
is_lvalue &&
2441 (cn
= SN.Collections.cConstMap
2442 || cn
= SN.Collections.cImmMap
) ->
2443 let (k
, v
) = match argl
with
2447 let any = (Reason.Rwitness
p, Tany
) in
2450 let env, _ = Type.unify
p Reason.URmap_get
env k
ty2 in
2452 | Tclass
((_, cn
), _)
2454 (cn
= SN.Collections.cConstMap
|| cn
= SN.Collections.cImmMap
) ->
2455 error_const_mutation
env p ety1
2456 | Tclass
((_, cn
) as id
, argl
)
2457 when (cn
= SN.Collections.cIndexish
2458 || cn
= SN.Collections.cKeyedContainer
) ->
2459 let (k
, v
) = match argl
with
2463 let any = (Reason.Rwitness
p, Tany
) in
2466 let env, _ = Type.unify
p Reason.URcontainer_get
env k
ty2 in
2468 | Tclass
((_, cn
) as id
, argl
)
2469 when not
is_lvalue &&
2470 (cn
= SN.Collections.cConstVector
|| cn
= SN.Collections.cImmVector
) ->
2471 let ty = match argl
with
2473 | _ -> arity_error id
; Reason.Rwitness
p, Tany
in
2474 let ty1 = Reason.Ridx
(fst
e2), Tprim Tint
in
2475 let ur = (match cn
with
2476 | x when x = SN.Collections.cConstVector
-> Reason.URconst_vector_get
2477 | x when x = SN.Collections.cImmVector
-> Reason.URimm_vector_get
2478 | _ -> failwith
("Unexpected collection name: " ^ cn
)) in
2479 let env, _ = Type.unify
p ur env ty2 ty1 in
2481 | Tclass
((_, cn
), _)
2483 (cn
= SN.Collections.cConstVector
|| cn
= SN.Collections.cImmVector
) ->
2484 error_const_mutation
env p ety1
2485 | Tarray
(Some k
, Some v
) ->
2486 let env, ty2 = TUtils.unresolved
env ty2 in
2487 let env, _ = Type.unify
p Reason.URarray_get
env k
ty2 in
2488 (* The values in the array are not consistent
2489 * we just give up. Use Maps!
2491 let env, ev
= TUtils.fold_unresolved
env v
in
2492 let env, ev
= Env.expand_type
env ev
in
2494 | _, Tunresolved
_ -> env, (Reason.Rwitness
p, Tany
)
2495 | _, (Tany
| Tmixed
| Tarray
(_, _) | Toption
_
2496 | Tprim
_ | Tvar
_ | Tfun
_ | Tclass
(_, _) | Tabstract
(_, _)
2497 | Ttuple
_ | Tanon
_ | Tobject
| Tshape
_) -> env, v
2499 | Tany
| Tarray
(None
, None
) -> env, (Reason.Rnone
, Tany
)
2501 let ty = Reason.Rwitness
p, Tprim Tstring
in
2502 let env, ty = Type.unify
p Reason.URnone
env ty1 ty in
2503 let int = Reason.Ridx
(fst
e2), Tprim Tint
in
2504 let env, _ = Type.unify
p Reason.URarray_get
env ty2 int in
2510 let idx = int_of_string
(snd n
) in
2511 let nth = List.nth_exn tyl
idx in
2514 Errors.typing_error
p (Reason.string_of_ureason
Reason.URtuple_get
);
2515 env, (Reason.Rwitness
p, Tany
)
2518 Errors.typing_error
p (Reason.string_of_ureason
Reason.URtuple_access
);
2519 env, (Reason.Rwitness
p, Tany
)
2521 | Tclass
((_, cn
) as id
, argl
) when cn
= SN.Collections.cPair
->
2522 let (ty1, ty2) = match argl
with
2523 | [ty1; ty2] -> (ty1, ty2)
2526 let any = (Reason.Rwitness
p, Tany
) in
2532 let idx = int_of_string
(snd n
) in
2533 let nth = List.nth_exn
[ty1; ty2] idx in
2536 Errors.typing_error
p (Reason.string_of_ureason
Reason.URpair_get
);
2537 env, (Reason.Rwitness
p, Tany
)
2540 Errors.typing_error
p (Reason.string_of_ureason
Reason.URpair_access
);
2541 env, (Reason.Rwitness
p, Tany
)
2543 | Tshape
(_, fdm
) ->
2545 let field = TUtils.shape_field_name
p e2'
in
2546 (match ShapeMap.get
field fdm
with
2548 Errors.undefined_field
p (TUtils.get_printable_shape_field_name
field);
2549 env, (Reason.Rwitness
p, Tany
)
2550 | Some
ty -> env, ty
2553 Errors.null_container
p
2555 "This is what makes me believe it can be null"
2558 env, (Reason.Rwitness
p, Tany
)
2560 if Env.is_strict
env
2561 then error_array
env p ety1
2562 else env, (Reason.Rnone
, Tany
)
2563 | Tabstract
(_, Some
ty) ->
2564 let env, ety
= Env.expand_type
env ty in
2566 (fun () -> array_get
is_lvalue p env ty ety
e2 ty2)
2567 (fun _ -> error_array
env p ety1
)
2568 | Tmixed
| Tarray
(_, _) | Tprim
_ | Tvar
_ | Tfun
_
2569 | Tabstract
(_, _) | Tclass
(_, _) | Tanon
(_, _) ->
2570 error_array
env p ety1
2572 and array_append
is_lvalue p env ty1 =
2573 let env, ty1 = TUtils.fold_unresolved
env ty1 in
2574 let env, ety1
= Env.expand_type
env ty1 in
2576 | Tany
| Tarray
(None
, None
) -> env, (Reason.Rnone
, Tany
)
2577 | Tclass
((_, n
), [ty])
2578 when n
= SN.Collections.cVector
|| n
= SN.Collections.cSet
->
2580 | Tclass
((_, n
), [])
2581 when n
= SN.Collections.cVector
|| n
= SN.Collections.cSet
->
2582 (* Handle the case where "Vector" or "Set" was used as a typehint
2583 without type parameters *)
2584 env, (Reason.Rnone
, Tany
)
2585 | Tclass
((_, n
), [tkey
; tvalue
]) when n
= SN.Collections.cMap
->
2586 (* You can append a pair to a map *)
2587 env, (Reason.Rmap_append
p, Tclass
((p, SN.Collections.cPair
), [tkey
; tvalue
]))
2588 | Tclass
((_, n
), []) when n
= SN.Collections.cMap
->
2589 (* Handle the case where "Map" was used as a typehint without
2591 env, (Reason.Rmap_append
p, Tclass
((p, SN.Collections.cPair
), []))
2592 | Tarray
(Some
ty, None
) ->
2595 if Env.is_strict
env
2596 then error_array_append
env p ety1
2597 else env, (Reason.Rnone
, Tany
)
2598 | Tabstract
(_, Some
ty) ->
2600 (fun () -> array_append
is_lvalue p env ty)
2601 (fun _ -> error_array_append
env p ety1
)
2602 | Tmixed
| Tarray
(_, _) | Toption
_ | Tprim
_
2603 | Tvar
_ | Tfun
_ | Tabstract
(_, _) | Tclass
(_, _) | Ttuple
_
2604 | Tanon
(_, _) | Tunresolved
_ | Tshape
_ ->
2605 error_array_append
env p ety1
2607 and error_array
env p (r, ty) =
2608 Errors.array_access
p (Reason.to_pos
r) (Typing_print.error
ty);
2609 env, (Reason.Rwitness
p, Tany
)
2611 and error_array_append
env p (r, ty) =
2612 Errors.array_append
p (Reason.to_pos
r) (Typing_print.error
ty);
2613 env, (Reason.Rwitness
p, Tany
)
2615 and error_const_mutation
env p (r, ty) =
2616 Errors.const_mutation
p (Reason.to_pos
r) (Typing_print.error
ty);
2617 env, (Reason.Rwitness
p, Tany
)
2620 * Checks if a class (given by cty) contains a given static method.
2622 * We could refactor this + class_get
2624 and class_contains_smethod
env cty
(_pos
, mid
) =
2626 | _, Tabstract
(_, Some
(_, Tclass
((_, c), _)))
2627 | _, Tclass
((_, c), _) ->
2628 let class_ = Env.get_class
env c in
2632 Env.get_static_member
true env class_ mid
2634 | _, (Tany
| Tmixed
| Tarray
(_, _) | Toption
_ | Tprim
_
2635 | Tvar
_ | Tfun
_ | Tabstract
(_, _) | Ttuple
_ | Tanon
(_, _)
2636 | Tunresolved
_ | Tobject
| Tshape
_)-> None
2638 and class_get ~
is_method ~is_const ?
(incl_tc
=false) env cty
(p, mid
) cid =
2641 this_for_method
env cid cty
2645 type_expansions
= [];
2647 substs
= SMap.empty
;
2648 from_class
= Some
cid;
2650 class_get_ ~
is_method ~is_const ~
ety_env ~incl_tc
env cty
(p, mid
)
2652 and class_get_ ~
is_method ~is_const ~
ety_env ?
(incl_tc
=false) env cty
(p, mid
) =
2654 | _, Tabstract
(_, Some cty
) ->
2655 class_get_ ~
is_method ~is_const ~
ety_env ~incl_tc
env cty
(p, mid
)
2656 | _, Tclass
((_, c), paraml
) ->
2657 let class_ = Env.get_class
env c in
2659 | None
-> env, (Reason.Rnone
, Tany
)
2664 then Env.get_const
env class_ mid
2665 else (match Env.get_typeconst
env class_ mid
with
2667 Errors.illegal_typeconst_direct_access
p;
2670 Env.get_const
env class_ mid
))
2671 else Env.get_static_member
is_method env class_ mid
in
2672 if !Typing_defs.accumulate_method_calls
then
2673 Typing_defs.accumulate_method_calls_result
:=
2674 (p, (class_.tc_name^
"::"^mid
)) ::
2675 !Typing_defs.accumulate_method_calls_result
;
2676 Typing_hooks.dispatch_smethod_hook
2677 class_ (p, mid
) env ety_env.from_class ~
is_method;
2679 | None
when not
is_method ->
2680 smember_not_found
p ~is_const ~
is_method class_ mid
;
2681 env, (Reason.Rnone
, Tany
)
2683 (match Env.get_static_member
is_method env class_ SN.Members.__callStatic
with
2685 smember_not_found
p ~is_const ~
is_method class_ mid
;
2686 env, (Reason.Rnone
, Tany
)
2687 | Some
{ce_visibility
= vis
; ce_type
= (r, Tfun
ft); _} ->
2688 check_visibility
p env (Reason.to_pos
r, vis
)
2692 substs
= TSubst.make
class_.tc_tparams paraml
} in
2693 let env, ft = Phase.localize_ft ~
ety_env env ft in
2695 ft_arity
= Fellipsis
0;
2696 ft_tparams
= []; ft_params
= [];
2699 | _ -> assert false)
2700 | Some
{ ce_visibility
= vis
; ce_type
= method_
; _ } ->
2701 check_visibility
p env (Reason.to_pos
(fst method_
), vis
)
2705 substs
= TSubst.make
class_.tc_tparams paraml
} in
2707 Phase.localize ~
ety_env env method_
in
2710 | _, (Tany
| Tabstract
_) ->
2711 (match Env.get_mode
env with
2712 | FileInfo.Mstrict
-> Errors.expected_class
p
2713 | FileInfo.Mdecl
| FileInfo.Mpartial
-> ()
2715 env, (Reason.Rnone
, Tany
)
2716 | _, (Tmixed
| Tarray
(_, _) | Toption
_ | Tprim
_ | Tvar
_
2717 | Tfun
_ | Ttuple
_ | Tanon
(_, _) | Tunresolved
_
2718 | Tobject
| Tshape
_) ->
2719 Errors.expected_class
p;
2720 env, (Reason.Rnone
, Tany
)
2722 and smember_not_found
pos ~is_const ~
is_method class_ member_name
=
2724 if is_const
then `class_constant
2725 else if is_method then `static_method
2726 else `class_variable
in
2728 let cid = (class_.tc_pos
, class_.tc_name
) in
2729 Errors.smember_not_found
kind pos cid member_name hint
2731 match Env.suggest_static_member
is_method class_ member_name
with
2733 (match Env.suggest_member
is_method class_ member_name
with
2734 | None
when not
class_.tc_members_fully_known
->
2735 (* no error in this case ... the member might be present
2736 * in one of the parents of class_ that the typing cannot see *)
2741 error (`closest
(pos2
, v
))
2744 error (`did_you_mean
(pos2
, v
))
2746 and member_not_found
pos ~
is_method class_ member_name
r =
2747 let kind = if is_method then `method_
else `member
in
2748 let cid = class_.tc_pos
, class_.tc_name
in
2749 let reason = Reason.to_string
2750 ("This is why I think it is an object of type "^strip_ns
class_.tc_name
) r
2753 Errors.member_not_found
kind pos cid member_name hint
reason in
2754 match Env.suggest_member
is_method class_ member_name
with
2756 (match Env.suggest_static_member
is_method class_ member_name
with
2757 | None
when not
class_.tc_members_fully_known
->
2758 (* no error in this case ... the member might be present
2759 * in one of the parents of class_ that the typing cannot see *)
2763 | Some
(def_pos
, v
) ->
2764 error (`closest
(def_pos
, v
))
2766 | Some
(def_pos
, v
) ->
2767 error (`did_you_mean
(def_pos
, v
))
2769 (* The type of the object member is passed into the continuation k. This is
2770 * useful for typing nullsafed method calls. Consider `$x?->f()`: obj_get will
2771 * pass `k` the type of f, and `k` will typecheck the method call and return
2772 * the method's return type. obj_get then wraps that type in a Toption. *)
2773 and obj_get ~
is_method ~
nullsafe env ty1 cid id k
=
2776 | Some
p when not
(type_could_be_null
env ty1) ->
2777 let env, (r, _) = Env.expand_type
env ty1 in
2778 Errors.nullsafe_not_needed
p
2780 "This is what makes me believe it cannot be null" r);
2783 let env, method_
, _ =
2784 obj_get_with_visibility ~
is_method ~
nullsafe env ty1 cid id k
in
2787 and obj_get_with_visibility ~
is_method ~
nullsafe env ty1
2789 obj_get_ ~
is_method ~
nullsafe env ty1 cid id k
(fun ty -> ty)
2791 and obj_get_ ~
is_method ~
nullsafe env ty1 cid (p, s
as id
)
2793 let env, ety1
= Env.expand_type
env ty1 in
2795 | _, Tunresolved tyl
->
2796 let (env, vis
), tyl
=
2798 (fun (env, vis
) ty ->
2800 obj_get_ ~
is_method ~
nullsafe env ty cid id k
k_lhs in
2801 (* There is one special case where we need to expose the
2802 * visibility outside of obj_get (checkout inst_meth special
2804 * We keep a witness of the "most restrictive" visibility
2805 * we encountered (position + visibility), to be able to
2806 * special case inst_meth.
2808 let vis = TUtils.min_vis_opt
vis vis'
in
2812 let env, method_
= TUtils.in_var
env (fst ety1
, Tunresolved
(tyl
)) in
2814 | p, Tabstract
(ak
, Some
ty) ->
2815 (* We probably don't want to rewrap new types for the 'this' closure *)
2816 let k_lhs'
ty = match ak
with
2817 | AKnewtype
(_, _) -> k_lhs ty
2818 | _ -> k_lhs (p, Tabstract
(ak
, Some
ty)) in
2819 obj_get_ ~
is_method ~
nullsafe env ty cid id k
k_lhs'
2820 | _, Toption
ty -> begin match nullsafe with
2822 let k'
(env, fty, x) = begin
2823 let env, method_
, x = k (env, fty, x) in
2824 let env, method_
= non_null
env method_
in
2825 env, (Reason.Rnullsafe_op
p1, Toption method_
), x
2827 obj_get_ ~
is_method ~
nullsafe env ty cid id
k'
k_lhs
2829 Errors.null_member s
p
2831 "This is what makes me believe it can be null"
2834 k (env, (fst ety1
, Tany
), None
)
2836 | _, (Tany
| Tmixed
| Tarray
(_, _) | Tprim
_ | Tvar
_
2837 | Tfun
_ | Tabstract
(_, _) | Tclass
(_, _) | Ttuple
_ | Tanon
(_, _)
2838 | Tobject
| Tshape
_) -> k begin
2840 | Tclass
(x, paraml
) ->
2841 let class_ = Env.get_class
env (snd
x) in
2844 env, (Reason.Rnone
, Tany
), None
2845 | Some
class_ when not
is_method
2846 && not
(Env.is_strict
env)
2847 && class_.tc_name
= SN.Classes.cStdClass
->
2848 env, (Reason.Rnone
, Tany
), None
2851 if List.length
paraml = 0
2852 then List.map
class_.tc_tparams
(fun _ -> Reason.Rwitness
p, Tany
)
2855 let member_ = Env.get_member
is_method env class_ s
in
2856 if !Typing_defs.accumulate_method_calls
then
2857 Typing_defs.accumulate_method_calls_result
:=
2858 (p, (class_.tc_name^
"::"^s
)) ::
2859 !Typing_defs.accumulate_method_calls_result
;
2860 Typing_hooks.dispatch_cmethod_hook
2861 class_ (p, s
) env None ~
is_method;
2863 | None
when not
is_method ->
2864 if not
(SN.Members.is_special_xhp_attribute s
)
2865 then member_not_found
p ~
is_method class_ s
(fst ety1
);
2866 env, (Reason.Rnone
, Tany
), None
2868 (match Env.get_member
is_method env class_ SN.Members.__call
with
2870 member_not_found
p ~
is_method class_ s
(fst ety1
);
2871 env, (Reason.Rnone
, Tany
), None
2872 | Some
{ce_visibility
= vis; ce_type
= (r, Tfun
ft); _} ->
2873 let mem_pos = Reason.to_pos
r in
2874 check_visibility
p env (mem_pos, vis) None
;
2875 (* the return type of __call can depend on the
2876 * class params or be this *)
2877 let this_ty = k_lhs ety1
in
2879 type_expansions
= [];
2881 substs
= TSubst.make
class_.tc_tparams
paraml;
2882 from_class
= Some
cid;
2884 let env, ft = Phase.localize_ft ~
ety_env env ft in
2886 (* we change the params of the underlying
2887 * declaration to act as a variadic function
2888 * ... this transform cannot be done when
2889 * processing the declaration of call because
2890 * direct calls to $inst->__call are also
2893 ft_arity
= Fellipsis
0;
2894 ft_tparams
= []; ft_params
= [];
2896 let member_ = (r, Tfun
ft) in
2897 env, member_, Some
(mem_pos, vis)
2900 | Some
({ce_visibility
= vis; ce_type
= member_; _ } as member_ce
) ->
2901 let mem_pos = Reason.to_pos
(fst
member_) in
2902 check_visibility
p env (mem_pos, vis) None
;
2903 let member_ = Typing_enum.member_type
env member_ce
in
2904 let this_ty = k_lhs ety1
in
2906 type_expansions
= [];
2908 substs
= TSubst.make
class_.tc_tparams
paraml;
2909 from_class
= Some
cid;
2911 let env, member_ = Phase.localize ~
ety_env env member_ in
2912 env, member_, Some
(mem_pos, vis)
2916 | Tany
-> env, (fst ety1
, Tany
), None
2917 | (Tmixed
| Tarray
(_, _) | Tprim
_ | Toption
_
2918 | Tvar
_ | Tabstract
(_, _) | Ttuple
_ | Tanon
(_, _)
2919 | Tfun
_ | Tunresolved
_ | Tshape
_) as ty ->
2920 Errors.non_object_member
2921 s
p (Typing_print.error ty) (Reason.to_pos
(fst ety1
));
2922 env, (fst ety1
, Tany
), None
2925 and type_could_be_null
env ty1 =
2926 let env, ety1
= Env.expand_type
env ty1 in
2927 match (snd ety1
) with
2928 | Tabstract
(_, Some
ty) -> type_could_be_null
env ty
2929 | Toption
_ | Tabstract
(_, None
) | Tunresolved
_ | Tmixed
| Tany
-> true
2930 | Tarray
(_, _) | Tprim
_ | Tvar
_ | Tfun
_
2931 | Tclass
(_, _) | Ttuple
_ | Tanon
(_, _) | Tobject
2934 and class_id_for_new
p env cid =
2935 let env, obj
= static_class_id
p env cid in
2938 | _, Tabstract
(AKdependent
(`this
, []), Some
(_, Tclass
(c, _)))
2939 | _, Tabstract
(AKgeneric
_, Some
(_, Tclass
(c, _))) ->
2940 let class_ = Env.get_class
env (snd
c) in
2941 env, (match class_ with
2943 | Some
class_ -> Some
(c, class_, obj
)
2945 | _, (Tany
| Tmixed
| Tarray
(_, _) | Toption
_ | Tprim
_
2946 | Tvar
_ | Tfun
_ | Tabstract
(_, _) | Ttuple
_ | Tanon
(_, _)
2947 | Tunresolved
_ | Tobject
| Tshape
_) -> env, None
2949 (* To be a valid trait declaration, all of its 'require extends' must
2950 * match; since there's no multiple inheritance, it follows that all of
2951 * the 'require extends' must belong to the same inheritance hierarchy
2952 * and one of them should be the child of all the others *)
2953 and trait_most_concrete_req_class trait
env =
2954 SMap.fold
(fun name ty acc
->
2955 let keep = match acc
with
2956 | Some
(c, _ty
) -> SMap.mem
name c.tc_ancestors
2961 let class_ = Env.get_class
env name in
2964 | Some
{ tc_kind
= Ast.Cinterface
; _ } -> acc
2965 | Some
{ tc_kind
= Ast.Ctrait
; _ } ->
2966 (* this is an error case for which the nastCheck spits out
2967 * an error, but does *not* currently remove the offending
2968 * 'require extends' or 'require implements' *)
2970 | Some
c -> Some
(c, ty)
2972 ) trait
.tc_req_ancestors None
2974 (* When invoking a method the class_id is used to determine what class we
2975 * lookup the method in, but the type of 'this' will be the late bound type.
2979 * public static function get(): this { return new static(); }
2981 * public static function alias(): this { return self::get(); }
2984 * In C::alias, when we invoke self::get(), 'self' is resolved to the class
2985 * in the lexical scope (C), so call C::get. However the method is executed in
2986 * the current context, so static inside C::get will be resolved to the late
2987 * bound type (get_called_class() within C::alias).
2989 * This means when determining the type of this, CIparent and CIself should be
2990 * changed to CIstatic. For the other cases of C::get() or $c::get(), we only
2991 * look at the left hand side of the '::' and use the type type associated
2994 * Thus C::get() will return a type C, while $c::get() will return the same
2997 and this_for_method
env cid default_ty
= match cid with
2998 | CIparent
| CIself
| CIstatic
->
2999 let p = Reason.to_pos
(fst default_ty
) in
3000 let env, ty = static_class_id
p env CIstatic
in
3001 env, ExprDepTy.make
env CIstatic
ty
3005 and static_class_id
p env = function
3007 (match Env.get_self
env with
3008 | _, Tclass
((_, self
), _) ->
3009 (match Env.get_class
env self
with
3011 {tc_kind
= Ast.Ctrait
; _}
3013 (match trait_most_concrete_req_class trait
env with
3015 Errors.parent_in_trait
p;
3016 env, (Reason.Rwitness
p, Tany
)
3017 | Some
(_, parent_ty
) ->
3018 (* inside a trait, parent is SN.Typehints.this, but with the
3019 * type of the most concrete class that the trait has
3020 * "require extend"-ed *)
3021 let r = Reason.Rwitness
p in
3022 let env, parent_ty
= Phase.localize_with_self
env parent_ty
in
3023 env, (r, TUtils.this_of parent_ty
)
3026 let parent = Env.get_parent
env in
3027 let parent_defined = snd
parent <> Tany
in
3028 if not
parent_defined
3029 then Errors.parent_undefined
p;
3030 let r = Reason.Rwitness
p in
3031 let env, parent = Phase.localize_with_self
env parent in
3032 (* parent is still technically the same object. *)
3033 env, (r, TUtils.this_of
(r, snd
parent))
3035 | _, (Tany
| Tmixed
| Tarray
(_, _) | Toption
_ | Tprim
_
3036 | Tfun
_ | Ttuple
_ | Tshape
_ | Tvar
_
3037 | Tanon
(_, _) | Tunresolved
_ | Tabstract
(_, _) | Tobject
3039 let parent = Env.get_parent
env in
3040 let parent_defined = snd
parent <> Tany
in
3041 if not
parent_defined
3042 then Errors.parent_undefined
p;
3043 let r = Reason.Rwitness
p in
3044 let env, parent = Phase.localize_with_self
env parent in
3045 (* parent is still technically the same object. *)
3046 env, (r, TUtils.this_of
(r, snd
parent))
3049 env, (Reason.Rwitness
p, TUtils.this_of
(Env.get_self
env))
3051 env, (Reason.Rwitness
p, snd
(Env.get_self
env))
3053 let class_ = Env.get_class
env (snd
c) in
3055 | None
-> env, (Reason.Rnone
, Tany
) (* Tobject *)
3057 let env, params = lfold
begin fun env _ ->
3058 TUtils.in_var
env (Reason.Rnone
, Tunresolved
[])
3059 end env class_.tc_tparams
in
3060 env, (Reason.Rwitness
(fst
c), Tclass
(c, params))
3063 let env, ty = expr
env e
in
3064 let env, ty = TUtils.fold_unresolved
env ty in
3065 let _, ty = Env.expand_type
env ty in
3066 let rec resolve_ety = fun ety
-> begin
3067 match TUtils.get_base_type ety
with
3068 | _, Tabstract
(AKnewtype
(classname
, [the_cls
]), _) when
3069 classname
= SN.Classes.cClassname
->
3071 | _, Tabstract
(AKgeneric
_, _)
3072 | _, Tclass
_ -> ety
3073 | _, (Tany
| Tmixed
| Tarray
(_, _) | Toption
_
3074 | Tprim
_ | Tvar
_ | Tfun
_ | Ttuple
_
3075 | Tabstract
((AKenum
_ | AKdependent
_ | AKnewtype
_), _)
3076 | Tanon
(_, _) | Tunresolved
_ | Tobject
| Tshape
_
3078 if Env.get_mode
env = FileInfo.Mstrict
3079 then Errors.dynamic_class
p;
3082 in env, resolve_ety ty
3084 and call_construct
p env class_ params el uel
cid =
3085 let cstr = Env.get_construct
env class_ in
3086 let mode = Env.get_mode
env in
3087 Typing_hooks.dispatch_constructor_hook
class_ env p;
3088 match (fst
cstr) with
3091 (mode = FileInfo.Mstrict
|| mode = FileInfo.Mpartial
) &&
3092 class_.tc_members_fully_known
3093 then Errors.constructor_no_args
p;
3094 fst
(lfold expr
env el
)
3095 | Some
{ ce_visibility
= vis; ce_type
= m
; _ } ->
3096 check_visibility
p env (Reason.to_pos
(fst m
), vis) None
;
3097 let cid = if cid = CIparent
then CIstatic
else cid in
3098 let env, cid_ty
= static_class_id
p env cid in
3100 type_expansions
= [];
3102 substs
= TSubst.make
class_.tc_tparams
params;
3103 from_class
= Some
cid;
3105 let env, m
= Phase.localize ~
ety_env env m
in
3106 fst
(call
p env m el uel
)
3108 and check_visibility
p env (p_vis
, vis) cid =
3109 match is_visible
env vis cid with
3111 | Some
(msg1
, msg2
) -> Errors.visibility
p msg1 p_vis msg2
3113 and is_visible
env vis cid =
3114 let self_id = Env.get_self_id
env in
3117 | Vprivate
_ when (Env.is_outside_class
env) ->
3118 Some
("You cannot access this member", "This member is private")
3122 let my_class = Env.get_class
env self_id in
3123 begin match my_class with
3124 | Some
{tc_final
= true; _} -> None
3126 ("Private members cannot be accessed with static:: since"
3127 ^
" a child class may also have an identically"
3128 ^
" named private member"),
3129 "This member is private")
3133 "You cannot access a private member with parent::",
3134 "This member is private")
3135 | Some CIself
-> None
3136 | Some
(CI
(_, called_ci
)) when x <> self_id ->
3137 (match Env.get_class
env called_ci
with
3138 | Some
{tc_kind
= Ast.Ctrait
; _} ->
3139 Some
("You cannot access private members"
3140 ^
" using the trait's name (did you mean to use self::?)",
3141 "This member is private")
3143 Some
("You cannot access this member", "This member is private"))
3144 | Some
(CIexpr e
) ->
3145 let env, ty = expr
env e
in
3146 let _, ty = Env.expand_type
env ty in
3147 begin match TUtils.get_base_type
ty with
3148 | _, Tclass
((_, c), _) ->
3149 (match Env.get_class
env c with
3150 | Some
{tc_final
= true; _} -> None
3152 ("Private members cannot be accessed dynamically. "
3153 ^
"Did you mean to use 'self::'?"),
3154 "This member is private"))
3155 | _, (Tany
| Tmixed
| Tarray
(_, _) | Toption
_
3156 | Tprim
_ | Tvar
_ | Tfun
_ | Tabstract
(_, _) | Ttuple
_
3157 | Tanon
(_, _) | Tunresolved
_ | Tobject
3158 | Tshape
_) -> assert false
3160 | None
when x <> self_id ->
3161 Some
("You cannot access this member", "This member is private")
3164 | Vprotected
x when x = self_id -> None
3165 | Vprotected
_ when (Env.is_outside_class
env) ->
3166 Some
("You cannot access this member", "This member is protected")
3168 let my_class = Env.get_class
env self_id in
3169 let their_class = Env.get_class
env x in
3170 match cid, their_class with
3171 | Some CI
_, Some
{tc_kind
= Ast.Ctrait
; _} ->
3172 Some
("You cannot access protected members"
3173 ^
" using the trait's name (did you mean to use static:: or self::?)",
3174 "This member is protected")
3176 match my_class, their_class with
3177 | Some
my_class, Some
their_class ->
3178 (* Children can call parent's protected methods and
3179 * parents can call children's protected methods (like a
3181 if SSet.mem
x my_class.tc_extends
3182 || SSet.mem
self_id their_class.tc_extends
3183 || SSet.mem
x my_class.tc_req_ancestors_extends
3184 || not
my_class.tc_members_fully_known
3187 "Cannot access this protected member, you don't extend "^
3189 "This member is protected"
3194 and check_arity ?
(check_min
=true) pos pos_def
(arity:int) exp_arity
=
3195 let exp_min = (Typing_defs.arity_min exp_arity
) in
3196 if check_min
&& arity < exp_min then
3197 Errors.typing_too_few_args
pos pos_def
;
3198 match exp_arity
with
3199 | Fstandard
(_, exp_max
) ->
3200 if (arity > exp_max
)
3201 then Errors.typing_too_many_args
pos pos_def
;
3202 | Fvariadic
_ | Fellipsis
_ -> ()
3204 and check_deprecated
p { ft_pos
; ft_deprecated
; _ } =
3205 match ft_deprecated
with
3206 | Some s
-> Errors.deprecated_use
p ft_pos s
3209 (* The variadic capture argument is an array listing the passed
3210 * variable arguments for the purposes of the function body; callsites
3211 * should not unify with it *)
3212 and variadic_param
env ft =
3213 match ft.ft_arity
with
3214 | Fvariadic
(_, p_ty
) -> env, Some p_ty
3215 | Fellipsis
_ | Fstandard
_ -> env, None
3217 and call
pos env fty el uel
=
3218 let env, ty = call_
pos env fty el uel
in
3219 (* We need to solve the constraints after every single function call.
3220 * The type-checker is control-flow sensitive, the same value could
3221 * have different type depending on the branch that we are in.
3222 * When this is the case, a call could violate one of the constraints
3224 let env = fold_fun_list
env env.Env.todo
in
3227 (* Enforces that e is unpackable. If e is a tuple, appends its unpacked types
3228 * into the e_tyl returned.
3230 and unpack_expr
env e_tyl e
=
3231 let env, ety
= expr
env e
in
3234 (* Todo: Check that tuples are allowed - that is, disallow a tuple
3235 * unpacking after an array unpacking.
3237 let unpacked_e_tyl = List.map tyl
(fun ty -> e
, ty) in
3238 env, e_tyl
@ unpacked_e_tyl, true
3241 let unpack_r = Reason.Runpack_param
pos in
3242 let container_ty = (unpack_r, Tclass
((pos, SN.Collections.cContainer
),
3243 [unpack_r, Tany
])) in
3244 let env = Type.sub_type
pos Reason.URparam
env container_ty ety
in
3248 (* Unpacks uel. If tuples are found, unpacked types are appended to the
3251 and unpack_exprl
env e_tyl uel
=
3252 List.fold_left uel ~init
:(env, e_tyl
, false)
3253 ~
f: begin fun (env, e_tyl
, unpacked_tuple
) e
->
3254 let env, e_tyl
, is_tuple
= unpack_expr
env e_tyl e
in
3255 (env, e_tyl
, is_tuple
|| unpacked_tuple
)
3258 and call_
pos env fty el uel
=
3259 let env, efty
= Env.expand_type
env fty in
3261 | _, (Tany
| Tunresolved
[]) ->
3262 let el = el @ uel
in
3264 (fun env elt
-> begin
3265 let env, arg_ty
= expr
env elt
in
3266 let arg_ty = check_valid_rvalue
pos env arg_ty in
3269 Typing_hooks.dispatch_fun_call_hooks
[] (List.map
(el @ uel
) fst
) env;
3270 env, (Reason.Rnone
, Tany
)
3271 | r, Tunresolved tyl
->
3272 let env, retl
= lmap
(fun env ty -> call
pos env ty el uel
) env tyl
in
3273 TUtils.in_var
env (r, Tunresolved retl
)
3275 (* Typing of format string functions. It is dependent on the arguments (el)
3276 * so it cannot be done earlier.
3278 let env, ft = Typing_exts.retype_magic_func
env ft el in
3279 check_deprecated
pos ft;
3280 let pos_def = Reason.to_pos
r2 in
3281 let env, var_param
= variadic_param
env ft in
3282 let env, e_tyl
= lmap
begin fun env e
->
3283 let env, ty = expr
env e
in
3286 let env, e_tyl
, unpacked_tuple
= unpack_exprl
env e_tyl uel
in
3287 let arity = if unpacked_tuple
3288 then List.length e_tyl
3289 (* Each array unpacked corresponds with at least 1 param. *)
3290 else List.length
el + List.length uel
in
3291 (* If we unpacked an array, we don't check arity exactly. Since each
3292 * unpacked array consumes 1 or many parameters, it is nonsensical to say
3293 * that not enough args were passed in (so we don't do the min check).
3295 let () = check_arity ~check_min
:(uel
= [] || unpacked_tuple
)
3296 pos pos_def arity ft.ft_arity
in
3297 let todos = ref [] in
3298 let env = wfold_left_default (call_param
todos) (env, var_param
)
3299 ft.ft_params e_tyl
in
3300 let env = fold_fun_list
env !todos in
3301 Typing_hooks.dispatch_fun_call_hooks
3302 ft.ft_params
(List.map
(el @ uel
) fst
) env;
3304 | r2, Tanon
(arity, id
) when uel
= [] ->
3305 let env, tyl
= lmap expr
env el in
3306 let anon = Env.get_anonymous
env id
in
3307 let fpos = Reason.to_pos
r2 in
3310 Errors.anonymous_recursive_call
pos;
3311 env, (Reason.Rnone
, Tany
)
3313 let () = check_arity
pos fpos (List.length tyl
) arity in
3314 let tyl = List.map
tyl (fun x -> None
, x) in
3316 | _, Tarray
_ when not
(Env.is_strict
env) ->
3317 (* Relaxing call_user_func to work with an array in partial mode *)
3318 env, (Reason.Rnone
, Tany
)
3321 env, (Reason.Rnone
, Tany
)
3324 and call_param
todos env (name, x) ((pos, _ as e
), arg_ty) =
3327 | Some
name -> Typing_suggest.save_param
name env x arg_ty
3329 let arg_ty = check_valid_rvalue
pos env arg_ty in
3331 (* When checking params the type 'x' may be expression dependent. Since
3332 * we store the expression id in the local env for Lvar, we want to apply
3335 let dep_ty = match snd e
with
3336 | Lvar
_ -> ExprDepTy.make
env (CIexpr e
) arg_ty
3338 (* We solve for Tanon types after all the other params because we want to
3339 * typecheck the lambda bodies with as much type information as possible. For
3340 * example, in array_map(fn, x), we might be able to use the type of x to
3341 * infer the type of fn, but if we call sub_type on fn first, we end up
3342 * typechecking its body without the benefit of knowing its full type. If
3343 * fn is typehinted but not x, we could use fn to infer the type of x, but
3344 * in practice the reverse situation is more likely. This rearrangement is
3345 * particularly useful since higher-order functions usually put fn before x.
3349 todos := (fun env ->
3350 Type.sub_type
pos Reason.URparam
env x arg_ty) :: !todos;
3352 | _, (Tany
| Tmixed
| Tarray
(_, _) | Toption
_ | Tprim
_
3353 | Tvar
_ | Tfun
_ | Tabstract
(_, _) | Tclass
(_, _) | Ttuple
_
3354 | Tunresolved
_ | Tobject
| Tshape
_) ->
3355 Type.sub_type
pos Reason.URparam
env x dep_ty
3358 Errors.bad_call
p (Typing_print.error ty)
3360 and unop
p env uop
ty =
3363 Async.enforce_not_awaitable
env p ty;
3364 (* !$x (logical not) works with any type, so we just return Tbool *)
3365 env, (Reason.Rlogic_ret
p, Tprim Tbool
)
3367 (* ~$x (bitwise not) only works with int *)
3368 Type.unify
p Reason.URnone
env (Reason.Rarith
p, Tprim Tint
) ty
3375 (* math operators work with int or floats, so we call sub_type *)
3376 let env = Type.sub_type
p Reason.URnone
env (Reason.Rarith
p, Tprim Tnum
) ty in
3379 (* We basically just ignore references *)
3382 and binop in_cond
p env bop
p1 ty1 p2 ty2 =
3383 let expand_num_type env p ty =
3384 let env, ty = TUtils.fold_unresolved
env ty in
3385 let env = Type.sub_type
p Reason.URnone
env (Reason.Rarith
p, Tprim Tnum
) ty in
3386 let env, ety
= Env.expand_type
env ty in
3390 let env, ty1 = TUtils.fold_unresolved
env ty1 in
3391 let env, ty2 = TUtils.fold_unresolved
env ty2 in
3392 let env, ety1
= Env.expand_type
env ty1 in
3393 let env, ety2
= Env.expand_type
env ty2 in
3394 (match ety1
, ety2
with
3395 (* For array<V1>+array<V2> and array<K1,V1>+array<K2,V2>, allow
3396 * the addition to produce a supertype. (We could also handle
3397 * when they have mismatching annotations, but we get better error
3398 * messages if we just let those get unified in the next case. *)
3399 | (_, Tarray
(Some
_, (Some
_ as t1b
))), (_, Tarray
(Some
_, Some
_))
3400 | (_, Tarray
(Some
_, (None
as t1b
))), (_, Tarray
(Some
_, None
)) ->
3401 let env, a_sup
= TUtils.in_var
env (Reason.Rnone
, Tunresolved
[]) in
3402 let env, b_sup
= opt
3403 (fun env _ -> TUtils.in_var
env (Reason.Rnone
, Tunresolved
[]))
3405 let res_ty = Reason.Rarray_plus_ret
p, Tarray
(Some a_sup
, b_sup
) in
3406 let env = Type.sub_type
p1 Reason.URnone
env res_ty ety1
in
3407 let env = Type.sub_type
p2 Reason.URnone
env res_ty ety2
in
3409 | (_, Tarray
_), (_, Tarray
_)
3410 | (_, Tany
), (_, Tarray
_)
3411 | (_, Tarray
_), (_, Tany
) ->
3412 let env, ty = Type.unify
p Reason.URnone
env ty1 ty2 in
3414 | (_, (Tany
| Tmixed
| Tarray
(_, _) | Toption
_
3415 | Tprim
_ | Tvar
_ | Tfun
_ | Tabstract
(_, _) | Tclass
(_, _)
3416 | Ttuple
_ | Tanon
(_, _) | Tunresolved
_ | Tobject
| Tshape
_
3418 ), _ -> binop in_cond
p env Ast.Minus
p1 ty1 p2 ty2
3420 | Ast.Minus
| Ast.Star
->
3421 let env, ty1 = TUtils.fold_unresolved
env ty1 in
3422 let env, ty2 = TUtils.fold_unresolved
env ty2 in
3423 let env = Type.sub_type
p1 Reason.URnone
env
3424 (Reason.Rarith
p1, Tprim Tnum
) ty1 in
3425 let env = Type.sub_type
p2 Reason.URnone
env
3426 (Reason.Rarith
p2, Tprim Tnum
) ty2 in
3427 let env, ety1
= Env.expand_type
env ty1 in
3428 let env, ety2
= Env.expand_type
env ty2 in
3429 (match ety1
, ety2
with
3430 | (r, Tprim Tfloat
), _ | _, (r, Tprim Tfloat
) ->
3431 (* if either side is a float then float: 1.0 - 1 -> float *)
3432 env, (r, Tprim Tfloat
)
3433 | (r, Tprim Tnum
), _ | _, (r, Tprim Tnum
) ->
3434 (* if either side is a num, then num: (3 / x) - 1 -> num *)
3435 env, (r, Tprim Tnum
)
3436 | (_, Tprim Tint
), (_, Tprim Tint
) ->
3437 (* Both sides are integers, then integer: 1 - 1 -> int *)
3438 env, (Reason.Rarith_ret
p, Tprim Tint
)
3439 | (_, (Tany
| Tmixed
| Tarray
(_, _) | Toption
_
3440 | Tprim
_ | Tvar
_ | Tfun
_ | Tabstract
(_, _) | Tclass
(_, _)
3441 | Ttuple
_ | Tanon
(_, _) | Tunresolved
_ | Tobject
| Tshape
_
3444 (* Either side is unknown, unknown *)
3445 (* TODO um, what? This seems very wrong, particularly where "newtype
3447 * This also causes issues with primitive constraints on generics.
3448 * See test/typecheck/generic_primitive_invariant.php as an example *)
3451 let env, ety1
= expand_num_type env p1 ty1 in
3452 let env, ety2
= expand_num_type env p2 ty2 in
3453 (match ety1
, ety2
with
3454 | (r, Tprim Tfloat
), _ | _, (r, Tprim Tfloat
) -> env, (r, Tprim Tfloat
)
3455 | (_, (Tany
| Tmixed
| Tarray
(_, _) | Toption
_
3456 | Tprim
_ | Tvar
_ | Tfun
_ | Tabstract
(_, _) | Tclass
(_, _)
3457 | Ttuple
_ | Tanon
(_, _) | Tunresolved
_ | Tobject
| Tshape
_
3459 ), _ -> env, (Reason.Rret_div
p, Tprim Tnum
)
3462 let env, ety1
= expand_num_type env p1 ty1 in
3463 let env, ety2
= expand_num_type env p2 ty2 in
3464 (match ety1
, ety2
with
3465 | (r, Tprim Tfloat
), _ | _, (r, Tprim Tfloat
) -> env, (r, Tprim Tfloat
)
3466 | (_, (Tany
| Tmixed
| Tarray
(_, _) | Toption
_
3467 | Tprim
_ | Tvar
_ | Tfun
_ | Tabstract
(_, _) | Tclass
(_, _)
3468 | Ttuple
_ | Tanon
(_, _) | Tunresolved
_ | Tobject
| Tshape
_
3470 ), _ -> env, (Reason.Rarith_ret
p, Tprim Tnum
)
3473 let env = Type.sub_type
p Reason.URnone
env (Reason.Rarith
p1, Tprim Tint
) ty1 in
3474 let env = Type.sub_type
p Reason.URnone
env (Reason.Rarith
p1, Tprim Tint
) ty2 in
3475 env, (Reason.Rarith_ret
p, Tprim Tint
)
3477 let env, ty1 = TUtils.fold_unresolved
env ty1 in
3478 let env, ty2 = TUtils.fold_unresolved
env ty2 in
3479 let env, ety1
= Env.expand_type
env ty1 in
3480 let env, ety2
= Env.expand_type
env ty2 in
3481 (match ety1
, ety2
with
3482 | (_, Tprim Tbool
), _ | _, (_, Tprim Tbool
) ->
3483 let env, _ = Type.unify
p Reason.URnone
env ty1 (Reason.Rlogic_ret
p1, Tprim Tbool
) in
3484 let env, _ = Type.unify
p Reason.URnone
env ty2 (Reason.Rlogic_ret
p1, Tprim Tbool
) in
3485 env, (Reason.Rlogic_ret
p, Tprim Tbool
)
3486 | (_, (Tany
| Tmixed
| Tarray
(_, _) | Toption
_
3487 | Tprim
_ | Tvar
_ | Tfun
_ | Tabstract
(_, _) | Tclass
(_, _)
3488 | Ttuple
_ | Tanon
(_, _) | Tunresolved
_ | Tobject
| Tshape
_
3491 let env, _ = Type.unify
p Reason.URnone
env ty1 (Reason.Rarith
p1, Tprim Tint
) in
3492 let env, _ = Type.unify
p Reason.URnone
env ty2 (Reason.Rarith
p1, Tprim Tint
) in
3493 env, (Reason.Rarith_ret
p, Tprim Tint
)
3495 | Ast.Eqeq
| Ast.Diff
->
3496 env, (Reason.Rcomp
p, Tprim Tbool
)
3497 | Ast.EQeqeq
| Ast.Diff2
->
3499 then TypingEqualityCheck.assert_nontrivial
p bop
env ty1 ty2;
3500 env, (Reason.Rcomp
p, Tprim Tbool
)
3501 | Ast.Lt
| Ast.Lte
| Ast.Gt
| Ast.Gte
->
3502 let ty_num = (Reason.Rcomp
p, Tprim
Nast.Tnum
) in
3503 let ty_string = (Reason.Rcomp
p, Tprim
Nast.Tstring
) in
3505 (Reason.Rcomp
p, Tclass
((p, SN.Classes.cDateTime
), [])) in
3507 SubType.is_sub_type
env ty ty1 && SubType.is_sub_type
env ty ty2 in
3508 if both_sub ty_num || both_sub ty_string || both_sub ty_datetime
3509 then env, (Reason.Rcomp
p, Tprim Tbool
)
3511 (* TODO this is questionable; PHP's semantics for conversions with "<"
3512 * are pretty crazy and we may want to just disallow this? *)
3513 let env, _ = Type.unify
p Reason.URnone
env ty1 ty2 in
3514 env, (Reason.Rcomp
p, Tprim Tbool
)
3516 let env = SubType.sub_string
p1 env ty1 in
3517 let env = SubType.sub_string
p2 env ty2 in
3518 env, (Reason.Rconcat_ret
p, Tprim Tstring
)
3521 env, (Reason.Rlogic_ret
p, Tprim Tbool
)
3522 | Ast.Amp
| Ast.Bar
| Ast.Ltlt
| Ast.Gtgt
->
3523 let env = Type.sub_type
p Reason.URnone
env (Reason.Rbitwise
p1, Tprim Tint
) ty1 in
3524 let env = Type.sub_type
p Reason.URnone
env (Reason.Rbitwise
p2, Tprim Tint
) ty2 in
3525 env, (Reason.Rbitwise_ret
p, Tprim Tint
)
3529 and non_null ?expanded
:(expanded
=ISet.empty
) env ty =
3530 let env, expanded
, ty = Env.expand_type_recorded
env expanded
ty in
3533 let env, expanded
, ty = Env.expand_type_recorded
env expanded
ty in
3534 (* When "??T" appears in the typing environment due to implicit
3535 * typing, the recursion here ensures that it's treated as
3536 * isomorphic to "?T"; that is, all nulls are created equal.
3538 non_null ~expanded
env ty
3539 | r, Tunresolved
tyl ->
3540 let env, tyl = lfold
(non_null ~expanded
) env tyl in
3541 (* We need to flatten the unresolved types, otherwise we could
3542 * end up with "Tunresolved[Tunresolved _]" which is not supposed
3545 let tyl = List.fold_right
tyl ~
f:begin fun ty tyl ->
3547 | _, Tunresolved l
-> l
@ tyl
3550 env, (r, Tunresolved
tyl)
3551 | r, Tabstract
(ak
, Some
ty) ->
3552 let env, ty = non_null ~expanded
env ty in
3553 env, (r, Tabstract
(ak
, Some
ty))
3554 | _, (Tany
| Tmixed
| Tarray
(_, _) | Tprim
_ | Tvar
_
3555 | Tabstract
(_, _) | Tclass
(_, _) | Ttuple
_ | Tanon
(_, _) | Tfun
_
3556 | Tobject
| Tshape
_) ->
3559 and condition_var_non_null
env = function
3561 let env, x_ty
= Env.get_local
env x in
3562 let env, x_ty
= Env.expand_type
env x_ty
in
3563 let env, x_ty
= non_null
env x_ty
in
3564 Env.set_local
env x x_ty
3565 | p, Class_get
(cname
, (_, member_name
)) as e
->
3566 let env, ty = expr
env e
in
3567 let env, local = Env.FakeMembers.make_static
p env cname member_name
in
3568 let env = Env.set_local
env local ty in
3569 let local = p, Lvar
(p, local) in
3570 condition_var_non_null
env local
3571 | p, Obj_get
((_, This
| _, Lvar
_ as obj
),
3572 (_, Id
(_, member_name
)),
3574 let env, ty = expr
env e
in
3575 let env, local = Env.FakeMembers.make
p env obj member_name
in
3576 let env = Env.set_local
env local ty in
3577 let local = p, Lvar
(p, local) in
3578 condition_var_non_null
env local
3581 and condition_isset
env = function
3582 | _, Array_get
(x, _) -> condition_isset
env x
3583 | v
-> condition_var_non_null
env v
3586 * Build an environment for the true or false branch of
3587 * conditional statements.
3589 and condition
env tparamet
=
3590 let expr = raw_expr ~in_cond
:true in function
3591 | _, Expr_list
[] -> env
3592 | _, Expr_list
[x] ->
3593 let env, _ = expr env x in
3594 condition
env tparamet
x
3595 | r, Expr_list
(x::xs
) ->
3596 let env, _ = expr env x in
3597 condition
env tparamet
(r, Expr_list xs
)
3598 | _, Call
(Cnormal
, (_, Id
(_, func
)), [param], [])
3599 when SN.PseudoFunctions.isset
= func
&& tparamet
&&
3600 not
(Env.is_strict
env) ->
3601 condition_isset
env param
3602 | _, Call
(Cnormal
, (_, Id
(_, func
)), [e
], [])
3603 when not tparamet
&& SN.StdlibFunctions.is_null
= func
->
3604 condition_var_non_null
env e
3605 | r, Binop
((Ast.Eqeq
| Ast.EQeqeq
as bop
),
3607 | r, Binop
((Ast.Eqeq
| Ast.EQeqeq
as bop
),
3608 e
, (_, Null
)) when not tparamet
->
3609 let env, x_ty
= expr env e
in
3610 let env, x_ty
= Env.expand_type
env x_ty
in
3612 if bop
== Ast.Eqeq
then check_null_wtf
env r x_ty
else env in
3613 condition_var_non_null
env e
3614 | (p, (Lvar
_ | Obj_get
_ | Class_get
_) as e
) ->
3615 let env, ty = expr env e
in
3616 let env, ety
= Env.expand_type
env ty in
3618 | _, Tarray
(None
, None
)
3619 | _, Tprim Tbool
-> env
3620 | _, (Tany
| Tmixed
| Tarray
(_, _) | Toption
_
3621 | Tprim
_ | Tvar
_ | Tfun
_ | Tabstract
(_, _) | Tclass
(_, _)
3622 | Ttuple
_ | Tanon
(_, _) | Tunresolved
_ | Tobject
| Tshape
_
3624 condition
env (not tparamet
) (p, Binop
(Ast.Eqeq
, e
, (p, Null
))))
3625 | r, Binop
(Ast.Eq None
, var
, e
) when tparamet
->
3626 let env, e_ty
= expr env e
in
3627 let env, e_ty
= Env.expand_type
env e_ty
in
3628 let env = check_null_wtf
env r e_ty
in
3629 condition_var_non_null
env var
3630 | p1, Binop
(Ast.Eq None
, (_, (Lvar
_ | Obj_get
_) as lv
), (p2, _)) ->
3631 let env, _ = expr env (p1, Binop
(Ast.Eq None
, lv
, (p2, Null
))) in
3632 condition
env tparamet lv
3633 | p, Binop
((Ast.Diff
| Ast.Diff2
as op
), e1
, e2) ->
3634 let op = if op = Ast.Diff
then Ast.Eqeq
else Ast.EQeqeq
in
3635 condition
env (not tparamet
) (p, Binop
(op, e1
, e2))
3636 | _, Binop
(Ast.AMpamp
, e1
, e2) when tparamet
->
3637 let env = condition
env true e1
in
3638 let env = condition
env true e2 in
3640 | _, Binop
(Ast.BArbar
, e1
, e2) when not tparamet
->
3641 let env = condition
env false e1
in
3642 let env = condition
env false e2 in
3644 | _, Call
(Cnormal
, (_, Id
(_, f)), [lv
], [])
3645 when tparamet
&& f = SN.StdlibFunctions.is_array
->
3647 | _, Call
(Cnormal
, (_, Id
(_, f)), [lv
], [])
3648 when tparamet
&& f = SN.StdlibFunctions.is_int
->
3650 | _, Call
(Cnormal
, (_, Id
(_, f)), [lv
], [])
3651 when tparamet
&& f = SN.StdlibFunctions.is_bool
->
3652 is_type
env lv Tbool
3653 | _, Call
(Cnormal
, (_, Id
(_, f)), [lv
], [])
3654 when tparamet
&& f = SN.StdlibFunctions.is_float
->
3655 is_type
env lv Tfloat
3656 | _, Call
(Cnormal
, (_, Id
(_, f)), [lv
], [])
3657 when tparamet
&& f = SN.StdlibFunctions.is_string
->
3658 is_type
env lv Tstring
3659 | _, Call
(Cnormal
, (_, Id
(_, f)), [lv
], [])
3660 when tparamet
&& f = SN.StdlibFunctions.is_resource
->
3661 is_type
env lv Tresource
3662 | _, Unop
(Ast.Unot
, e
) ->
3663 condition
env (not tparamet
) e
3664 | p, InstanceOf
(ivar
, cid) when tparamet
&& is_instance_var ivar
->
3665 let env, (ivar_pos
, x) = get_instance_var
env ivar
in
3666 let env, x_ty
= Env.get_local
env x in
3667 let env, x_ty
= Env.expand_type
env x_ty
in (* We don't want to modify x *)
3668 (* XXX the position p here is not really correct... it's the position
3669 * of the instanceof expression, not the class id. But we don't store
3670 * position data for the latter. *)
3671 let env, obj_ty = static_class_id
p env cid in
3673 | _, Tabstract
(AKgeneric
_, _) ->
3674 Env.set_local
env x obj_ty
3675 | _, Tabstract
(AKdependent
(`this
, []), Some
(_, Tclass
_)) ->
3677 (* Technically instanceof static is not strong enough to prove
3678 * that a type is exactly the same as the late bound type.
3679 * For now we allow this lie to exist. To solve
3680 * this we either need to create a new type that means
3681 * subtype of static or provide a way of specifying exactly
3682 * the late bound type i.e. $x::class === static::class
3684 if cid = CIstatic
then
3685 ExprDepTy.make
env CIstatic
obj_ty
3688 let env = Env.set_local
env x obj_ty in
3690 | _, Tclass
((_, cid as _c
), _) ->
3691 let class_ = Env.get_class
env cid in
3693 | None
-> Env.set_local
env x (Reason.Rwitness ivar_pos
, Tobject
)
3695 if SubType.is_sub_type
env obj_ty x_ty
3697 (* If the right side of the `instanceof` object is
3698 * a super type of what we already knew. In this case,
3699 * since we already have a more specialized object, we
3700 * don't touch the original object. Check out the unit
3701 * test srecko.php if this is unclear.
3703 * Note that if x_ty is Tany, no amount of subtype
3704 * checking will be able to specify it
3705 * further. This is arguably desirable to maintain
3706 * the invariant that removing annotations gets rid
3707 * of typing errors in partial mode (See also
3710 else Env.set_local
env x obj_ty
3712 | _, (Tany
| Tmixed
| Tarray
(_, _) | Toption
_
3713 | Tprim
_ | Tvar
_ | Tfun
_ | Tabstract
(_, _) | Ttuple
_
3714 | Tanon
(_, _) | Tunresolved
_ | Tobject
3715 | Tshape
_) -> Env.set_local
env x (Reason.Rwitness ivar_pos
, Tobject
)
3717 | _, Binop
((Ast.Eqeq
| Ast.EQeqeq
), e
, (_, Null
))
3718 | _, Binop
((Ast.Eqeq
| Ast.EQeqeq
), (_, Null
), e
) ->
3719 let env, _ = expr env e
in
3722 let env, _ = expr env e
in
3725 and is_instance_var
= function
3726 | _, (Lvar
_ | This
) -> true
3727 | _, Obj_get
((_, This
), (_, Id
_), _) -> true
3728 | _, Obj_get
((_, Lvar
_), (_, Id
_), _) -> true
3729 | _, Class_get
(_, _) -> true
3732 and get_instance_var
env = function
3733 | p, Class_get
(cname
, (_, member_name
)) ->
3734 let env, local = Env.FakeMembers.make_static
p env cname member_name
in
3736 | p, Obj_get
((_, This
| _, Lvar
_ as obj
), (_, Id
(_, member_name
)), _) ->
3737 let env, local = Env.FakeMembers.make
p env obj member_name
in
3739 | _, Lvar
(p, x) -> env, (p, x)
3740 | p, This
-> env, (p, this
)
3741 | _ -> failwith
"Should only be called when is_instance_var is true"
3743 and check_null_wtf
env p ty =
3744 if not
(Env.is_strict
env) then env else
3745 let env, ty = TUtils.fold_unresolved
env ty in
3746 let env, ety
= Env.expand_type
env ty in
3749 let env, ty = Env.expand_type
env ty in
3753 Errors.sketchy_null_check
p
3755 Errors.sketchy_null_check_primitive
p
3756 | _, (Tarray
(_, _) | Toption
_ | Tvar
_ | Tfun
_
3757 | Tabstract
(_, _) | Tclass
(_, _) | Ttuple
_ | Tanon
(_, _)
3758 | Tunresolved
_ | Tobject
| Tshape
_ ) -> ());
3760 | _, (Tany
| Tmixed
| Tarray
(_, _) | Tprim
_ | Tvar
_
3761 | Tfun
_ | Tabstract
(_, _) | Tclass
(_, _) | Ttuple
_ | Tanon
(_, _)
3762 | Tunresolved
_ | Tobject
| Tshape
_ ) -> env
3764 and is_type
env e tprim
=
3766 | p, Class_get
(cname
, (_, member_name
)) ->
3767 let env, local = Env.FakeMembers.make_static
p env cname member_name
in
3768 Env.set_local
env local (Reason.Rwitness
p, Tprim tprim
)
3769 | p, Obj_get
((_, This
| _, Lvar
_ as obj
), (_, Id
(_, member_name
)), _) ->
3770 let env, local = Env.FakeMembers.make
p env obj member_name
in
3771 Env.set_local
env local (Reason.Rwitness
p, Tprim tprim
)
3773 Env.set_local
env x (Reason.Rwitness
p, Tprim tprim
)
3776 and is_array
env = function
3777 | p, Class_get
(cname
, (_, member_name
)) ->
3778 let env, local = Env.FakeMembers.make_static
p env cname member_name
in
3779 Env.set_local
env local (Reason.Rwitness
p, Tarray
(None
, None
))
3780 | p, Obj_get
((_, This
| _, Lvar
_ as obj
), (_, Id
(_, member_name
)), _) ->
3781 let env, local = Env.FakeMembers.make
p env obj member_name
in
3782 Env.set_local
env local (Reason.Rwitness
p, Tarray
(None
, None
))
3784 Env.set_local
env x (Reason.Rwitness
p, Tarray
(None
, None
))
3787 and string2
env idl
=
3788 List.fold_left idl ~init
:env ~
f:begin fun env x ->
3789 let env, ty = expr env x in
3791 let env = SubType.sub_string
p env ty in
3795 (* If the current class inherits from classes that take type arguments, we need
3796 * to check that the arguments provided are consistent with the constraints on
3797 * the type parameters. *)
3798 and check_implements_tparaml
(env: Env.env) ht
=
3799 let _r, (p, c), paraml = Typing_hint.open_class_hint ht
in
3800 let class_ = Env.get_class_dep
env c in
3803 (* The class lives in PHP land *)
3806 let size1 = List.length
class_.tc_tparams
in
3807 let size2 = List.length
paraml in
3808 if size1 <> size2 then Errors.class_arity
p class_.tc_pos
c size1;
3809 let subst = Inst.make_subst
class_.tc_tparams
paraml in
3810 iter2_shortest
begin fun (_, (p, _), cstr_opt
) ty ->
3813 | Some
(Ast.Constraint_as
, cstr) ->
3814 let cstr = snd
(Inst.instantiate
subst env cstr) in
3815 ignore
(Type.sub_type_decl
p Reason.URnone
env cstr ty);
3816 | Some
(Ast.Constraint_super
, cstr) ->
3817 let cstr = snd
(Inst.instantiate
subst env cstr) in
3818 ignore
(Type.sub_type_decl
p Reason.URnone
env ty cstr);
3819 end class_.tc_tparams
paraml
3821 (* In order to type-check a class, we need to know what "parent"
3822 * refers to. Sometimes people write "parent::", when that happens,
3823 * we need to know the type of parent.
3825 and class_def_parent
env class_def class_type
=
3826 match class_def
.c_extends
with
3827 | (_, Happly
((_, x), _) as parent_ty
) :: _ ->
3828 let parent_type = Env.get_class_dep
env x in
3829 (match parent_type with
3830 | Some
parent_type -> check_parent class_def class_type
parent_type
3832 let env, parent_ty
= Typing_hint.hint
env parent_ty
in
3833 env, Some
x, parent_ty
3834 (* The only case where we have more than one parent class is when
3835 * dealing with interfaces and interfaces cannot use parent.
3838 | _ -> env, None
, (Reason.Rnone
, Tany
)
3840 and check_parent class_def class_type
parent_type =
3841 let position = fst class_def
.c_name
in
3842 (* Are all the parents in Hack? Do we know all their methods?
3843 * If so, let's check that the abstract methods have been implemented.
3845 if class_type
.tc_members_fully_known
3846 then check_parent_abstract
position parent_type class_type
;
3847 if parent_type.tc_final
3848 then Errors.extend_final
position parent_type.tc_pos
parent_type.tc_name
3851 and check_parent_abstract
position parent_type class_type
=
3852 let is_final = class_type
.tc_final
in
3853 if parent_type.tc_kind
= Ast.Cabstract
&&
3854 (class_type
.tc_kind
<> Ast.Cabstract
|| is_final)
3856 check_extend_abstract_meth ~
is_final position class_type
.tc_methods
;
3857 check_extend_abstract_meth ~
is_final position class_type
.tc_smethods
;
3858 check_extend_abstract_const ~
is_final position class_type
.tc_consts
;
3859 check_extend_abstract_typeconst
3860 ~
is_final position class_type
.tc_typeconsts
;
3863 and class_def env_up nenv
_ c =
3864 let c = Naming.class_meth_bodies nenv
c in
3865 if not
!auto_complete
then begin
3866 NastCheck.class_ env_up
c;
3867 NastInitCheck.class_ env_up
c;
3869 let env_tmp = Env.set_root env_up
(Dep.Class
(snd
c.c_name
)) in
3870 let tc = Env.get_class
env_tmp (snd
c.c_name
) in
3873 (* This can happen if there was an error during the declaration
3876 | Some
tc -> class_def_ env_up
c tc
3878 and get_self_from_c
env c =
3879 let _, tparams
= lfold
type_param env (fst
c.c_tparams
) in
3880 let tparams = List.map
tparams begin fun (_, (p, s
), param) ->
3881 Reason.Rwitness
p, Tgeneric
(s
, param)
3883 let ret = Reason.Rwitness
(fst
c.c_name
), Tapply
(c.c_name
, tparams) in
3886 and class_def_ env_up
c tc =
3887 Typing_hooks.dispatch_enter_class_def_hook
c tc;
3888 let env = Env.set_self_id env_up
(snd
c.c_name
) in
3889 let env = Env.set_mode
env c.c_mode
in
3890 let env = Env.set_root
env (Dep.Class
(snd
c.c_name
)) in
3891 let pc, _ = c.c_name
in
3893 lmap
Typing_hint.hint
env (c.c_extends
@ c.c_implements
@ c.c_uses
) in
3894 let env = Typing_hint.check_tparams_instantiable
env (fst
c.c_tparams
) in
3895 Typing_variance.class_ (snd
c.c_name
) tc impl
;
3896 let self = get_self_from_c
env c in
3897 List.iter impl
(check_implements_tparaml
env);
3898 let env, parent_id
, parent = class_def_parent
env c tc in
3899 let is_final = tc.tc_final
in
3900 if (tc.tc_kind
= Ast.Cnormal
|| is_final) && tc.tc_members_fully_known
3902 check_extend_abstract_meth ~
is_final pc tc.tc_methods
;
3903 check_extend_abstract_meth ~
is_final pc tc.tc_smethods
;
3904 check_extend_abstract_const ~
is_final pc tc.tc_consts
;
3905 check_extend_abstract_typeconst ~
is_final pc tc.tc_typeconsts
;
3907 (* For enums, localize makes self:: into an abstract type, which we don't
3909 let env, self = match c.c_kind
with
3910 | Ast.Cenum
-> env, (fst
self, Tclass
(c.c_name
, []))
3911 | Ast.Cinterface
| Ast.Cabstract
| Ast.Ctrait
3912 | Ast.Cnormal
-> Phase.localize_with_self
env self in
3913 let env = Env.set_self
env self in
3914 let env = Env.set_parent
env parent in
3915 let env = match parent_id
with
3917 | Some parent_id
-> Env.set_parent_id
env parent_id
in
3918 if tc.tc_final
then begin
3920 | Ast.Cinterface
-> Errors.interface_final
(fst
c.c_name
)
3921 | Ast.Cabstract
-> ()
3922 | Ast.Ctrait
-> Errors.trait_final
(fst
c.c_name
)
3923 | Ast.Cenum
-> (* the parser won't let enums be final *) assert false
3926 List.iter impl
(class_implements_type
env c);
3927 List.iter
c.c_vars
(class_var_def
env false c);
3928 List.iter
c.c_methods
(method_def
env);
3929 List.iter
c.c_typeconsts
(typeconst_def
env);
3930 let const_types = List.map
c.c_consts
(class_const_def
env) in
3931 let env = Typing_enum.enum_class_check
env tc c.c_consts
const_types in
3932 class_constr_def
env c;
3933 let env = Env.set_static
env in
3934 List.iter
c.c_static_vars
(class_var_def
env true c);
3935 List.iter
c.c_static_methods
(method_def
env);
3936 Typing_hooks.dispatch_exit_class_def_hook
c tc
3938 and check_extend_abstract_meth ~
is_final p smap
=
3939 SMap.iter
begin fun x ce
->
3940 match ce
.ce_type
with
3941 | r, Tfun
{ ft_abstract
= true; _ } ->
3942 Errors.implement_abstract ~
is_final p (Reason.to_pos
r) "method" x
3943 | _, (Tany
| Tmixed
| Tarray
(_, _) | Tgeneric
(_,_) | Toption
_ | Tprim
_
3944 | Tfun
_ | Tapply
(_, _) | Ttuple
_ | Tshape
_ | Taccess
(_, _)
3949 (* Type constants must be bound to a concrete type for non-abstract classes.
3951 and check_extend_abstract_typeconst ~
is_final p smap
=
3952 SMap.iter
begin fun x tc ->
3953 if tc.ttc_type
= None
then
3954 Errors.implement_abstract ~
is_final p (fst
tc.ttc_name
) "type constant" x
3957 and check_extend_abstract_const ~
is_final p smap
=
3958 SMap.iter
begin fun x ce
->
3959 match ce
.ce_type
with
3960 | r, Tgeneric
_ when not ce
.ce_synthesized
->
3961 Errors.implement_abstract ~
is_final p (Reason.to_pos
r) "constant" x
3962 | _, (Tany
| Tmixed
| Tarray
(_, _) | Toption
_ | Tprim
_ | Tfun
_
3963 | Tapply
(_, _) | Ttuple
_ | Tshape
_ | Taccess
(_, _) | Tthis
3967 and typeconst_def
env {
3968 c_tconst_name
= (pos, _);
3969 c_tconst_constraint
;
3972 let env, cstr = opt
Typing_hint.hint_locl
env c_tconst_constraint
in
3973 let env, ty = opt
Typing_hint.hint_locl
env c_tconst_type
in
3975 Option.map2
cstr ty ~
f:(Type.sub_type
pos Reason.URtypeconst_cstr
env)
3978 and class_const_def
env (h
, id
, e
) =
3981 | None
-> env, Env.fresh_type
()
3983 Typing_hint.hint_locl
env h
3987 let env, ty'
= expr env e
in
3988 ignore
(Type.sub_type
(fst id
) Reason.URhint
env ty ty'
);
3992 and class_constr_def
env c =
3993 match c.c_constructor
with
3998 and class_implements_type
env c1 ctype2
=
3999 let env, params = lfold
begin fun env (_, (p, s
), param) ->
4000 let env, param = match param with
4002 let env, ty = Typing_hint.hint
env h
in
4004 | None
-> env, None
in
4005 env, (Reason.Rwitness
p, Tgeneric
(s
, param))
4006 end env (fst c1
.c_tparams
)
4008 let r = Reason.Rwitness
(fst c1
.c_name
) in
4009 let ctype1 = r, Tapply
(c1
.c_name
, params) in
4010 Typing_extends.check_implements
env ctype2
ctype1;
4013 and class_var_def
env is_static
c cv
=
4015 match cv
.cv_expr
with
4016 | None
-> env, Env.fresh_type
()
4017 | Some e
-> expr env e
in
4018 match cv
.cv_type
with
4019 | None
when Env.is_strict
env ->
4020 Errors.add_a_typehint
(fst cv
.cv_id
)
4022 let pos, name = cv
.cv_id
in
4023 let name = if is_static
then "$"^
name else name in
4024 let var_type = Reason.Rwitness
pos, Tany
in
4025 (match cv
.cv_expr
with
4027 Typing_suggest.uninitialized_member
(snd
c.c_name
) name env var_type ty;
4030 Typing_suggest.save_member
name env var_type ty;
4033 | Some
(p, _ as cty
) ->
4035 (* If this is an XHP attribute and we're in strict mode,
4036 relax to partial mode to allow the use of the "array"
4037 annotation without specifying type parameters. Until
4038 recently HHVM did not allow "array" with type parameters
4039 in XHP attribute declarations, so this is a temporary
4040 hack to support existing code for now. *)
4041 (* Task #5815945: Get rid of this Hack *)
4042 if cv
.cv_is_xhp
&& (Env.is_strict
env)
4043 then Env.set_mode
env FileInfo.Mpartial
4045 let env, cty
= Typing_hint.hint_locl ~ensure_instantiable
:true env cty
in
4046 let _ = Type.sub_type
p Reason.URhint
env cty
ty in
4049 and method_def
env m
=
4050 (* reset the expression dependent display ids for each method body *)
4051 Reason.expr_display_id_map
:= IMap.empty
;
4052 Typing_hooks.dispatch_enter_method_def_hook m
;
4053 let env = { env with Env.lenv = Env.empty_local
} in
4054 let env = Env.set_local
env this
(Env.get_self
env) in
4055 let env, ret = match m
.m_ret
with
4056 | None
-> env, (Reason.Rwitness
(fst m
.m_name
), Tany
)
4058 let env, ret = Typing_hint.hint ~ensure_instantiable
:true env ret in
4059 (* If a 'this' type appears it needs to be compatiable with the
4063 { (Phase.env_with_self
env) with
4064 from_class
= Some CIstatic
} in
4065 Phase.localize ~
ety_env env ret in
4066 let m_params = match m
.m_variadic
with
4067 | FVvariadicArg
param -> param :: m
.m_params
4070 let env = Typing_hint.check_params_instantiable
env m_params in
4071 let env = Typing_hint.check_tparams_instantiable
env m
.m_tparams
in
4073 lfold
make_param_local_ty env m_params in
4074 if Env.is_strict
env then begin
4075 List.iter2_exn ~
f:(check_param
env) m_params params;
4077 if Attributes.mem
SN.UserAttributes.uaMemoize m
.m_user_attributes
then
4078 List.iter2_exn ~
f:(check_memoizable
env) m_params params;
4079 let env = List.fold2_exn ~
f:bind_param ~init
:env params m_params in
4080 let nb = Nast.assert_named_body m
.m_body
in
4081 let env = fun_ ~abstract
:m
.m_abstract
env ret (fst m
.m_name
) nb m
.m_fun_kind
in
4083 List.fold_left
(Env.get_todo
env) ~
f:(fun env f -> f env) ~init
:env in
4085 | None
when Env.is_strict
env && snd m
.m_name
<> SN.Members.__destruct
->
4086 (* if we are in strict mode, the only case where we don't want to enforce
4087 * a return type is when the method is a destructor
4089 suggest_return env (fst m
.m_name
) ret
4092 Typing_hooks.dispatch_exit_method_def_hook m
4094 and typedef_def env_up typedef
=
4095 NastCheck.typedef env_up typedef
;
4098 t_constraint
= tcstr
;
4100 t_user_attributes
= _;
4102 ignore
(Typing_hint.hint_locl ~ensure_instantiable
:true env_up hint
);
4103 ignore
(Option.map tcstr
(Typing_hint.check_instantiable env_up
));
4105 | pos, Hshape fdm
->
4106 ignore
(check_shape_keys_validity env_up
pos (ShapeMap.keys fdm
))
4109 (* Calls the method of a class, but allows the f callback to override the
4110 * return value type *)
4111 and overload_function
p env class_id method_id
el uel
f =
4112 let env, ty = static_class_id
p env class_id
in
4113 let env, fty = class_get ~
is_method:true ~is_const
:false env ty
4114 method_id class_id
in
4115 (* call the function as declared to validate arity and input types,
4116 but ignore the result and overwrite with custom one *)
4117 let (env, res
), has_error
= Errors.try_with_error
4118 (fun () -> call
p env fty el uel
, false)
4119 (fun () -> (env, (Reason.Rwitness
p, Tany
)), true) in
4120 (* if there are errors already stop here - going forward would
4121 * report them twice *)
4122 if has_error
then env, res
4123 else f env fty res
el