2 * Copyright (c) 2015, Facebook, Inc.
5 * This source code is licensed under the BSD-style license found in the
6 * LICENSE file in the "hack" directory of this source tree. An additional grant
7 * of patent rights can be found in the PATENTS file in the same directory.
11 (* This module implements the typing.
13 * Given an Nast.program, it infers the type of all the local
14 * variables, and checks that all the types are correct (aka
23 module TUtils
= Typing_utils
24 module Reason
= Typing_reason
25 module Inst
= Decl_instantiate
26 module Type
= Typing_ops
27 module Env
= Typing_env
28 module LEnv
= Typing_lenv
29 module Dep
= Typing_deps.Dep
30 module Async
= Typing_async
31 module SubType
= Typing_subtype
32 module Unify
= Typing_unify
33 module TGen
= Typing_generic
34 module SN
= Naming_special_names
35 module TAccess
= Typing_taccess
36 module TI
= Typing_instantiability
37 module TVis
= Typing_visibility
38 module TNBody
= Typing_naming_body
39 module TS
= Typing_structure
41 module Phase
= Typing_phase
42 module Subst
= Decl_subst
43 module ExprDepTy
= Typing_dependent_type.ExprDepTy
44 module Conts
= Typing_continuations
46 (*****************************************************************************)
48 (*****************************************************************************)
50 (* A guess as to the last position we were typechecking, for use in debugging,
51 * such as figuring out what a runaway hh_server thread is doing. Updated
52 * only best-effort -- it's an approximation to point debugging in the right
53 * direction, nothing more. *)
54 let debug_last_pos = ref Pos.none
55 let debug_print_last_pos _
= print_endline
(Pos.string (Pos.to_absolute
58 (****************************************************************************)
60 (****************************************************************************)
62 let expr_hook = ref None
64 let with_expr_hook hook f
= with_context
65 ~enter
: (fun () -> expr_hook := Some hook
)
66 ~exit
: (fun () -> expr_hook := None
)
69 (*****************************************************************************)
71 (*****************************************************************************)
73 let suggest env p ty
=
74 let ty = Typing_expand.fully_expand env
ty in
75 (match Typing_print.suggest ty with
76 | "..." -> Errors.expecting_type_hint p
77 | ty -> Errors.expecting_type_hint_suggest p
ty
80 let suggest_return env p
ty =
81 let ty = Typing_expand.fully_expand env
ty in
82 (match Typing_print.suggest ty with
83 | "..." -> Errors.expecting_return_type_hint p
84 | ty -> Errors.expecting_return_type_hint_suggest p
ty
87 let err_witness p
= Reason.Rwitness p
, Terr
88 let err_none = Reason.Rnone
, Terr
90 let expr_error env r
=
91 env
, T.make_implicitly_typed_expr
Pos.none
T.Any
, (r
, Terr
)
94 env
, T.make_implicitly_typed_expr
Pos.none
T.Any
, (r
, Tany
)
96 let compare_field_kinds x y
=
98 | Nast.AFvalue
(p1
, _
), Nast.AFkvalue
((p2
, _
), _
)
99 | Nast.AFkvalue
((p2
, _
), _
), Nast.AFvalue
(p1
, _
) ->
100 Errors.field_kinds p1 p2
;
105 let check_consistent_fields x l
=
106 List.for_all l
(compare_field_kinds x
)
108 let unbound_name env
(pos
, name
) =
109 match Env.get_mode env
with
110 | FileInfo.Mstrict
->
111 (Errors.unbound_name_typing pos name
;
112 expr_error env
Reason.Rnone
)
114 | FileInfo.Mdecl
| FileInfo.Mpartial
| FileInfo.Mphp
->
115 expr_any env
Reason.Rnone
117 (* Try running function on each concrete supertype in turn. Return all
120 let try_over_concrete_supertypes env
ty f
=
121 let env, tyl
= TUtils.get_concrete_supertypes
env ty in
122 (* If there is just a single result then don't swallow errors *)
126 let rec iter_over_types env resl tyl
=
131 (fun () -> iter_over_types env (f
env ty::resl
) tyl
)
132 (fun _
-> iter_over_types env resl tyl
) in
133 iter_over_types env [] tyl
135 (*****************************************************************************)
136 (* Handling function/method arguments *)
137 (*****************************************************************************)
139 let rec wfold_left_default f
(env, def1
) l1 l2
=
140 match l1
, def1
, l2
with
143 | [], Some d1
, x2
:: rl2
->
144 let env = f
env d1 x2
in
145 wfold_left_default f
(env, def1
) [] rl2
146 | x1
:: rl1
, _
, x2
:: rl2
->
147 let env = f
env x1 x2
in
148 wfold_left_default f
(env, def1
) rl1 rl2
150 let rec check_memoizable env param
ty =
151 let env, ty = Env.expand_type
env ty in
152 let p = param
.param_pos
in
154 | _
, (Tprim
(Tarraykey
| Tbool
| Tint
| Tfloat
| Tstring
| Tnum
)
155 | Tmixed
| Tany
| Terr
) ->
157 | _
, Tprim
(Tvoid
| Tresource
| Tnoreturn
) ->
158 let ty_str = Typing_print.error
(snd
ty) in
159 let msgl = Reason.to_string
("This is "^
ty_str) (fst
ty) in
160 Errors.invalid_memoized_param
p msgl
162 check_memoizable env param
ty
163 | _
, Tshape
(_
, fdm
) ->
164 ShapeMap.iter
begin fun name _
->
165 match ShapeMap.get name fdm
with
166 | Some
{ sft_ty
; _
} -> check_memoizable env param sft_ty
168 let ty_str = Typing_print.error
(snd
ty) in
169 let msgl = Reason.to_string
("This is "^
ty_str) (fst
ty) in
170 Errors.invalid_memoized_param
p msgl;
173 List.iter tyl
begin fun ty ->
174 check_memoizable env param
ty
176 | _
, Tabstract
(AKenum _
, _
) ->
178 | _
, Tabstract
(AKnewtype
(_
, _
), _
) ->
180 let ety_env = Phase.env_with_self
env in
181 Typing_tdef.force_expand_typedef ~
ety_env env ty in
182 check_memoizable env param t'
183 (* Just accept all generic types for now. Stricter checks to come later. *)
184 | _
, Tabstract
(AKgeneric _
, _
) ->
186 (* For parameter type 'this::TID' defined by 'type const TID as Bar' checks
189 | _
, Tabstract
(AKdependent _
, Some
ty) ->
190 check_memoizable env param
ty
191 (* Allow unconstrined dependent type `abstract type const TID` just as we
192 * allow unconstrained generics. *)
193 | _
, Tabstract
(AKdependent _
, None
) ->
195 (* Handling Tunresolved case here for completeness, even though it
196 * shouldn't be possible to have an unresolved type when checking
197 * the method declaration. No corresponding test case for this.
199 | _
, Tunresolved tyl
->
200 List.iter tyl
begin fun ty ->
201 check_memoizable env param
ty
203 (* Allow untyped arrays. *)
204 | _
, Tarraykind AKany
205 | _
, Tarraykind AKempty
->
207 | _
, Tarraykind
(AKvarray
ty | AKvec
ty | AKdarray
(_
, ty) | AKmap
(_
, ty)) ->
208 check_memoizable env param
ty
209 | _
, Tarraykind
(AKshape fdm
) ->
210 ShapeMap.iter
begin fun _
(_
, tv
) ->
211 check_memoizable env param tv
213 | _
, Tarraykind
(AKtuple fields
) ->
214 IMap.iter
begin fun _ tv
->
215 check_memoizable env param tv
217 | _
, Tclass
(_
, _
) ->
218 let type_param = Env.fresh_type
() in
221 Tclass
((Pos.none
, SN.Collections.cContainer
), [type_param]) in
222 let env, is_container
=
225 SubType.sub_type
env ty container_type, true)
226 (fun _
-> env, false) in
228 check_memoizable env param
type_param
231 let memoizable_type =
232 r, Tclass
((Pos.none
, SN.Classes.cIMemoizeParam
), []) in
233 if SubType.is_sub_type
env ty memoizable_type
236 let ty_str = Typing_print.error
(snd
ty) in
237 let msgl = Reason.to_string
("This is "^
ty_str) (fst
ty) in
238 Errors.invalid_memoized_param
p msgl;
243 let ty_str = Typing_print.error
(snd
ty) in
244 let msgl = Reason.to_string
("This is "^
ty_str) (fst
ty) in
245 Errors.invalid_memoized_param
p msgl
247 (* This function is used to determine the type of an argument.
248 * When we want to type-check the body of a function, we need to
249 * introduce the type of the arguments of the function in the environment
250 * Let's take an example, we want to check the code of foo:
252 * function foo(int $x): int {
253 * // CALL TO make_param_type on (int $x)
254 * // Now we know that the type of $x is int
256 * return $x; // in the environment $x is an int, the code is correct
259 * When we localize, we want to resolve to "static" or "$this" depending on
260 * the context. Even though we are passing in CIstatic, resolve_with_class_id
261 * is smart enough to know what to do. Why do this? Consider the following
264 * abstract const type T;
266 * private this::T $val;
268 * final public function __construct(this::T $x) {
272 * public static function create(this::T $x): this {
273 * return new static($x);
277 * class D extends C { const type T = int; }
279 * In __construct() we want to be able to assign $x to $this->val. The type of
280 * $this->val will expand to '$this::T', so we need $x to also be '$this::T'.
281 * We can do this soundly because when we construct a new class such as,
282 * 'new D(0)' we can determine the late static bound type (D) and resolve
283 * 'this::T' to 'D::T' which is int.
285 * A similar line of reasoning is applied for the static method create.
287 let make_param_local_ty env param
=
289 { (Phase.env_with_self
env) with from_class
= Some CIstatic
; } in
291 match param
.param_hint
with
293 (* if the type is missing, use an unbound type variable *)
294 let _r, ty = Env.fresh_type
() in
295 let r = Reason.Rwitness param
.param_pos
in
298 let ty = Decl_hint.hint
env.Env.decl_env x
in
299 Phase.localize ~
ety_env env ty
301 let ty = match ty with
302 | _
, t
when param
.param_is_variadic
->
303 (* when checking the body of a function with a variadic
304 * argument, "f(C ...$args)", $args is an array<C> *)
305 let r = Reason.Rvar_param param
.param_pos
in
306 let arr_values = r, t
in
307 r, Tarraykind
(AKvec
arr_values)
310 Typing_hooks.dispatch_infer_ty_hook
ty param
.param_pos
env;
313 (* Given a localized parameter type and parameter information, infer
314 * a type for the parameter default expression (if present) and check that
315 * it is a subtype of the parameter type. Set the type of the parameter in
316 * the locals environment *)
317 let rec bind_param env (ty1
, param
) =
318 let env, param_te
, ty2
=
319 match param
.param_expr
with
321 env, None
, (Reason.none
, Tany
)
323 let env, te
, ty = expr
env e
in
324 Typing_sequencing.sequence_check_expr e
;
327 Typing_suggest.save_param
(param
.param_name
) env ty1 ty2
;
328 let env = Type.sub_type param
.param_pos
Reason.URhint
env ty2 ty1
in
330 T.param_hint
= param
.param_hint
;
331 T.param_is_reference
= param
.param_is_reference
;
332 T.param_is_variadic
= param
.param_is_variadic
;
333 T.param_pos
= param
.param_pos
;
334 T.param_name
= param
.param_name
;
335 T.param_expr
= param_te
;
337 Env.set_local
env (Local_id.get param
.param_name
) ty1
, tparam
339 (* In strict mode, we force you to give a type declaration on a parameter *)
340 (* But the type checker is nice: it makes a suggestion :-) *)
341 and check_param
env param
ty =
342 match param
.param_hint
with
343 | None
-> suggest env param
.param_pos
ty
346 (*****************************************************************************)
347 (* Now we are actually checking stuff! *)
348 (*****************************************************************************)
349 and fun_def tcopt f
=
350 (* reset the expression dependent display ids for each function body *)
351 Reason.expr_display_id_map
:= IMap.empty
;
352 Typing_hooks.dispatch_enter_fun_def_hook f
;
353 let nb = TNBody.func_body tcopt f
in
354 let dep = Typing_deps.Dep.Fun
(snd f
.f_name
) in
355 let env = Env.empty tcopt
(Pos.filename
(fst f
.f_name
)) (Some
dep) in
356 NastCheck.fun_
env f
nb;
357 (* Fresh type environment is actually unnecessary, but I prefer to
358 * have a guarantee that we are using a clean typing environment. *)
359 let tfun_def = Env.fresh_tenv
env (
361 let env = Env.set_mode
env f
.f_mode
in
362 let env, constraints
=
363 Phase.localize_generic_parameters_with_bounds
env f
.f_tparams
364 ~
ety_env:(Phase.env_with_self
env) in
365 let env = add_constraints
(fst f
.f_name
) env constraints
in
369 | None
-> env, (Reason.Rwitness
(fst f
.f_name
), Tany
)
371 let ty = TI.instantiable_hint
env ret
in
372 Phase.localize_with_self
env ty
374 (* TODO TAST: convert default expression in param *)
375 let f_params = match f
.f_variadic
with
376 | FVvariadicArg param
-> param
:: f
.f_params
379 TI.check_params_instantiable
env f_params;
380 TI.check_tparams_instantiable
env f
.f_tparams
;
381 let env, param_tys
= List.map_env
env f_params make_param_local_ty in
382 let env, tparams
= List.map_env
env (List.zip_exn param_tys
f_params)
384 let env, tb
= fun_
env hret
(fst f
.f_name
) nb f
.f_fun_kind
in
385 let env = fold_fun_list
env env.Env.todo
in
386 if Env.is_strict
env then begin
387 List.iter2_exn
f_params param_tys
(check_param
env);
389 | None
-> suggest_return env (fst f
.f_name
) hret
396 T.f_tparams
= f
.f_tparams
;
397 T.f_variadic
= T.FVnonVariadic
(* TAST: get this right *);
398 T.f_params = tparams
;
399 T.f_fun_kind
= f
.f_fun_kind
;
400 T.f_user_attributes
= List.map f
.f_user_attributes
(user_attribute
env);
401 T.f_body
= T.NamedBody
{
403 T.fnb_unsafe
= false (* TAST get this right *)
407 Typing_hooks.dispatch_exit_fun_def_hook f
;
410 (*****************************************************************************)
411 (* function used to type closures, functions and methods *)
412 (*****************************************************************************)
414 and fun_ ?
(abstract
=false) env hret pos named_body f_kind
=
415 Env.with_return
env begin fun env ->
416 debug_last_pos := pos
;
417 let env = Env.set_return
env hret
in
418 let env = Env.set_fn_kind
env f_kind
in
419 let env, tb
= block
env named_body
.fnb_nast
in
420 Typing_sequencing.sequence_check_block named_body
.fnb_nast
;
421 let ret = Env.get_return
env in
423 if Nast_terminality.Terminal.block
env named_body
.fnb_nast
||
425 named_body
.fnb_unsafe
||
428 else fun_implicit_return
env pos
ret named_body
.fnb_nast f_kind
in
429 debug_last_pos := Pos.none
;
433 and fun_implicit_return
env pos
ret _b
= function
434 | Ast.FGenerator
| Ast.FAsyncGenerator
-> env
436 (* A function without a terminal block has an implicit return; the
438 let rty = Reason.Rno_return pos
, Tprim
Nast.Tvoid
in
439 Typing_suggest.save_return
env ret rty;
440 Type.sub_type pos
Reason.URreturn
env rty ret
442 (* An async function without a terminal block has an implicit return;
443 * the Awaitable<void> type *)
444 let r = Reason.Rno_return_async pos
in
445 let rty = r, Tclass
((pos
, SN.Classes.cAwaitable
), [r, Tprim
Nast.Tvoid
]) in
446 Typing_suggest.save_return
env ret rty;
447 Type.sub_type pos
Reason.URreturn
env rty ret
450 List.map_env
env stl stmt
452 and stmt
env = function
458 let env, te
, ty = expr
env e
in
459 (* NB: this check does belong here and not in expr, even though it only
460 * applies to expressions -- we actually want to perform the check on
461 * statements that are expressions, e.g., "foo();" we want to check, but
462 * "return foo();" we do not even though the expression "foo()" is a
463 * subexpression of the statement "return foo();". *)
465 | Nast.Binop
(Ast.Eq _
, _
, _
) -> ()
466 | _
-> Async.enforce_not_awaitable
env (fst e
) ty);
469 let env, te
, _
= expr
env e
in
470 (* We stash away the locals environment because condition updates it
471 * locally for checking b1. For example, we might have condition
472 * $x === null, or $x instanceof C, which changes the type of $x in
474 let parent_lenv = env.Env.lenv
in
475 let env = condition
env true e
in
476 let env, tb2
= block
env b1
in
477 let lenv1 = env.Env.lenv
in
478 let env = { env with Env.lenv
= parent_lenv } in
479 let env = condition
env false e
in
480 let env, tb1
= block
env b2
in
481 let lenv2 = env.Env.lenv
in
482 let terminal1 = Nast_terminality.Terminal.block
env b1
in
483 let terminal2 = Nast_terminality.Terminal.block
env b2
in
485 if terminal1 && terminal2
487 let env = LEnv.integrate
env parent_lenv lenv1 in
488 let env = LEnv.integrate
env env.Env.lenv
lenv2 in
489 LEnv.integrate
env env.Env.lenv
parent_lenv
492 let env = LEnv.integrate
env parent_lenv lenv1 in
493 LEnv.integrate
env env.Env.lenv
lenv2
497 let env = LEnv.integrate
env parent_lenv lenv2 in
498 LEnv.integrate
env env.Env.lenv
lenv1
500 else LEnv.intersect
env parent_lenv lenv1 lenv2 in
501 (* TODO TAST: annotate with joined types *)
502 env, T.If
(te
, tb1
, tb2
)
503 | Return
(p, None
) ->
504 let rty = match Env.get_fn_kind
env with
505 | Ast.FSync
-> (Reason.Rwitness
p, Tprim Tvoid
)
507 (* Return type checked against the "yield". *)
508 | Ast.FAsyncGenerator
-> (Reason.Rnone
, Tany
)
509 | Ast.FAsync
-> (Reason.Rwitness
p,
510 Tclass
((p, SN.Classes.cAwaitable
),
511 [(Reason.Rwitness
p, Tprim Tvoid
)])) in
512 let expected_return = Env.get_return
env in
513 Typing_suggest.save_return
env expected_return rty;
514 let env = Type.sub_type
p Reason.URreturn
env rty expected_return in
515 env, T.Return
(p, None
)
516 | Return
(p, Some e
) ->
518 let env, te
, rty = expr
env e
in
519 let rty = match Env.get_fn_kind
env with
522 (* Is an error, but caught in NastCheck. *)
523 | Ast.FAsyncGenerator
-> (Reason.Rnone
, Terr
)
525 (Reason.Rwitness
p), Tclass
((p, SN.Classes.cAwaitable
), [rty]) in
526 let expected_return = Env.get_return
env in
527 (match snd
(Env.expand_type
env expected_return) with
529 (* Yell about returning a value from a void function. This catches
530 * more issues than just unifying with void would do -- in particular
531 * just unifying allows you to return a Tany from a void function,
532 * which is clearly wrong. Note this check is best-effort; if the
533 * function returns a generic type which later ends up being Tvoid
534 * then there's not much we can do here. *)
535 Errors.return_in_void
p (Reason.to_pos
r);
536 env, T.Return
(p, Some te
)
537 | _
, Tunresolved _
->
538 (* we allow return types to grow for anonymous functions *)
539 let env, rty = TUtils.unresolved
env rty in
540 let env = Type.sub_type
pos Reason.URreturn
env rty expected_return in
541 env, T.Return
(p, Some te
)
542 | _
, (Terr
| Tany
| Tmixed
| Tarraykind _
| Toption _
| Tprim _
543 | Tvar _
| Tfun _
| Tabstract
(_
, _
) | Tclass
(_
, _
) | Ttuple _
544 | Tanon
(_
, _
) | Tobject
| Tshape _
) ->
545 Typing_suggest.save_return
env expected_return rty;
546 let env = Type.sub_type
pos Reason.URreturn
env rty expected_return in
547 env, T.Return
(p, Some te
)
550 (* NOTE: leaks scope as currently implemented; this matches
551 the behavior in naming (cf. `do_stmt` in naming/naming.ml).
553 let parent_lenv = env.Env.lenv
in
554 let env = Env.freeze_local_env
env in
555 let env, _
= block
env b
in
556 let env, te
, _
= expr
env e
in
557 let after_block = env.Env.lenv
in
559 if env.Env.in_loop
then 1 else Typing_alias.get_depth st
in
560 let env, tb
= Env.in_loop
env begin
561 iter_n_acc
alias_depth begin fun env ->
562 let env = condition
env true e
in
563 let env, tb
= block
env b
in
567 if Nast.Visitor.HasContinue.block b
568 then LEnv.fully_integrate
env parent_lenv
570 let env = LEnv.integrate
env parent_lenv env.Env.lenv
in
571 let env = { env with Env.lenv
= after_block } in
573 let env = condition
env false e
in
575 | While
(e
, b
) as st
->
576 let env, te
, _
= expr
env e
in
577 let parent_lenv = env.Env.lenv
in
578 let env = Env.freeze_local_env
env in
580 if env.Env.in_loop
then 1 else Typing_alias.get_depth st
in
581 let env, tb
= Env.in_loop
env begin
582 iter_n_acc
alias_depth begin fun env ->
583 let env = condition
env true e
in
584 (* TODO TAST: avoid repeated generation of block *)
585 let env, tb
= block
env b
in
589 let env = LEnv.fully_integrate
env parent_lenv in
590 let env = condition
env false e
in
591 env, T.While
(te
, tb
)
592 | For
(e1
, e2
, e3
, b
) as st
->
593 (* For loops leak their initalizer, but nothing that's defined in the
596 let (env, te1
, _
) = expr
env e1
in (* initializer *)
597 let (env, te2
, _
) = expr
env e2
in
598 let parent_lenv = env.Env.lenv
in
599 let env = Env.freeze_local_env
env in
601 if env.Env.in_loop
then 1 else Typing_alias.get_depth st
in
602 let env, (tb
, te3
) = Env.in_loop
env begin
603 iter_n_acc
alias_depth begin fun env ->
604 let env = condition
env true e2
in (* iteration 0 *)
605 let env, tb
= block
env b
in
606 let (env, te3
, _
) = expr
env e3
in
610 let env = LEnv.fully_integrate
env parent_lenv in
611 let env = condition
env false e2
in
612 env, T.For
(te1
, te2
, te3
, tb
)
614 let cl = List.map ~f
:drop_dead_code_after_break
cl in
615 Nast_terminality.SafeCase.check
(fst e
) env cl;
616 let env, te
, ty = expr
env e
in
617 Async.enforce_not_awaitable
env (fst e
) ty;
618 let env = check_exhaustiveness
env (fst e
) ty cl in
619 let parent_lenv = env.Env.lenv
in
620 let env, cl, tcl
= case_list
parent_lenv ty env cl in
621 let env = LEnv.intersect_list
env parent_lenv cl in
622 env, T.Switch
(te
, tcl
)
623 | Foreach
(e1
, e2
, b
) as st
->
624 let env, te1
, ty1
= expr
env e1
in
625 let parent_lenv = env.Env.lenv
in
626 let env = Env.freeze_local_env
env in
627 let env, ty2
= as_expr
env (fst e1
) e2
in
628 let env = Type.sub_type
(fst e1
) Reason.URforeach
env ty1 ty2
in
630 if env.Env.in_loop
then 1 else Typing_alias.get_depth st
in
631 let env, (te2
, tb
) = Env.in_loop
env begin
632 iter_n_acc
alias_depth begin fun env ->
633 let env, te2
= bind_as_expr
env ty2 e2
in
634 let env, tb
= block
env b
in
638 let env = LEnv.fully_integrate
env parent_lenv in
639 env, T.Foreach
(te1
, te2
, tb
)
640 | Try
(tb
, cl, fb
) ->
641 let env, ttb
, tcl
= try_catch
env tb
cl in
642 let env, tfb
= block
env fb
in
643 env, T.Try
(ttb
, tcl
, tfb
)
645 let env = List.fold_left el ~f
:begin fun env e
->
647 | _
, Binop
(Ast.Eq _
, (_
, Lvar
(p, x
)), _
) ->
648 Env.add_todo
env (TGen.no_generic
p x
)
651 let env, tel
, _
= exprs
env el
in
652 env, T.Static_var tel
653 | Throw
(is_terminal
, e
) ->
655 let env, te
, ty = expr
env e
in
656 let env = exception_ty
p env ty in
657 env, T.Throw
(is_terminal
, te
)
663 and check_exhaustiveness
env pos ty caselist
=
664 check_exhaustiveness_
env pos ty caselist
false
666 and check_exhaustiveness_
env pos ty caselist enum_coming_from_unresolved
=
667 (* Right now we only do exhaustiveness checking for enums. *)
668 (* This function has a built in hack where if Tunresolved has an enum
669 inside then it tells the enum exhaustiveness checker to
670 not punish for extra default *)
671 let env, (_
, ty) = Env.expand_type
env ty in
674 let new_enum = enum_coming_from_unresolved
||
675 (List.length tyl
> 1 && List.exists tyl ~f
:begin fun cur_ty
->
676 let _, (_, cur_ty
) = Env.expand_type
env cur_ty
in
678 | Tabstract
(AKenum
_, _) -> true
681 List.fold_left tyl ~init
:env ~f
:begin fun env ty ->
682 check_exhaustiveness_
env pos ty caselist
new_enum
684 | Tabstract
(AKenum id
, _) ->
685 let tc = unsafe_opt
@@ Env.get_enum
env id
in
686 Typing_enum.check_enum_exhaustiveness
pos tc
687 caselist enum_coming_from_unresolved
;
689 | Terr
| Tany
| Tmixed
| Tarraykind
_ | Tclass
_ | Toption
_ | Tprim
_
690 | Tvar
_ | Tfun
_ | Tabstract
(_, _) | Ttuple
_ | Tanon
(_, _)
691 | Tobject
| Tshape
_ -> env
693 and case_list
parent_lenv ty env cl =
694 let env = { env with Env.lenv
= parent_lenv } in
695 case_list_
parent_lenv ty env cl
697 and try_catch
env tb
cl =
698 let parent_lenv = env.Env.lenv
in
699 let env = Env.freeze_local_env
env in
700 let env, ttb
= block
env tb
in
701 let after_try = env.Env.lenv
in
702 let env, term_lenv_l
= List.map_env
env cl
703 begin fun env (_, _, b
as catch_block
) ->
704 let env, lenv
= catch
parent_lenv after_try env catch_block
in
705 let term = Nast_terminality.Terminal.block
env b
in
709 (Nast_terminality.Terminal.block
env tb
, after_try) :: term_lenv_l in
710 let env = LEnv.intersect_list
env parent_lenv term_lenv_l in
711 env, ttb
, [] (* TODO TAST tcl *)
713 and drop_dead_code_after_break_block
= function
715 | Break x
:: _ -> [Break x
], true
719 | If
(_, [], []) as if_stmt
-> if_stmt
, false
720 | If
(cond
, b1
, b2
) ->
721 let b1, drop1
= if b1 = [] then [], true
722 else drop_dead_code_after_break_block
b1 in
723 let b2, drop2
= if b2 = [] then [], true
724 else drop_dead_code_after_break_block
b2 in
725 If
(cond
, b1, b2), drop1
&& drop2
728 if drop
then ([x'
], true) else begin
729 let rest'
, drop
= drop_dead_code_after_break_block
rest in
733 and drop_dead_code_after_break
= function
734 | Default b
-> Default
(fst
(drop_dead_code_after_break_block b
))
735 | Case
(e
, b
) -> Case
(e
, fst
(drop_dead_code_after_break_block b
))
737 and case_list_
parent_lenv ty env = function
740 (* TODO this is wrong, should continue on to the other cases, but it
741 * doesn't matter in practice since our parser won't parse default
742 * anywhere but in the last position :) Should fix all of this as well
743 * as totality detection for switch. *)
744 let env, tb
= block
env b
in
745 env, [Nast_terminality.Terminal.case
env (Default b
), env.Env.lenv
],
747 | Case
(e
, b
) :: rl
->
748 (* TODO - we should consider handling the comparisons the same
749 * way as Binop Ast.EqEq, since case statements work using ==
750 * comparison rules *)
752 (* The way we handle terminal/nonterminal here is not quite right, you
753 * can still break the type system with things like P3131824. *)
754 let ty_num = (Reason.Rnone
, Tprim
Nast.Tnum
) in
755 let ty_arraykey = (Reason.Rnone
, Tprim
Nast.Tarraykey
) in
756 let both_are_sub_types env tprim ty1 ty2
=
757 (SubType.is_sub_type
env ty1 tprim
) &&
758 (SubType.is_sub_type
env ty2 tprim
) in
759 if Nast_terminality.Terminal.block
env b
then
760 let env, te
, ty2
= expr
env e
in
762 if (both_are_sub_types env ty_num ty ty2
) ||
763 (both_are_sub_types env ty_arraykey ty ty2
)
765 else Type.unify
(fst e
) Reason.URnone
env ty ty2
in
766 let env, tb
= block
env b
in
767 let lenv = env.Env.lenv in
768 let env, rl
, tcl
= case_list
parent_lenv ty env rl
in
769 env, (Nast_terminality.Terminal.case
env (Case
(e
, b
)), lenv) :: rl
,
772 let env, _te
, ty2
= expr
env e
in
774 if (both_are_sub_types env ty_num ty ty2
) ||
775 (both_are_sub_types env ty_arraykey ty ty2
)
777 else Type.unify
(fst e
) Reason.URnone
env ty ty2
in
778 (* Since this block is not terminal we will end up falling through to the
779 * next block. This means the lenv will include what our current
780 * environment is, intersected (or integrated?) with the environment
781 * after executing the block. Example:
783 * $x = 0; // $x = int
786 * $x = ''; // $x = string
789 * $x; // $x = int & string
792 let lenv1 = env.Env.lenv in
793 let env, _ = block
env b
in
794 (* PERF: If the case is empty or a Noop then we do not need to intersect
795 * the lenv since they will be the same.
797 * This saves the cost of intersecting the lenv for the common pattern of
803 let env = match b
with
805 | _ -> LEnv.intersect
env parent_lenv lenv1 env.Env.lenv in
806 case_list_
parent_lenv ty env rl
808 and catch
parent_lenv after_try env (ety
, exn
, b
) =
809 let env = { env with Env.lenv = after_try } in
810 let env = LEnv.fully_integrate
env parent_lenv in
811 let cid = CI
(ety
, []) in
812 let ety_p = (fst ety
) in
813 TUtils.process_class_id
cid;
814 let env, _, _ = instantiable_cid
ety_p env cid in
815 let env, _te
, ety
= static_class_id
ety_p env cid in
816 let env = exception_ty
ety_p env ety
in
817 let env = Env.set_local
env (snd exn
) ety
in
818 let env, _tb
= block
env b
in
819 (* Only keep the local bindings if this catch is non-terminal *)
823 let make_result ty = env, ty in
826 let ty = Env.fresh_type
() in
827 let tvector = Tclass
((pe
, SN.Collections.cTraversable
), [ty]) in
828 make_result (Reason.Rforeach pe
, tvector)
830 let ty1 = Env.fresh_type
() in
831 let ty2 = Env.fresh_type
() in
832 let tmap = Tclass
((pe
, SN.Collections.cKeyedTraversable
), [ty1; ty2]) in
833 make_result (Reason.Rforeach pe
, tmap)
835 let ty = Env.fresh_type
() in
836 let tvector = Tclass
((pe
, SN.Classes.cAsyncIterator
), [ty]) in
837 make_result (Reason.Rasyncforeach pe
, tvector)
839 let ty1 = Env.fresh_type
() in
840 let ty2 = Env.fresh_type
() in
841 let tmap = Tclass
((pe
, SN.Classes.cAsyncKeyedIterator
), [ty1; ty2]) in
842 make_result (Reason.Rasyncforeach pe
, tmap)
844 and bind_as_expr
env ty aexpr
=
845 let env, ety
= Env.expand_type
env ty in
848 | _, Tclass
((p, _), [ty2]) -> (p, (Reason.Rnone
, Tmixed
), ty2)
849 | _, Tclass
((p, _), [ty1; ty2]) -> (p, ty1, ty2)
850 | _ -> assert false in
853 let env, te
, _ = assign
p env ev
ty2 in
855 | Await_as_v
(p, ev
) ->
856 let env, te
, _ = assign
p env ev
ty2 in
857 env, T.Await_as_v
(p, te
)
858 | As_kv
((p, Lvar
((_, k
) as id
)), ev
) ->
859 let env, ty1'
= set_valid_rvalue
p env k
ty1 in
860 let env, te
, _ = assign
p env ev
ty2 in
861 env, T.As_kv
(T.make_typed_expr
p ty1'
(T.Lvar id
), te
)
862 | Await_as_kv
(p, (p1
, Lvar
((_, k
) as id
)), ev
) ->
863 let env, ty1'
= set_valid_rvalue
p env k
ty1 in
864 let env, te
, _ = assign
p env ev
ty2 in
865 env, T.Await_as_kv
(p, T.make_typed_expr p1
ty1'
(T.Lvar id
), te
)
866 | _ -> (* TODO Probably impossible, should check that *)
870 raw_expr ~in_cond
:false env e
872 and raw_expr ~in_cond ?valkind
:(valkind
=`other
) env e
=
873 debug_last_pos := fst e
;
874 let env, te
, ty = expr_ ~in_cond ~valkind
env e
in
875 let () = match !expr_hook with
876 | Some f
-> f e
(Typing_expand.fully_expand
env ty)
878 Typing_hooks.dispatch_infer_ty_hook
ty (fst e
) env;
882 let valkind = `lvalue
in
883 expr_ ~in_cond
:false ~
valkind env e
885 and is_pseudo_function s
=
886 s
= SN.PseudoFunctions.hh_show
||
887 s
= SN.PseudoFunctions.hh_show_env
||
888 s
= SN.PseudoFunctions.hh_log_level
890 (* $x ?? 0 is handled similarly to $x ?: 0, except that the latter will also
891 * look for sketchy null checks in the condition. *)
892 (* TODO TAST: type refinement should be made explicit in the typed AST *)
893 and eif
env ~coalesce ~in_cond
p c e1 e2
=
894 let env, tc, tyc
= raw_expr in_cond
env c
in
895 let parent_lenv = env.Env.lenv in
896 let c = if coalesce
then (p, Binop
(Ast.Diff2
, c, (p, Null
))) else c in
897 let env = condition
env true c in
898 let env, te1
, ty1 = match e1
with
900 let env, ty = non_null
env tyc
in
903 let env, te1
, ty1 = expr
env e1
in
906 let lenv1 = env.Env.lenv in
907 let env = { env with Env.lenv = parent_lenv } in
908 let env = condition
env false c in
909 let env, te2
, ty2 = expr
env e2
in
910 let lenv2 = env.Env.lenv in
912 LEnv.intersect_fake
lenv1.Env.fake_members lenv2.Env.fake_members in
913 (* we restore the locals to their parent state so as not to leak the
914 * effects of the `condition` calls above *)
915 let env = { env with Env.lenv =
916 { parent_lenv with Env.fake_members = fake_members } } in
917 (* This is a shortened form of what we do in Typing_lenv.intersect. The
918 * latter takes local environments as arguments, but our types here
919 * aren't assigned to local variables in an environment *)
920 let env, ty1 = TUtils.unresolved
env ty1 in
921 let env, ty2 = TUtils.unresolved
env ty2 in
922 let env, ty = Unify.unify
env ty1 ty2 in
923 env, T.make_typed_expr
p ty (T.Eif
(tc, te1
, te2
)), ty
931 let env, te
, ty = expr
env e
in
932 let env, tel
, tyl
= exprs
env el
in
933 env, te
::tel
, ty::tyl
937 ~
(valkind: [> `lvalue
| `lvalue_subexpr
| `other
])
939 let make_result env te
ty =
940 env, T.make_typed_expr
p ty te
, ty in
943 * Given a list of types, computes their supertype. If any of the types are
944 * unknown (e.g., comes from PHP), the supertype will be Tany.
946 let compute_supertype env tys
=
947 let env, supertype
= Env.fresh_unresolved_type
env in
948 let has_unknown = List.exists tys
(fun (_, ty) -> ty = Tany
) in
949 let env, tys
= List.rev_map_env
env tys
TUtils.unresolved
in
950 let subtype_value env ty =
951 Type.sub_type
p Reason.URarray_value
env ty supertype
in
953 (* If one of the values comes from PHP land, we have to be conservative
954 * and consider that we don't know what the type of the values are. *)
955 env, (Reason.Rnone
, Tany
)
957 let env = List.fold_left tys ~init
:env ~f
:subtype_value in
961 * Given a 'a list and a method to extract an expr and its ty from a 'a, this
962 * function extracts a list of exprs from the list, and computes the supertype
963 * of all of the expressions' tys.
965 let compute_exprs_and_supertype env l extract_expr_and_ty
=
966 let env, exprs_and_tys
= List.map_env
env l extract_expr_and_ty
in
967 let exprs, tys
= List.unzip exprs_and_tys
in
968 let env, supertype
= compute_supertype env tys
in
969 env, exprs, supertype
in
972 | Any
-> expr_error env (Reason.Rwitness
p)
974 make_result env (T.Array
[]) (Reason.Rwitness
p, Tarraykind AKempty
)
975 | Array l
when Typing_arrays.is_shape_like_array
env l
->
976 let env, (tafl
, fdm
) = List.fold_left_env
env l
977 ~init
:([], ShapeMap.empty
)
978 ~f
:begin fun env (tafl
,fdm
) x ->
979 let env, taf
, (key
, value) = akshape_field
env x in
980 env, (taf
::tafl
, Nast.ShapeMap.add key
value fdm
)
982 make_result env (T.Array
(List.rev tafl
))
983 (Reason.Rwitness
p, Tarraykind
(AKshape fdm
))
985 | Array
(x :: rl
as l
) ->
986 let fields_consistent = check_consistent_fields x rl
in
987 let is_vec = match x with
988 | Nast.AFvalue
_ -> true
989 | Nast.AFkvalue
_ -> false in
990 if fields_consistent && is_vec then
991 let env, tel
, fields
=
992 List.foldi l ~f
:begin fun index
(env, tel
, acc
) e
->
993 let env, te
, ty = aktuple_field
env e
in
994 env, te
::tel
, IMap.add index
ty acc
995 end ~init
:(env, [], IMap.empty
) in
997 (T.Array
(List.map
(List.rev tel
) (fun e
-> T.AFvalue e
)))
998 (Reason.Rwitness
p, Tarraykind
(AKtuple fields
))
1000 let env, value_exprs_and_tys
= List.rev_map_env
env l array_field_value
in
1001 let tvl, value_tys
= List.unzip value_exprs_and_tys
in
1002 let env, value = compute_supertype env value_tys
in
1003 (* TODO TAST: produce a typed expression here *)
1005 make_result env T.Any
1006 (Reason.Rwitness
p, Tarraykind
(AKvec
value))
1008 let env, key_exprs_and_tys
= List.rev_map_env
env l array_field_key
in
1009 let tkl, key_tys
= List.unzip key_exprs_and_tys
in
1010 let env, key
= compute_supertype env key_tys
in
1012 (T.Array
(List.map
(List.rev
(List.zip_exn
tkl tvl))
1013 (fun (tek
, tev
) -> T.AFkvalue
(tek
, tev
))))
1014 (Reason.Rwitness
p, Tarraykind
(AKmap
(key
, value)))
1017 let keys, values
= List.unzip l
in
1019 let env, value_exprs
, value_ty
=
1020 compute_exprs_and_supertype env values array_value
in
1021 let env, key_exprs
, key_ty
=
1022 compute_exprs_and_supertype env keys array_value
in
1024 let field_exprs = List.zip_exn key_exprs value_exprs
in
1026 (T.Darray
field_exprs)
1027 (Reason.Rwitness
p, Tarraykind
(AKdarray
(key_ty
, value_ty
)))
1030 let env, value_exprs
, value_ty
=
1031 compute_exprs_and_supertype env values array_value
in
1033 (T.Varray value_exprs
)
1034 (Reason.Rwitness
p, Tarraykind
(AKvarray value_ty
))
1036 | ValCollection
(kind
, el
) ->
1037 let env, x = Env.fresh_unresolved_type
env in
1038 let env, tel
, tyl
= exprs env el
in
1039 let env, tyl
= List.map_env
env tyl
Typing_env.unbind
in
1040 let env, tyl
= List.map_env
env tyl
TUtils.unresolved
in
1041 let subtype_val env ty =
1042 Type.sub_type
p Reason.URvector
env ty x in
1044 List.fold_left tyl ~init
:env ~f
:subtype_val in
1045 let tvector = Tclass
((p, vc_kind_to_name kind
), [x]) in
1046 let ty = Reason.Rwitness
p, tvector in
1047 make_result env (T.ValCollection
(kind
, tel
)) ty
1048 | KeyValCollection
(kind
, l
) ->
1049 let kl, vl
= List.unzip l
in
1050 let env, tkl, kl = exprs env kl in
1051 let env, kl = List.map_env
env kl Typing_env.unbind
in
1052 let env, tvl, vl
= exprs env vl
in
1053 let env, vl
= List.map_env
env vl
Typing_env.unbind
in
1054 let env, k
= Env.fresh_unresolved_type
env in
1055 let env, v
= Env.fresh_unresolved_type
env in
1056 let env, kl = List.map_env
env kl TUtils.unresolved
in
1057 let subtype_key env ty = Type.sub_type
p Reason.URkey
env ty k
in
1059 List.fold_left
kl ~init
:env ~f
:subtype_key in
1060 let subtype_val env ty = Type.sub_type
p Reason.URvalue
env ty v
in
1061 let env, vl
= List.map_env
env vl
TUtils.unresolved
in
1063 List.fold_left vl ~init
:env ~f
:subtype_val in
1064 let ty = Tclass
((p, kvc_kind_to_name kind
), [k
; v
])
1066 make_result env (T.KeyValCollection
(kind
, List.zip_exn
tkl tvl))
1067 (Reason.Rwitness
p, ty)
1069 let env, te
, ty = expr
env e
in
1070 make_result env (T.Clone te
) ty
1071 | This
when Env.is_static
env ->
1072 Errors.this_in_static
p;
1073 expr_error env (Reason.Rwitness
p)
1074 | This
when valkind = `lvalue
->
1075 Errors.this_lvalue
p;
1076 expr_error env (Reason.Rwitness
p)
1078 let r, _ = Env.get_self
env in
1080 then Errors.this_var_outside_class
p;
1081 let env, (_, ty) = Env.get_local
env this
in
1082 let r = Reason.Rwitness
p in
1084 let ty = r, TUtils.this_of
ty in
1085 (* '$this' always refers to the late bound static type *)
1086 make_result env T.This
(ExprDepTy.make
env CIstatic
ty)
1087 | Assert
(AE_assert e
) ->
1088 let env = condition
env true e
in
1089 make_result env T.Any
(Reason.Rwitness
p, Tprim Tvoid
)
1091 make_result env T.True
(Reason.Rwitness
p, Tprim Tbool
)
1093 make_result env T.False
(Reason.Rwitness
p, Tprim Tbool
)
1094 (* TODO TAST: consider checking that the integer is in range. Right now
1095 * it's possible for HHVM to fail on well-typed Hack code
1098 make_result env (T.Int s
) (Reason.Rwitness
p, Tprim Tint
)
1100 make_result env (T.Float s
) (Reason.Rwitness
p, Tprim Tfloat
)
1101 (* TODO TAST: consider introducing a "null" type, and defining ?t to
1105 let ty = Env.fresh_type
() in
1106 make_result env T.Null
(Reason.Rwitness
p, Toption
ty)
1108 make_result env (T.String s
) (Reason.Rwitness
p, Tprim Tstring
)
1110 let env, tel
= string2
env idl
in
1111 make_result env (T.String2 tel
) (Reason.Rwitness
p, Tprim Tstring
)
1113 Typing_hooks.dispatch_id_hook
x env;
1114 let env, fty
= fun_type_of_id
env x in
1115 begin match fty
with
1116 | _, Tfun fty
-> check_deprecated
(fst
x) fty
;
1119 make_result env (T.Fun_id
x) fty
1120 | Id
((cst_pos
, cst_name
) as id
) ->
1121 Typing_hooks.dispatch_id_hook id
env;
1122 Typing_hooks.dispatch_global_const_hook id
;
1123 (match Env.get_gconst
env cst_name
with
1124 | None
when Env.is_strict
env ->
1125 Errors.unbound_global cst_pos
;
1126 expr_error env (Reason.Rwitness cst_pos
)
1128 make_result env (T.Id id
) (Reason.Rnone
, Tany
)
1131 Phase.localize_with_self
env ty in
1132 make_result env (T.Id id
) ty
1134 | Method_id
(instance
, meth
) ->
1135 (* Method_id is used when creating a "method pointer" using the magic
1136 * inst_meth function.
1138 * Typing this is pretty simple, we just need to check that instance->meth
1139 * is public+not static and then return its type.
1141 Typing_hooks.dispatch_fun_id_hook
(p, "\\"^
SN.SpecialFunctions.inst_meth
);
1142 let env, te
, ty1 = expr
env instance
in
1143 let env, result
, vis
=
1144 obj_get_with_visibility ~is_method
:true ~nullsafe
:None
env ty1
1145 (CIexpr instance
) meth
(fun x -> x) in
1146 let has_lost_info = Env.FakeMembers.is_invalid
env instance
(snd meth
) in
1149 let name = "the method "^snd meth
in
1150 let env, result
= Env.lost_info
name env result
in
1151 make_result env (T.Method_id
(te
, meth
)) result
1155 | _, Tfun fty
-> check_deprecated
p fty
1158 | Some
(method_pos
, Vprivate
_) ->
1159 Errors.private_inst_meth method_pos
p
1160 | Some
(method_pos
, Vprotected
_) ->
1161 Errors.protected_inst_meth method_pos
p
1164 make_result env (T.Method_id
(te
, meth
)) result
1166 | Method_caller
((pos, class_name
) as pos_cname
, meth_name
) ->
1167 (* meth_caller('X', 'foo') desugars to:
1170 Typing_hooks.dispatch_fun_id_hook
(p, "\\"^
SN.SpecialFunctions.meth_caller
);
1171 let class_ = Env.get_class
env class_name
in
1173 | None
-> unbound_name env pos_cname
1175 (* Create a class type for the given object instantiated with unresolved
1176 * types for its type parameters.
1179 List.map_env
env class_.tc_tparams
TUtils.unresolved_tparam
in
1180 let params = List.map
class_.tc_tparams
begin fun (_, (p, n
), _) ->
1181 Reason.Rwitness
p, Tgeneric n
1183 let obj_type = Reason.Rwitness
p, Tapply
(pos_cname
, params) in
1185 (Phase.env_with_self
env) with
1186 substs
= Subst.make
class_.tc_tparams tvarl
;
1188 let env, local_obj_ty
= Phase.localize ~
ety_env env obj_type in
1190 obj_get ~is_method
:true ~nullsafe
:None
env local_obj_ty
1191 (CI
((pos, class_name
), [])) meth_name
(fun x -> x) in
1193 | reason
, Tfun fty
->
1194 check_deprecated
p fty
;
1195 (* We are creating a fake closure:
1196 * function(Class $x, arg_types_of(Class::meth_name))
1197 : return_type_of(Class::meth_name)
1200 ety_env with substs
= Subst.make
class_.tc_tparams tvarl
1203 Phase.check_tparams_constraints ~
ety_env env class_.tc_tparams
in
1204 let env, local_obj_ty
= Phase.localize ~
ety_env env obj_type in
1205 let fty = { fty with
1206 ft_params
= (None
, local_obj_ty
) :: fty.ft_params
} in
1207 let fun_arity = match fty.ft_arity
with
1208 | Fstandard
(min
, max
) -> Fstandard
(min
+ 1, max
+ 1)
1209 | Fvariadic
(min
, x) -> Fvariadic
(min
+ 1, x)
1210 | Fellipsis min
-> Fellipsis
(min
+ 1) in
1213 ft_deprecated
= None
;
1214 ft_abstract
= false;
1215 ft_arity
= fun_arity;
1216 ft_tparams
= fty.ft_tparams
;
1217 ft_where_constraints
= fty.ft_where_constraints
;
1218 ft_params
= fty.ft_params
;
1219 ft_ret
= fty.ft_ret
;
1221 make_result env (T.Method_caller
(pos_cname
, meth_name
))
1222 (reason
, Tfun
caller)
1224 (* This can happen if the method lives in PHP *)
1225 make_result env (T.Method_caller
(pos_cname
, meth_name
))
1226 (Reason.Rwitness
pos, Tany
)
1229 | Smethod_id
(c, meth
) ->
1230 (* Smethod_id is used when creating a "method pointer" using the magic
1231 * class_meth function.
1233 * Typing this is pretty simple, we just need to check that c::meth is
1234 * public+static and then return its type.
1236 Typing_hooks.dispatch_fun_id_hook
(p, "\\"^
SN.SpecialFunctions.class_meth
);
1237 let class_ = Env.get_class
env (snd
c) in
1240 (* The class given as a static string was not found. *)
1243 let smethod = Env.get_static_member
true env class_ (snd meth
) in
1245 | None
-> (* The static method wasn't found. *)
1246 smember_not_found
p ~is_const
:false ~is_method
:true class_ (snd meth
);
1247 expr_error env Reason.Rnone
1248 | Some
{ ce_type
= lazy ty; ce_visibility
; _ } ->
1249 let cid = CI
(c, []) in
1250 let env, _te
, cid_ty
= static_class_id
(fst
c) env cid in
1252 type_expansions
= [];
1253 substs
= SMap.empty
;
1255 from_class
= Some
cid;
1257 let env, smethod_type
= Phase.localize ~
ety_env env ty in
1258 (match smethod_type
with
1259 | _, Tfun
fty -> check_deprecated
p fty
1261 (match smethod_type
, ce_visibility
with
1262 | (r, (Tfun
_ as ty)), Vpublic
->
1263 make_result env (T.Smethod_id
(c, meth
)) (r, ty)
1264 | (r, Tfun
_), Vprivate
_ ->
1265 Errors.private_class_meth
(Reason.to_pos
r) p;
1267 | (r, Tfun
_), Vprotected
_ ->
1268 Errors.protected_class_meth
(Reason.to_pos
r) p;
1271 Errors.internal_error
p "We have a method which isn't callable";
1277 let r = Reason.Rplaceholder
p in
1278 let ty = r, Tprim Tvoid
in
1279 make_result env (T.Lplaceholder
p) ty
1280 | Dollardollar
((_, x) as id
) ->
1282 Env.get_local
env x in
1283 make_result env (T.Dollardollar id
) ty
1284 | Lvar
((_, x) as id
) ->
1285 Typing_hooks.dispatch_lvar_hook id
env;
1286 let env, ty = Env.get_local
env x in
1287 make_result env (T.Lvar id
) ty
1288 | Lvarvar
(i
, id
) ->
1289 Typing_hooks.dispatch_lvar_hook id
env;
1290 (** Can't easily track any typing information for variable variable. *)
1291 make_result env (T.Lvarvar
(i
, id
)) (Reason.Rnone
, Tany
)
1293 let env, tel
, tyl
= exprs env el
in
1294 (* TODO TAST: figure out role of unbind here *)
1295 let env, tyl
= List.map_env
env tyl
Typing_env.unbind
in
1296 let ty = Reason.Rwitness
p, Ttuple tyl
in
1297 make_result env (T.List tel
) ty
1299 let env, te1
, ty1 = expr
env e1
in
1300 let env, ty1 = Typing_env.unbind
env ty1 in
1301 let env, te2
, ty2 = expr
env e2
in
1302 let env, ty2 = Typing_env.unbind
env ty2 in
1304 Reason.Rwitness
p, Tclass
((p, SN.Collections.cPair
), [ty1; ty2]) in
1305 make_result env (T.Pair
(te1
, te2
)) ty
1307 let env, tel
, tyl
= exprs env el
in
1308 let ty = Reason.Rwitness
p, Ttuple tyl
in
1309 make_result env (T.Expr_list tel
) ty
1310 | Array_get
(e
, None
) ->
1311 let env, te1
, ty1 = update_array_type
p env e None
valkind in
1312 let env, ty = array_append
p env ty1 in
1313 make_result env (T.Array_get
(te1
, None
)) ty
1314 | Array_get
(e1
, Some e2
) ->
1315 let env, te1
, ty1 = update_array_type
p env e1
(Some e2
) valkind in
1316 let env, ty1 = TUtils.fold_unresolved
env ty1 in
1317 let env, te2
, ty2 = expr
env e2
in
1318 let is_lvalue = (valkind == `lvalue
) in
1319 let env, ty = array_get
is_lvalue p env ty1 e2
ty2 in
1320 make_result env (T.Array_get
(te1
, Some te2
)) ty
1321 | Call
(Cnormal
, (pos_id
, Id
((_, s
) as id
)), el
, [])
1322 when is_pseudo_function s
->
1323 let env, tel
, tys
= exprs env el
in
1324 if s
= SN.PseudoFunctions.hh_show
1325 then List.iter tys
(Typing_log.hh_show
p env)
1327 if s
= SN.PseudoFunctions.hh_show_env
1328 then Typing_log.hh_show_env
p env
1330 if s
= SN.PseudoFunctions.hh_log_level
1332 | [(_, Int
(_, level_str
))] ->
1333 Typing_log.hh_log_level
(int_of_string level_str
)
1339 T.make_implicitly_typed_expr pos_id
(T.Id id
),
1341 [])) (Env.fresh_type
())
1342 | Call
(call_type
, e
, el
, uel
) ->
1343 let env, te
, result
= dispatch_call
p env call_type e el uel
in
1344 let env = Env.forget_members
env p in
1346 (* For example, e1 += e2. This is typed and translated as if
1347 * written e1 = e1 + e2.
1348 * TODO TAST: is this right? e1 will get evaluated more than once
1350 | Binop
(Ast.Eq
(Some op
), e1
, e2
) ->
1351 let e2 = p, Binop
(op
, e1
, e2) in
1352 raw_expr in_cond
env (p, Binop
(Ast.Eq None
, e1
, e2))
1353 | Binop
(Ast.Eq None
, e1
, e2) ->
1354 let env, te2
, ty2 = raw_expr in_cond
env e2 in
1355 let env, te1
, ty = assign
p env e1
ty2 in
1356 Typing_hooks.dispatch_assign_hook
p ty2 env;
1357 (* If we are assigning a local variable to another local variable then
1358 * the expression ID associated with e2 is transferred to e1
1361 | (_, Lvar
(_, x1
)), (_, Lvar
(_, x2
)) ->
1362 let eid2 = Env.get_local_expr_id
env x2
in
1366 ~f
:(Env.set_local_expr_id
env x1
) in
1367 make_result env (T.Binop
(Ast.Eq None
, te1
, te2
)) ty
1369 make_result env (T.Binop
(Ast.Eq None
, te1
, te2
)) ty
1371 | Binop
((Ast.AMpamp
| Ast.BArbar
as bop
), e1
, e2) ->
1372 let c = bop
= Ast.AMpamp
in
1373 let lenv = env.Env.lenv in
1374 let env, te1
, ty1 = expr
env e1
in
1375 let env = condition
env c e1
in
1376 let env, te2
, ty2 = raw_expr in_cond
env e2 in
1377 let env = { env with Env.lenv = lenv } in
1378 Typing_hooks.dispatch_binop_hook
p bop
ty1 ty2;
1379 make_result env (T.Binop
(bop
, te1
, te2
))
1380 (Reason.Rlogic_ret
p, Tprim Tbool
)
1381 | Binop
(bop
, e
, (pe
, Null
))
1382 | Binop
(bop
, (pe
, Null
), e
)
1383 when Env.is_strict
env && (bop
= Ast.EQeqeq
|| bop
= Ast.Diff2
) ->
1384 let _, te
, ty = raw_expr in_cond
env e
in
1386 then Typing_equality_check.assert_nullable
p bop
env ty;
1387 make_result env (T.Binop
(bop
, te
, T.make_typed_expr pe
ty T.Null
))
1388 (Reason.Rcomp
p, Tprim Tbool
)
1389 | Binop
(bop
, e1
, e2) ->
1390 let env, te1
, ty1 = raw_expr in_cond
env e1
in
1391 let env, te2
, ty2 = raw_expr in_cond
env e2 in
1393 binop in_cond
p env bop
(fst e1
) te1
ty1 (fst
e2) te2
ty2 in
1394 Typing_hooks.dispatch_binop_hook
p bop
ty1 ty2;
1396 | Pipe
((_, id
) as e0
, e1
, e2) ->
1397 let env, te1
, ty = expr
env e1
in
1398 (** id is the ID of the $$ that is implicitly declared by the pipe.
1399 * Set the local type for the $$ in the RHS. *)
1400 let env = Env.set_local
env id
ty in
1401 let env, te2
, ty2 = expr
env e2 in
1403 * Return ty2 since the type of the pipe expression is the type of the
1406 * Note: env does have the type of this Pipe's $$, but it doesn't
1407 * override the outer one since they have different ID's.
1410 * a() |> ( inner1($$) |> inner2($$) ) + $$
1412 * The rightmost $$ refers to the result of a()
1414 make_result env (T.Pipe
(e0
, te1
, te2
)) ty2
1416 let env, te
, ty = raw_expr in_cond
env e
in
1417 unop
p env uop te
ty
1418 | Eif
(c, e1
, e2) -> eif
env ~coalesce
:false ~in_cond
p c e1
e2
1419 | NullCoalesce
(e1
, e2) -> eif
env ~coalesce
:true ~in_cond
p e1 None
e2
1421 begin match Env.get_typedef
env (snd sid
) with
1422 | Some
{td_tparams
= tparaml
; _} ->
1423 (* Typedef type parameters cannot have constraints *)
1424 let params = List.map ~f
:begin fun (_, (p, x), _) ->
1425 Reason.Rwitness
p, Tgeneric
x
1427 let tdef = Reason.Rwitness
(fst sid
), Tapply
(sid
, params) in
1429 Reason.Rwitness
p, Tapply
((p, SN.Classes.cTypename
), [tdef]) in
1430 let env, tparams
= List.map_env
env tparaml
begin fun env _ ->
1431 Env.fresh_unresolved_type
env
1433 let ety_env = { (Phase.env_with_self
env) with
1434 substs
= Subst.make tparaml tparams
} in
1435 let env = Phase.check_tparams_constraints ~
ety_env env tparaml
in
1436 let env, ty = Phase.localize ~
ety_env env typename in
1437 make_result env (T.Typename sid
) ty
1439 (* Should never hit this case since we only construct this AST node
1440 * if in the expression Foo::class, Foo is a type def.
1442 expr_error env (Reason.Rwitness
p)
1444 | Class_const
(cid, mid
) -> class_const
env p (cid, mid
)
1445 | Class_get
(x, (_, y
))
1446 when Env.FakeMembers.get_static
env x y
<> None
->
1447 let env, local
= Env.FakeMembers.make_static
p env x y
in
1448 let local = p, Lvar
(p, local) in
1450 | Class_get
(cid, mid
) ->
1451 TUtils.process_static_find_ref
cid mid
;
1452 let env, te
, cty
= static_class_id
p env cid in
1454 class_get ~is_method
:false ~is_const
:false env cty mid
cid in
1455 if Env.FakeMembers.is_static_invalid
env cid (snd mid
)
1457 let fake_name = Env.FakeMembers.make_static_id
cid (snd mid
) in
1458 let env, ty = Env.lost_info
fake_name env ty in
1459 make_result env (T.Class_get
(te
, mid
)) ty
1461 make_result env (T.Class_get
(te
, mid
)) ty
1462 (* Fake member property access. For example:
1463 * if ($x->f !== null) { ...$x->f... }
1465 | Obj_get
(e
, (_, Id
(_, y
)), _)
1466 when Env.FakeMembers.get
env e y
<> None
->
1467 let env, local = Env.FakeMembers.make
p env e y
in
1468 let local = p, Lvar
(p, local) in
1470 (* Statically-known instance property access e.g. $x->f *)
1471 | Obj_get
(e1
, (pm
, Id m
), nullflavor
) ->
1473 (match nullflavor
with
1474 | OG_nullthrows
-> None
1475 | OG_nullsafe
-> Some
p
1477 let env, te1
, ty1 = expr
env e1
in
1479 obj_get ~is_method
:false ~
nullsafe env ty1 (CIexpr e1
) m
(fun x -> x) in
1480 let has_lost_info = Env.FakeMembers.is_invalid
env e1
(snd m
) in
1484 let name = "the member " ^ snd m
in
1485 Env.lost_info
name env result
1489 make_result env (T.Obj_get
(te1
,
1490 T.make_implicitly_typed_expr pm
(T.Id m
), nullflavor
)) result
1491 (* Dynamic instance property access e.g. $x->$f *)
1492 | Obj_get
(e1
, e2, nullflavor
) ->
1493 let env, te1
, _ = expr
env e1
in
1494 let env, te2
, _ = expr
env e2 in
1495 let ty = (Reason.Rwitness
p, Tany
) in
1496 make_result env (T.Obj_get
(te1
, te2
, nullflavor
)) ty
1498 make_result env T.Yield_break
(Reason.Rwitness
p, Tany
)
1500 let env, (taf
, opt_key
, value) = array_field
env af
in
1501 let send = Env.fresh_type
() in
1502 let env, key
= match af
, opt_key
with
1503 | Nast.AFvalue
(p, _), None
->
1505 match Env.get_fn_kind
env with
1508 Errors.internal_error
p "yield found in non-generator";
1511 (Reason.Rwitness
p, Tprim Tint
)
1512 | Ast.FAsyncGenerator
->
1513 (Reason.Ryield_asyncnull
p,
1514 Toption
(Env.fresh_type
()))
1519 | _, _ -> assert false in
1520 let rty = match Env.get_fn_kind
env with
1522 Reason.Ryield_gen
p,
1523 Tclass
((p, SN.Classes.cGenerator
), [key
; value; send])
1524 | Ast.FAsyncGenerator
->
1525 Reason.Ryield_asyncgen
p,
1526 Tclass
((p, SN.Classes.cAsyncGenerator
), [key
; value; send])
1527 | Ast.FSync
| Ast.FAsync
->
1528 failwith
"Parsing should never allow this" in
1530 Type.sub_type
p (Reason.URyield
) env rty (Env.get_return
env) in
1531 let env = Env.forget_members
env p in
1532 make_result env (T.Yield taf
) (Reason.Ryield_send
p, Toption
send)
1534 let env, te
, rty = expr
env e
in
1535 let env, ty = Async.overload_extract_from_awaitable
env p rty in
1536 make_result env (T.Await te
) ty
1537 | Special_func func
-> special_func
env p func
1538 | New
(c, el
, uel
) ->
1539 Typing_hooks.dispatch_new_id_hook
c env p;
1540 TUtils.process_static_find_ref
c (p, SN.Members.__construct
);
1541 let check_not_abstract = true in
1542 let env, tc, tel
, tuel
, ty =
1543 new_object ~
check_not_abstract p env c el uel
in
1544 let env = Env.forget_members
env p in
1545 make_result env (T.New
(tc, tel
, tuel
)) (ExprDepTy.make
env c ty)
1546 | Cast
((_, Harray
(None
, None
)), _) when Env.is_strict
env ->
1547 Errors.array_cast
p;
1548 expr_error env (Reason.Rwitness
p)
1550 let env, te
, ty2 = expr
env e
in
1551 Async.enforce_not_awaitable
env (fst e
) ty2;
1552 let env, ty = Phase.hint_locl
env hint
in
1553 make_result env (T.Cast
(hint
, te
)) ty
1554 | InstanceOf
(e
, cid) ->
1555 let env, te
, _ = expr
env e
in
1556 TUtils.process_class_id
cid;
1557 let env, te2
, _class
= instantiable_cid
p env cid in
1558 make_result env (T.InstanceOf
(te
, te2
)) (Reason.Rwitness
p, Tprim Tbool
)
1560 let ft = Decl.fun_decl_in_env
env.Env.decl_env f
in
1561 (* When creating a closure, the 'this' type will mean the late bound type
1562 * of the current enclosing class
1565 { (Phase.env_with_self
env) with from_class
= Some CIstatic
} in
1566 let env, ft = Phase.localize_ft ~
ety_env env ft in
1567 (* check for recursive function calls *)
1568 let anon = anon_make
env p f
in
1569 let env, anon_id
= Env.add_anonymous
env anon in
1570 let env = Errors.try_with_error
1572 ignore
(anon env ft.ft_params
); env)
1574 (* If the anonymous function declaration has errors itself, silence
1575 them in any subsequent usages. *)
1576 let anon env fun_params
=
1577 Errors.ignore_
(fun () -> (anon env fun_params
)) in
1578 Env.set_anonymous
env anon_id
anon) in
1579 (* TODO TAST: introduce lambda node in Tast.expr *)
1580 make_result env T.Any
(Reason.Rwitness
p, Tanon
(ft.ft_arity
, anon_id
))
1581 | Xml
(sid
, attrl
, el
) ->
1582 let cid = CI
(sid
, []) in
1583 let env, _te
, obj
= expr
env (fst sid
, New
(cid, [], [])) in
1584 let env, attr_ptyl
= List.map_env
env attrl
begin fun env attr
->
1585 (* Typecheck the expressions - this just checks that the expressions are
1586 * valid, not that they match the declared type for the attribute *)
1587 let namepstr, valexpr
= attr
in
1588 let valp, _ = valexpr
in
1589 let env, _te
, valty
= expr
env valexpr
in
1590 env, (namepstr, (valp, valty
))
1592 let env, _tel
, _body
= exprs env el
in
1593 let env, _te
, classes
= class_id_for_new
p env cid in
1595 | [] -> make_result env T.Any
(Reason.Runknown_class
p, Tobject
)
1596 (* OK to ignore rest of list; class_info only used for errors, and
1597 * cid = CI sid cannot produce a union of classes anyhow *)
1598 | (_, class_info
, _)::_ ->
1599 let env = List.fold_left attr_ptyl ~f
:begin fun env attr
->
1600 let namepstr, valpty
= attr
in
1601 let valp, valty
= valpty
in
1602 (* We pretend that XHP attributes are stored as member variables,
1603 * prefixed with a colon.
1605 * This converts the member name to an attribute name. *)
1606 let name = ":" ^
(snd
namepstr) in
1608 obj_get ~is_method
:false ~
nullsafe:None
env obj
cid
1609 (fst
namepstr, name) (fun x -> x) in
1610 let ureason = Reason.URxhp
(class_info
.tc_name
, snd
namepstr) in
1611 Type.sub_type
valp ureason env valty declty
1613 make_result env T.Any obj
1615 (* TODO TAST: change AST so that order of shape expressions is preserved.
1616 * At present, evaluation order is unspecified in TAST *)
1618 (* allow_inter adds a type-variable *)
1621 (fun env e
-> let env, te
, ty = expr
env e
in env, (te
,ty))
1624 let convert_expr_and_type_to_shape_field_type env (_, ty) =
1625 let env, sft_ty
= TUtils.unresolved
env ty in
1626 (* An expression evaluation always corresponds to a shape_field_type
1627 with sft_optional = false. *)
1628 env, { sft_optional
= false; sft_ty
} in
1629 ShapeMap.map_env
convert_expr_and_type_to_shape_field_type env tfdm
in
1630 let env = check_shape_keys_validity
env p (ShapeMap.keys fdm
) in
1631 (* Fields are fully known, because this shape is constructed
1632 * using shape keyword and we know exactly what fields are set. *)
1633 make_result env (T.Shape
(ShapeMap.map
(fun (te
,_) -> te
) tfdm
))
1634 (Reason.Rwitness
p, Tshape
(FieldsFullyKnown
, fdm
))
1636 and class_const ?
(incl_tc
=false) env p (cid, mid
) =
1637 TUtils.process_static_find_ref
cid mid
;
1638 let env, ce
, cty
= static_class_id
p env cid in
1639 let env, const_ty
, cc_abstract_info
=
1640 class_get ~is_method
:false ~is_const
:true ~incl_tc
env cty mid
cid in
1641 match cc_abstract_info
with
1642 | Some
(cc_pos
, cc_name
) ->
1643 let () = match cid with
1644 | CIstatic
| CIexpr
_ -> ();
1645 | _ -> Errors.abstract_const_usage
p cc_pos cc_name
; ()
1646 in env, T.make_typed_expr
p const_ty
(T.Class_const
(ce
, mid
)), const_ty
1648 env, T.make_typed_expr
p const_ty
(T.Class_const
(ce
, mid
)), const_ty
1650 (*****************************************************************************)
1651 (* Anonymous functions. *)
1652 (*****************************************************************************)
1653 and anon_bind_param
params env ty : Env.env =
1656 (* This code cannot be executed normally, because the arity is wrong
1657 * and it will error later. Bind as many parameters as we can and carry
1660 | param
:: paraml
->
1662 match param
.param_hint
with
1665 let h = Decl_hint.hint
env.Env.decl_env
h in
1666 (* When creating a closure, the 'this' type will mean the
1667 * late bound type of the current enclosing class
1670 { (Phase.env_with_self
env) with from_class
= Some CIstatic
} in
1671 let env, h = Phase.localize ~
ety_env env h in
1672 let pos = Reason.to_pos
(fst
ty) in
1673 let env = Type.sub_type
pos Reason.URparam
env ty h in
1674 (* Closures are allowed to have explicit type-hints. When
1675 * that is the case we should check that the argument passed
1676 * is compatible with the type-hint.
1677 * The body of the function should be type-checked with the
1678 * hint and not the type of the argument passed.
1679 * Otherwise it leads to strange results where
1680 * foo(?string $x = null) is called with a string and fails to
1681 * type-check. If $x is a string instead of ?string, null is not
1682 * subtype of string ...
1684 let env, _ = bind_param env (h, param
) in
1687 let env, _ = bind_param env (ty, param
) in
1690 and anon_bind_opt_param
env param
: Env.env =
1691 match param
.param_expr
with
1693 let ty = Reason.Rnone
, Tany
in
1694 let env, _ = bind_param env (ty, param
) in
1697 let env, _te
, ty = expr
env default
in
1698 Typing_sequencing.sequence_check_expr default
;
1699 let env, _ = bind_param env (ty, param
) in
1702 and anon_check_param
env param
=
1703 match param
.param_hint
with
1706 let env, hty
= Phase.hint_locl
env hty
in
1707 let env, paramty
= Env.get_local
env (Local_id.get param
.param_name
) in
1708 let hint_pos = Reason.to_pos
(fst hty
) in
1709 let env = Type.sub_type
hint_pos Reason.URhint
env paramty hty
in
1712 and anon_make tenv
p f
=
1713 let anon_lenv = tenv
.Env.lenv in
1714 let is_typing_self = ref false in
1715 let nb = Nast.assert_named_body f
.f_body
in
1716 fun env (supplied_params
: (string option * _) list
) ->
1719 Errors.anonymous_recursive
p;
1723 is_typing_self := true;
1724 Env.anon anon_lenv env begin fun env ->
1725 let params = ref f
.f_params in
1726 let env = List.fold_left ~f
:(anon_bind_param
params) ~init
:env
1727 (List.map supplied_params snd
) in
1728 let env = List.fold_left ~f
:anon_bind_opt_param ~init
:env !params in
1729 let env = List.fold_left ~f
:anon_check_param ~init
:env f
.f_params in
1732 | None
-> Env.fresh_unresolved_type
env
1734 let ret = TI.instantiable_hint
env x in
1735 (* If a 'this' type appears it needs to be compatible with the
1739 { (Phase.env_with_self
env) with
1740 from_class
= Some CIstatic
} in
1741 Phase.localize ~
ety_env env ret in
1742 let env = Env.set_return
env hret
in
1743 let env = Env.set_fn_kind
env f
.f_fun_kind
in
1744 let env, _ = block
env nb.fnb_nast
in
1746 if Nast_terminality.Terminal.block tenv
nb.fnb_nast
1747 || nb.fnb_unsafe
|| !auto_complete
1749 else fun_implicit_return
env p hret
nb.fnb_nast f
.f_fun_kind
1751 is_typing_self := false;
1756 (*****************************************************************************)
1757 (* End of anonymous functions. *)
1758 (*****************************************************************************)
1760 and special_func
env p func
=
1761 let env, tfunc
, ty = (match func
with
1763 let env, te
, ety
= expr
env e
in
1764 let env, ty = Async.gena
env p ety
in
1767 let env, tel
, etyl
= exprs env el
in
1768 let env, ty = Async.genva
env p etyl
in
1769 env, T.Genva tel
, ty
1770 | Gen_array_rec e
->
1771 let env, te
, ety
= expr
env e
in
1772 let env, ty = Async.gen_array_rec
env p ety
in
1773 env, T.Gen_array_rec te
, ty
1776 (Reason.Rwitness
p, Tclass
((p, SN.Classes.cAwaitable
), [ty])) in
1777 env, T.make_typed_expr
p result_ty (T.Special_func tfunc
), result_ty
1780 and requires_consistent_construct
= function
1787 and new_object ~
check_not_abstract p env cid el uel
=
1788 (* Obtain class info from the cid expression. We get multiple
1789 * results with a CIexpr that has a union type *)
1790 let env, tcid
, classes
= instantiable_cid
p env cid in
1791 let rec gather env tel tuel res classes
=
1797 let _ = exprs env el
in
1798 env, tcid
, tel
, tuel
, (Reason.Runknown_class
p, Tobject
)
1799 | [ty] -> env, tcid
, tel
, tuel
, ty
1800 | tyl
-> env, tcid
, tel
, tuel
, (Reason.Rwitness
p, Tunresolved tyl
)
1803 | (cname
, class_info
, c_ty
)::classes
->
1804 if check_not_abstract && class_info
.tc_abstract
1805 && not
(requires_consistent_construct
cid) then
1806 uninstantiable_error
p cid class_info
.tc_pos class_info
.tc_name
p c_ty
;
1807 let env, obj_ty_
, params =
1808 match cid, snd c_ty
with
1809 | CI
(_, _::_), Tclass
(_, tyl
) -> env, (snd c_ty
), tyl
1811 let env, params = List.map_env
env class_info
.tc_tparams
1812 (fun env _ -> Env.fresh_unresolved_type
env) in
1813 env, (Tclass
(cname
, params)), params in
1814 let r_witness = Reason.Rwitness
p in
1815 let obj_ty = (r_witness, obj_ty_
) in
1816 let env, _tcid
, tel
, tuel
=
1817 call_construct
p env class_info
params el uel
cid in
1818 if not
(snd class_info
.tc_construct
) then
1820 | CIstatic
-> Errors.new_inconsistent_construct
p cname `static
1821 | CIexpr
_ -> Errors.new_inconsistent_construct
p cname `classname
1826 ((r_witness, TUtils.this_of
obj_ty)::res
) classes
1828 (match (fst class_info
.tc_construct
) with
1829 | Some
{ce_type
= lazy ty; _ } ->
1831 type_expansions
= [];
1832 substs
= SMap.empty
;
1836 let _, ce_type
= Phase.localize ~
ety_env env ty in
1837 ignore
(check_abstract_parent_meth
SN.Members.__construct
p ce_type
)
1839 gather env tel tuel
(obj_ty::res
) classes
1840 | CI
_ | CIself
-> gather env tel tuel
(obj_ty::res
) classes
1842 let c_ty = r_witness, snd
c_ty in
1843 (* When constructing from a (classname) variable, the variable
1844 * dictates what the constructed object is going to be. This allows
1845 * for generic and dependent types to be correctly carried
1846 * through the 'new $foo()' iff the constructed obj_ty is a
1847 * supertype of the variable-dictated c_ty *)
1848 let env = SubType.sub_type
env c_ty obj_ty in
1849 gather env tel tuel
(c_ty::res
) classes
1851 gather env [] [] [] classes
1853 (* FIXME: we need to separate our instantiability into two parts. Currently,
1854 * all this function is doing is checking if a given type is inhabited --
1855 * that is, whether there are runtime values of type T. However,
1856 * instantiability should be the stricter notion that T has a runtime
1857 * constructor; that is, `new T()` should be valid. In particular, interfaces
1858 * are inhabited, but not instantiable.
1859 * To make this work with classname, we likely need to add something like
1860 * concrete_classname<T>, where T cannot be an interface.
1862 and instantiable_cid
p env cid =
1863 let env, te
, classes
= class_id_for_new
p env cid in
1865 List.iter classes
begin fun ((pos, name), class_info
, c_ty) ->
1866 if class_info
.tc_kind
= Ast.Ctrait
|| class_info
.tc_kind
= Ast.Cenum
1869 | CIexpr
_ | CI
_ ->
1870 uninstantiable_error
p cid class_info
.tc_pos
name pos c_ty
1871 | CIstatic
| CIparent
| CIself
-> ()
1872 else if class_info
.tc_kind
= Ast.Cabstract
&& class_info
.tc_final
1874 uninstantiable_error
p cid class_info
.tc_pos
name pos c_ty
1879 and uninstantiable_error reason_pos
cid c_tc_pos c_name c_usage_pos
c_ty =
1880 let reason_msgl = match cid with
1882 let ty_str = "This would be "^
Typing_print.error
(snd
c_ty) in
1883 [(reason_pos
, ty_str)]
1885 Errors.uninstantiable_class c_usage_pos c_tc_pos c_name
reason_msgl
1887 and exception_ty
pos env ty =
1888 let exn_ty = Reason.Rthrow
pos, Tclass
((pos, SN.Classes.cException
), []) in
1889 Type.sub_type
pos (Reason.URthrow
) env ty exn_ty
1891 and shape_field_pos
= function
1892 | Ast.SFlit
(p, _) -> p
1893 | Ast.SFclass_const
((cls_pos
, _), (mem_pos
, _)) -> Pos.btw cls_pos mem_pos
1895 and check_shape_keys_validity
env pos keys =
1896 (* If the key is a class constant, get its class name and type. *)
1897 let get_field_info env key
=
1898 let key_pos = shape_field_pos key
in
1899 (* Empty strings or literals that start with numbers are not
1900 permitted as shape field names. *)
1902 | Ast.SFlit
(_, key_name
) ->
1903 if (String.length key_name
= 0) then
1904 (Errors.invalid_shape_field_name_empty
key_pos)
1905 else if (key_name
.[0] >= '
0'
&& key_name
.[0] <='
9'
) then
1906 (Errors.invalid_shape_field_name_number
key_pos);
1908 | Ast.SFclass_const
(_, cls
as x, y
) ->
1909 let env, _te
, ty = class_const
env pos (CI
(x, []), y
) in
1910 let env = Typing_enum.check_valid_array_key_type
1911 Errors.invalid_shape_field_type ~allow_any
:false
1913 env, key_pos, Some
(cls
, ty))
1916 let check_field witness_pos witness_info
env key
=
1917 let env, key_pos, key_info
= get_field_info env key
in
1918 (match witness_info
, key_info
with
1920 Errors.invalid_shape_field_literal
key_pos witness_pos
; env
1922 Errors.invalid_shape_field_const
key_pos witness_pos
; env
1924 | Some
(cls1
, ty1), Some
(cls2
, ty2) ->
1925 if cls1
<> cls2
then
1926 Errors.shape_field_class_mismatch
1927 key_pos witness_pos
(strip_ns cls2
) (strip_ns cls1
);
1928 (* We want to use our own error message here instead of the normal
1929 * unification one. *)
1931 (fun () -> Unify.iunify
env ty1 ty2)
1933 Errors.shape_field_type_mismatch
1935 (Typing_print.error
(snd
ty2)) (Typing_print.error
(snd
ty1));
1939 (* Sort the keys by their positions since the error messages will make
1940 * more sense if we take the one that appears first as canonical and if
1941 * they are processed in source order. *)
1942 let cmp_keys x y
= Pos.compare
(shape_field_pos
x) (shape_field_pos y
) in
1943 let keys = List.sort
cmp_keys keys in
1947 | witness
:: rest_keys
->
1948 let env, pos, info
= get_field_info env witness
in
1949 List.fold_left ~f
:(check_field pos info
) ~init
:env rest_keys
1951 and check_valid_rvalue
p env ty =
1952 let rec iter_over_types env tyl
=
1958 let env, ety
= Env.expand_type
env ty in
1960 | r, Tprim Tnoreturn
->
1961 Errors.noreturn_usage
p
1962 (Reason.to_string
"A noreturn function always throws or exits" r);
1967 (Reason.to_string
"A void function doesn't return a value" r);
1970 | _, Tunresolved tyl2
->
1971 iter_over_types env (tyl2
@ tyl
)
1974 iter_over_types env tyl
in
1975 iter_over_types env [ty]
1977 and set_valid_rvalue
p env x ty =
1978 let env, ty = check_valid_rvalue
p env ty in
1979 let env = Env.set_local
env x ty in
1980 (* We are assigning a new value to the local variable, so we need to
1981 * generate a new expression id
1983 let env = Env.set_local_expr_id
env x (Ident.tmp
()) in
1986 (* Deal with assignment of a value of type ty2 to lvalue e1 *)
1987 and assign
p env e1
ty2 : _ * T.expr
* T.ty =
1988 let make_result env te1
ty1 = (env, T.make_typed_expr
p ty1 te1
, ty1) in
1990 | (_, Lvar
((_, x) as id
)) ->
1991 let env, ty1 = set_valid_rvalue
p env x ty2 in
1992 make_result env (T.Lvar id
) ty1
1993 | (_, Lplaceholder id
) ->
1994 let placeholder_ty = Reason.Rplaceholder
p, (Tprim Tvoid
) in
1995 make_result env (T.Lplaceholder id
) placeholder_ty
1997 let env, folded_ty2
= TUtils.fold_unresolved
env ty2 in
1999 try_over_concrete_supertypes env folded_ty2
2000 begin fun env ty2 ->
2002 (* Vector<t> or ImmVector<t> or ConstVector<t> or vec<T> *)
2003 | (_, Tclass
((_, x), [elt_type
]))
2004 when x = SN.Collections.cVector
2005 || x = SN.Collections.cImmVector
2006 || x = SN.Collections.cVec
2007 || x = SN.Collections.cConstVector
->
2008 let env, tel
= List.map_env
env el
begin fun env e
->
2009 let env, te
, _ = assign
(fst e
) env e elt_type
in
2012 make_result env (T.List tel
) ty2
2014 | (_, Tarraykind
(AKvec elt_type
)) ->
2015 let env, tel
= List.map_env
env el
begin fun env e
->
2016 let env, te
, _ = assign
(fst e
) env e elt_type
in
2019 make_result env (T.List tel
) ty2
2020 (* array or empty array or Tany *)
2021 | (r, (Tarraykind
(AKany
| AKempty
) | Tany
)) ->
2022 let env, tel
= List.map_env
env el
begin fun env e
->
2023 let env, te
, _ = assign
(fst e
) env e
(r, Tany
) in
2026 make_result env (T.List tel
) ty2
2028 | ((r, Tclass
((_, coll
), [ty1; ty2])) as folded_ety2
)
2029 when coll
= SN.Collections.cPair
->
2032 let env, te1
, _ = assign
p env x1
ty1 in
2033 let env, te2
, _ = assign
p env x2
ty2 in
2034 make_result env (T.List
[te1
; te2
]) folded_ety2
2036 Errors.pair_arity
p;
2037 make_result env T.Any
(r, Terr
))
2038 (* tuple-like array *)
2039 | (r, Tarraykind
(AKtuple fields
)) ->
2041 let p2 = Reason.to_pos
r in
2042 let tyl = List.rev
(IMap.values fields
) in
2043 let size1 = List.length el
in
2044 let size2 = List.length
tyl in
2047 Errors.tuple_arity
p2 size2 p1 size1;
2048 make_result env T.Any
(r, Terr
)
2051 let env, reversed_tel
=
2052 List.fold2_exn el
tyl ~f
:begin fun (env,tel
) lvalue
ty2 ->
2053 let env, te
, _ = assign
p env lvalue
ty2 in
2055 end ~init
:(env,[]) in
2056 make_result env (T.List
(List.rev reversed_tel
)) ty2
2057 (* Other, including tuples. Create a tuple type for the left hand
2058 * side and attempt subtype against it. In particular this deals with
2059 * types such as (string,int) | (int,bool) *)
2062 List.map_env
env el
(fun env _ -> Env.fresh_unresolved_type
env) in
2063 let env = Type.sub_type
p Reason.URassign
env folded_ty2
2064 (Reason.Rwitness
(fst e1
), Ttuple
tyl) in
2065 let env, reversed_tel
=
2066 List.fold2_exn el
tyl ~init
:(env,[]) ~f
:(fun (env,tel
) lvalue
ty2 ->
2067 let env, te
, _ = assign
p env lvalue
ty2 in
2069 make_result env (T.List
(List.rev reversed_tel
)) ty2
2071 begin match resl with
2073 | _ -> assign_simple
p env e1
ty2
2077 let lenv = env.Env.lenv in
2078 let no_fakes = LEnv.env_with_empty_fakes
env in
2079 (* In this section, we check that the assignment is compatible with
2080 * the real type of a member. Remember that members can change
2081 * type (cf fake_members). But when we assign a value to $this->x,
2082 * we want to make sure that the type assign to $this->x is compatible
2083 * with the actual type hint. In this portion of the code, type-check
2084 * the assignment in an environment without fakes, and therefore
2085 * check that the assignment is compatible with the type of
2088 let env, _te1
, real_type
= lvalue
no_fakes e1
in
2089 let env, exp_real_type
= Env.expand_type
env real_type
in
2090 let env = { env with Env.lenv = lenv } in
2091 let env, ety2
= Env.expand_type
env ty2 in
2092 let real_type_list =
2093 match exp_real_type
with
2094 | _, Tunresolved
tyl -> tyl
2097 let env = List.fold_left
real_type_list ~f
:begin fun env real_type
->
2098 Type.sub_type
p (Reason.URassign
) env ety2 real_type
2101 | _, Obj_get
((_, This
| _, Lvar
_ as obj
),
2102 (_, Id
(_, member_name
)),
2104 let env, local = Env.FakeMembers.make
p env obj member_name
in
2105 let () = (match obj
with
2107 Typing_suggest.save_member member_name
env exp_real_type
ty2
2110 let env, ty = set_valid_rvalue
p env local ty2 in
2111 make_result env T.Any
ty
2112 | _, Class_get
(x, (_, y
)) ->
2113 let env, local = Env.FakeMembers.make_static
p env x y
in
2114 let env, ty3
= set_valid_rvalue
p env local ty2 in
2118 Typing_suggest.save_member y
env exp_real_type
ty2;
2120 make_result env T.Any ty3
2121 | _ -> make_result env T.Any
ty2
2123 | _, Array_get
((_, Lvar
(_, lvar
)) as shape
, ((Some
_) as e2)) ->
2124 let access_type = Typing_arrays.static_array_access
env e2 in
2125 (* In the case of an assignment of the form $x['new_field'] = ...;
2126 * $x could be a shape where the field 'new_field' is not yet defined.
2127 * When that is the case we want to add the field to its type.
2129 let env, _te
, shape_ty
= expr
env shape
in
2130 let env, shape_ty
= Typing_arrays.update_array_type_on_lvar_assignment
2131 p access_type env shape_ty
in
2132 let env, _ = set_valid_rvalue
p env lvar shape_ty
in
2133 (* We still need to call assign_simple in order to bind the freshly
2134 * created variable in added shape field. Moreover, it's needed because
2135 * shape_ty could be more than just a shape. It could be an unresolved
2136 * type where some elements are shapes and some others are not.
2138 assign_simple
p env e1
ty2
2140 Errors.this_lvalue
p;
2141 make_result env T.Any
(Reason.Rwitness
p, Terr
)
2142 | pref
, Unop
(Ast.Uref
, e1'
) ->
2143 (* references can be "lvalues" in foreach bindings *)
2144 if Env.is_strict
env then
2145 Errors.reference_expr pref
;
2146 assign
p env e1'
ty2
2148 assign_simple
p env e1
ty2
2150 and assign_simple
pos env e1
ty2 =
2151 let env, te1
, ty1 = lvalue
env e1
in
2152 let env, ty2 = check_valid_rvalue
pos env ty2 in
2153 let env, ty2 = TUtils.unresolved
env ty2 in
2154 let env = Type.sub_type
pos (Reason.URassign
) env ty2 ty1 in
2157 and array_field
env = function
2158 | Nast.AFvalue ve
->
2159 let env, tve
, tv
= expr
env ve
in
2160 let env, tv
= Typing_env.unbind
env tv
in
2161 env, (T.AFvalue tve
, None
, tv
)
2162 | Nast.AFkvalue
(ke
, ve
) ->
2163 let env, tke
, tk
= expr
env ke
in
2164 let env, tve
, tv
= expr
env ve
in
2165 let env, tv
= Typing_env.unbind
env tv
in
2166 env, (T.AFkvalue
(tke
, tve
), Some tk
, tv
)
2168 and array_value
env x =
2169 let env, te
, ty = expr
env x in
2170 let env, ty = Typing_env.unbind
env ty in
2173 and array_field_value
env = function
2175 | Nast.AFkvalue
(_, x) ->
2178 and array_field_key
env = function
2179 (* This shouldn't happen *)
2180 | Nast.AFvalue
(p, _) ->
2181 env, (T.make_implicitly_typed_expr
p T.Any
,
2182 (Reason.Rwitness
p, Tprim Tint
))
2183 | Nast.AFkvalue
(x, _) ->
2186 and akshape_field
env = function
2187 | Nast.AFkvalue
(k
, v
) ->
2188 let env, tek
, tk
= expr
env k
in
2189 let env, tk
= Typing_env.unbind
env tk
in
2190 let env, tk
= TUtils.unresolved
env tk
in
2191 let env, tev
, tv
= expr
env v
in
2192 let env, tv
= Typing_env.unbind
env tv
in
2193 let env, tv
= TUtils.unresolved
env tv
in
2195 match TUtils.shape_field_name
env Pos.none
(snd k
) with
2196 | Some
field_name -> field_name
2197 | None
-> assert false in (* Typing_arrays.is_shape_like_array
2198 * should have prevented this *)
2199 env, T.AFkvalue
(tek
, tev
), (field_name, (tk
, tv
))
2200 | Nast.AFvalue
_ -> assert false (* Typing_arrays.is_shape_like_array
2201 * should have prevented this *)
2203 and aktuple_afvalue
env v
=
2204 let env, tev
, tv
= expr
env v
in
2205 let env, tv
= Typing_env.unbind
env tv
in
2206 let env, ty = TUtils.unresolved
env tv
in
2209 and aktuple_field
env = function
2210 | Nast.AFvalue v
-> aktuple_afvalue
env v
2211 | Nast.AFkvalue
_ -> assert false (* check_consistent_fields
2212 * should have prevented this *)
2213 and check_parent_construct
pos env el uel env_parent
=
2214 let check_not_abstract = false in
2215 let env, env_parent
= Phase.localize_with_self
env env_parent
in
2216 let env, _tcid
, tel
, tuel
, parent
=
2217 new_object ~
check_not_abstract pos env CIparent el uel
in
2218 let env, _ = Type.unify
pos (Reason.URnone
) env env_parent parent
in
2219 env, tel
, tuel
, (Reason.Rwitness
pos, Tprim Tvoid
)
2221 and call_parent_construct
pos env el uel
=
2222 let parent = Env.get_parent
env in
2225 check_parent_construct
pos env el uel
parent
2242 ) -> (* continue here *)
2243 let default = env, [], [], (Reason.Rnone
, Tany
) in
2244 match Env.get_self
env with
2245 | _, Tclass
((_, self
), _) ->
2246 (match Env.get_class
env self
with
2247 | Some
({tc_kind
= Ast.Ctrait
; _}
2249 (match trait_most_concrete_req_class trait
env with
2250 | None
-> Errors.parent_in_trait
pos; default
2251 | Some
(_, parent_ty
) ->
2252 check_parent_construct
pos env el uel parent_ty
2255 if not self_tc
.tc_members_fully_known
2256 then () (* Don't know the hierarchy, assume it's correct *)
2257 else Errors.undefined_parent
pos;
2259 | None
-> assert false)
2260 | _, (Terr
| Tany
| Tmixed
| Tarraykind
_ | Toption
_
2261 | Tprim
_ | Tfun
_ | Ttuple
_ | Tshape
_ | Tvar
_
2262 | Tabstract
(_, _) | Tanon
(_, _) | Tunresolved
_ | Tobject
2264 Errors.parent_outside_class
pos;
2265 env, [], [], (Reason.Rnone
, Terr
)
2267 (* parent::method() in a class definition invokes the specific parent
2268 * version of the method ... it better be callable *)
2269 and check_abstract_parent_meth mname
pos fty =
2270 if is_abstract_ft
fty
2271 then Errors.parent_abstract_call mname
pos (Reason.to_pos
(fst
fty));
2274 and is_abstract_ft
fty = match fty with
2275 | _r, Tfun
{ ft_abstract
= true; _ } -> true
2276 | _r, (Terr
| Tany
| Tmixed
| Tarraykind
_ | Toption
_ | Tprim
_
2277 | Tvar
_ | Tfun
_ | Tclass
(_, _) | Tabstract
(_, _) | Ttuple
_
2278 | Tanon
_ | Tunresolved
_ | Tobject
| Tshape
_
2282 (* Depending on the kind of expression we are dealing with
2283 * The typing of call is different.
2285 and dispatch_call
p env call_type
(fpos
, fun_expr
as e
) el uel
=
2286 let make_call env te tel tuel
ty =
2287 env, T.make_typed_expr
p ty (T.Call
(call_type
, te
, tel
, tuel
)), ty in
2288 let make_call_special env id tel
ty =
2289 make_call env (T.make_implicitly_typed_expr fpos
(T.Id id
)) tel
[] ty in
2291 (* Special function `echo` *)
2292 | Id
((_, pseudo_func
) as id
) when pseudo_func
= SN.SpecialFunctions.echo
->
2293 let env, tel
, _ = exprs env el
in
2294 make_call_special env id tel
(Reason.Rwitness
p, Tprim Tvoid
)
2295 (* Special function `empty` *)
2296 | Id
((_, pseudo_func
) as id
) when pseudo_func
= SN.PseudoFunctions.empty
->
2297 let env, tel
, _ = exprs env el
in
2299 Errors.unpacking_disallowed_builtin_function
p pseudo_func
;
2300 if Env.is_strict
env then
2301 Errors.empty_in_strict
p;
2302 make_call_special env id tel
(Reason.Rwitness
p, Tprim Tbool
)
2303 (* Special function `isset` *)
2304 | Id
((_, pseudo_func
) as id
) when pseudo_func
= SN.PseudoFunctions.isset
->
2305 let env, tel
, _ = exprs env el
in
2307 Errors.unpacking_disallowed_builtin_function
p pseudo_func
;
2308 if Env.is_strict
env then
2309 Errors.isset_in_strict
p;
2310 make_call_special env id tel
(Reason.Rwitness
p, Tprim Tbool
)
2311 (* Special function `unset` *)
2312 | Id
((_, pseudo_func
) as id
) when pseudo_func
= SN.PseudoFunctions.unset
->
2313 let env, tel
, _ = exprs env el
in
2315 Errors.unpacking_disallowed_builtin_function
p pseudo_func
;
2316 let env = if Env.is_strict
env then
2318 | [(_, Array_get
(ea
, Some
_))], [] ->
2319 let env, _te
, ty = expr
env ea
in
2320 if List.exists ~f
:(fun super
-> SubType.is_sub_type
env ty super
) [
2321 (Reason.Rnone
, (Tclass
((Pos.none
, SN.Collections.cDict
),
2322 [(Reason.Rnone
, Tany
); (Reason.Rnone
, Tany
)])));
2323 (Reason.Rnone
, (Tclass
((Pos.none
, SN.Collections.cKeyset
),
2324 [(Reason.Rnone
, Tany
)])));
2325 (Reason.Rnone
, Tarraykind AKany
)
2328 let env, (r, ety
) = Env.expand_type
env ty in
2329 Errors.unset_nonidx_in_strict
2331 (Reason.to_string
("This is " ^
Typing_print.error ety
) r);
2334 | _ -> Errors.unset_nonidx_in_strict
p []; env)
2337 | [(p, Obj_get
(_, _, OG_nullsafe
))] ->
2339 Errors.nullsafe_property_write_context
p;
2340 make_call_special env id tel
(Reason.Rwitness
p, Terr
)
2343 make_call_special env id tel
(Reason.Rwitness
p, Tprim Tvoid
))
2344 (* Pseudo-function `get_called_class` *)
2345 | Id
(cp
, get_called_class
) when
2346 get_called_class
= SN.StdlibFunctions.get_called_class
2347 && el
= [] && uel
= [] ->
2348 (* get_called_class fetches the late-bound class *)
2349 if Env.is_outside_class
env then Errors.static_outside_class
p;
2350 class_const
env p (CIstatic
, (cp
, SN.Members.mClass
))
2351 (* Special function `array_filter` *)
2352 | Id
((_, array_filter
) as id
)
2353 when array_filter
= SN.StdlibFunctions.array_filter
&& el
<> [] && uel
= [] ->
2354 (* dispatch the call to typecheck the arguments *)
2355 let env, fty = fun_type_of_id
env id
in
2356 let env, tel
, tuel
, res
= call
p env fty el uel
in
2357 (* but ignore the result and overwrite it with custom return type *)
2358 let x = List.hd_exn el
in
2359 let env, _tx
, ty = expr
env x in
2360 let explain_array_filter (r, t
) =
2361 (Reason.Rarray_filter
(p, r), t
) in
2362 let get_value_type env tv
=
2364 if List.length el
> 1
2366 else non_null
env tv
in
2367 env, explain_array_filter tv
in
2368 let rec get_array_filter_return_type env ty =
2369 let env, ety
= Env.expand_type
env ty in
2371 | (_, Tarraykind
(AKany
| AKempty
)) as array_type
->
2373 | (_, Tarraykind
(AKtuple
_)) ->
2374 let env, ty = Typing_arrays.downcast_aktypes
env ty in
2375 get_array_filter_return_type env ty
2376 | (r, Tarraykind
(AKvec tv
)) ->
2377 let env, tv
= get_value_type env tv
in
2378 env, (r, Tarraykind
(AKvec tv
))
2379 | (r, Tunresolved
x) ->
2380 let env, x = List.map_env
env x get_array_filter_return_type in
2381 env, (r, Tunresolved
x)
2387 let tk, tv
= Env.fresh_type
(), Env.fresh_type
() in
2390 let keyed_container = (
2393 (Pos.none
, SN.Collections.cKeyedContainer
), [tk; tv
]
2396 let env = SubType.sub_type
env ety
keyed_container in
2397 let env, tv
= get_value_type env tv
in
2398 env, (r, Tarraykind
(AKmap
(
2399 (explain_array_filter tk),
2402 (fun _ -> Errors.try_
2407 (Pos.none
, SN.Collections.cContainer
), [tv
]
2410 let env = SubType.sub_type
env ety
container in
2411 let env, tv
= get_value_type env tv
in
2412 env, (r, Tarraykind
(AKmap
(
2413 (explain_array_filter (r, Tprim Tarraykey
)),
2415 (fun _ -> env, res
)))
2416 in let env, rty = get_array_filter_return_type env ty in
2417 make_call env (T.make_implicitly_typed_expr fpos
(T.Id id
)) tel tuel
rty
2418 (* Special function `type_structure` *)
2419 | Id
(p, type_structure
)
2420 when type_structure
= SN.StdlibFunctions.type_structure
2421 && (List.length el
= 2) && uel
= [] ->
2425 | p, Nast.String cst
->
2426 (* find the class constant implicitly defined by the typeconst *)
2427 let cid = (match e1
with
2428 | _, Class_const
(cid, (_, x))
2429 | _, Class_get
(cid, (_, x)) when x = SN.Members.mClass
-> cid
2430 | _ -> Nast.CIexpr e1
) in
2431 class_const ~incl_tc
:true env p (cid, cst
)
2433 Errors.illegal_type_structure
p "second argument is not a string";
2434 expr_error env Reason.Rnone
)
2435 | _ -> assert false)
2436 (* Special function `array_map` *)
2437 | Id
((_, array_map
) as x)
2438 when array_map
= SN.StdlibFunctions.array_map
&& el
<> [] && uel
= [] ->
2439 let env, fty = fun_type_of_id
env x in
2440 let env, fty = Env.expand_type
env fty in
2441 let env, fty = match fty, el
with
2442 | ((r_fty
, Tfun
fty), _::args
) when args
<> [] ->
2443 let arity = List.length args
in
2445 Builds a function with signature:
2447 function<T1, ..., Tn, Tr>(
2448 (function(T1, ..., Tn):Tr),
2454 where R is constructed by build_output_container applied to Tr
2456 let build_function build_output_container
=
2457 let vars = List.map args
(fun _ -> Env.fresh_type
()) in
2458 let tr = Env.fresh_type
() in
2462 ft_pos
= fty.ft_pos
;
2463 ft_deprecated
= None
;
2464 ft_abstract
= false;
2465 ft_arity
= Fstandard
(arity, arity);
2467 ft_where_constraints
= [];
2468 ft_params
= List.map
vars (fun x -> (None
, x));
2472 let containers = List.map
vars (fun var
->
2475 Tclass
((fty.ft_pos
, SN.Collections.cContainer
), [var
])
2479 (r_fty
, Tfun
{fty with
2480 ft_arity
= Fstandard
(arity+1, arity+1);
2481 ft_params
= f::containers;
2482 ft_ret
= build_output_container
tr;
2486 Takes a Container type and returns a function that can "pack" a type
2487 into an array of appropriate shape, preserving the key type, i.e.:
2488 array -> f, where f R = array
2489 array<X> -> f, where f R = array<R>
2490 array<X, Y> -> f, where f R = array<X, R>
2491 Vector<X> -> f where f R = array<R>
2492 KeyedContainer<X, Y> -> f, where f R = array<X, R>
2493 Container<X> -> f, where f R = array<arraykey, R>
2494 X -> f, where f R = Y
2496 let rec build_output_container
2497 (env:Env.env) (x:locl
ty) : (Env.env * (locl
ty -> locl
ty)) =
2498 let env, x = Env.expand_type
env x in (match x with
2499 | (_, Tarraykind
(AKany
| AKempty
)) as array_type
->
2500 env, (fun _ -> array_type
)
2501 | (_, Tarraykind
(AKtuple
_ )) ->
2502 let env, x = Typing_arrays.downcast_aktypes
env x in
2503 build_output_container env x
2504 | (r, Tarraykind AKvec
_) ->
2505 env, (fun tr -> (r, Tarraykind
(AKvec
(tr))) )
2506 | ((_, Tany
) as any
) ->
2508 | ((_, Terr
) as err
) ->
2510 | (r, Tunresolved
x) ->
2511 let env, x = List.map_env
env x build_output_container in
2512 env, (fun tr -> (r, Tunresolved
(List.map
x (fun f -> f tr))))
2514 let tk, tv
= Env.fresh_type
(), Env.fresh_type
() in
2515 let try_vector env =
2519 (fty.ft_pos
, SN.Collections.cConstVector
), [tv
]
2522 let env = SubType.sub_type
env x vector in
2523 env, (fun tr -> (r, Tarraykind
(
2526 let try_keyed_container env =
2527 let keyed_container = (
2530 (fty.ft_pos
, SN.Collections.cKeyedContainer
), [tk; tv
]
2533 let env = SubType.sub_type
env x keyed_container in
2534 env, (fun tr -> (r, Tarraykind
(AKmap
(
2538 let try_container env =
2542 (fty.ft_pos
, SN.Collections.cContainer
), [tv
]
2545 let env = SubType.sub_type
env x container in
2546 env, (fun tr -> (r, Tarraykind
(AKmap
(
2547 (r, Tprim Tarraykey
),
2552 (fun _ -> Errors.try_
2554 try_keyed_container env)
2555 (fun _ -> Errors.try_
2558 (fun _ -> env, (fun _ -> (Reason.Rwitness
p, Tany
)))))) in
2560 Single argument calls preserve the key type, multi argument
2561 calls always return an array<Tr>
2565 let env, _tx
, x = expr
env x in
2566 let env, output_container
= build_output_container env x in
2567 env, build_function output_container
2569 env, build_function (fun tr ->
2570 (r_fty
, Tarraykind
(AKvec
(tr)))))
2572 let env, tel
, _tuel
, ty = call
p env fty el
[] in
2573 make_call_special env x tel
ty
2574 (* Special function `idx` *)
2575 | Id
((_, idx
) as id
) when idx
= SN.FB.idx
->
2576 (* Directly call get_fun so that we can muck with the type before
2577 * instantiation -- much easier to work in terms of Tgeneric Tk/Tv than
2578 * trying to figure out which Tvar is which. *)
2579 (match Env.get_fun
env (snd id
) with
2581 let param1, (name2
, (r2
, _)), (name3
, (r3
, _)) =
2582 match fty.ft_params
with
2583 | [param1; param2
; param3
] -> param1, param2
, param3
2584 | _ -> assert false in
2585 let params, ret = match List.length el
with
2587 let param2 = (name2
, (r2
, Toption
(r2
, Tgeneric
"Tk"))) in
2588 let rret = fst
fty.ft_ret
in
2589 let ret = (rret, Toption
(rret, Tgeneric
"Tv")) in
2590 [param1; param2], ret
2592 let param2 = (name2
, (r2
, Tgeneric
"Tk")) in
2593 let param3 = (name3
, (r3
, Tgeneric
"Tv")) in
2594 let ret = (fst
fty.ft_ret
, Tgeneric
"Tv") in
2595 [param1; param2; param3], ret
2596 | _ -> fty.ft_params
, fty.ft_ret
in
2597 let fty = { fty with ft_params
= params; ft_ret
= ret } in
2598 let ety_env = Phase.env_with_self
env in
2599 let env, fty = Phase.localize_ft ~
ety_env env fty in
2600 let tfun = Reason.Rwitness
fty.ft_pos
, Tfun
fty in
2601 let env, tel
, _tuel
, ty = call
p env tfun el
[] in
2602 make_call_special env id tel
ty
2603 | None
-> unbound_name env id
)
2605 (* Special function `Shapes::idx` *)
2606 | Class_const
(CI
((_, shapes
), _) as class_id
, ((_, idx
) as method_id
))
2607 when shapes
= SN.Shapes.cShapes
&& idx
= SN.Shapes.idx
->
2608 overload_function
p env class_id method_id el uel
2609 begin fun env fty res el
-> match el
with
2611 let env, _ts
, shape_ty
= expr
env shape
in
2612 Typing_shapes.idx
env fty shape_ty field None
2613 | [shape
; field
; default] ->
2614 let env, _ts
, shape_ty
= expr
env shape
in
2615 let env, _td
, default_ty
= expr
env default in
2616 Typing_shapes.idx
env fty shape_ty field
2617 (Some
((fst
default), default_ty
))
2620 (* Special function `Shapes::keyExists` *)
2621 | Class_const
(CI
((_, shapes
), _) as class_id
, ((_, key_exists
) as method_id
))
2622 when shapes
= SN.Shapes.cShapes
&& key_exists
= SN.Shapes.keyExists
->
2623 overload_function
p env class_id method_id el uel
2624 begin fun env fty res el
-> match el
with
2626 let env, _te
, shape_ty
= expr
env shape
in
2627 (* try accessing the field, to verify existence, but ignore
2628 * the returned type and keep the one coming from function
2629 * return type hint *)
2630 let env, _ = Typing_shapes.idx
env fty shape_ty field None
in
2634 (* Special function `Shapes::removeKey` *)
2635 | Class_const
(CI
((_, shapes
), _) as class_id
, ((_, remove_key
) as method_id
))
2636 when shapes
= SN.Shapes.cShapes
&& remove_key
= SN.Shapes.removeKey
->
2637 overload_function
p env class_id method_id el uel
2638 begin fun env _ res el
-> match el
with
2639 | [shape
; field
] -> begin match shape
with
2640 | (_, Lvar
(_, lvar
)) ->
2641 let env, _te
, shape_ty
= expr
env shape
in
2643 Typing_shapes.remove_key
p env shape_ty field
in
2644 let env, _ = set_valid_rvalue
p env lvar shape_ty
in
2647 Errors.invalid_shape_remove_key
(fst shape
);
2652 (* Special function `Shapes::toArray` *)
2653 | Class_const
(CI
((_, shapes
), _) as class_id
, ((_, to_array
) as method_id
))
2654 when shapes
= SN.Shapes.cShapes
&& to_array
= SN.Shapes.toArray
->
2655 overload_function
p env class_id method_id el uel
2656 begin fun env _ res el
-> match el
with
2658 let env, _te
, shape_ty
= expr
env shape
in
2659 Typing_shapes.to_array
env shape_ty res
2663 (* Special function `parent::__construct` *)
2664 | Class_const
(CIparent
, ((callee_pos
, construct
) as id
))
2665 when construct
= SN.Members.__construct
->
2666 Typing_hooks.dispatch_parent_construct_hook
env callee_pos
;
2667 let env, tel
, tuel
, ty = call_parent_construct
p env el uel
in
2668 make_call env (T.make_implicitly_typed_expr fpos
2669 (T.Class_const
(T.CIparent
, id
))) tel tuel
ty
2671 (* Calling parent method *)
2672 | Class_const
(CIparent
, m
) ->
2673 let env, _te
, ty1 = static_class_id
p env CIparent
in
2674 if Env.is_static
env
2676 (* in static context, you can only call parent::foo() on static
2679 class_get ~is_method
:true ~is_const
:false env ty1 m CIparent
in
2680 let fty = check_abstract_parent_meth
(snd m
) p fty in
2681 let env, tel
, tuel
, ty = call
p env fty el uel
in
2682 make_call env (T.make_typed_expr fpos
fty
2683 (T.Class_const
(T.CIparent
, m
))) tel tuel
ty
2686 (* in instance context, you can call parent:foo() on static
2687 * methods as well as instance methods *)
2688 if not
(class_contains_smethod
env ty1 m
)
2690 (* parent::nonStaticFunc() is really weird. It's calling a method
2691 * defined on the parent class, but $this is still the child class.
2692 * We can deal with this by hijacking the continuation that
2693 * calculates the SN.Typehints.this type *)
2694 let this_ty = ExprDepTy.make
env CIstatic
2695 (Reason.Rwitness fpos
, TUtils.this_of
(Env.get_self
env)) in
2696 let k_lhs _ = this_ty in
2697 let env, method_
, _ =
2698 obj_get_ ~is_method
:true ~
nullsafe:None
env ty1 CIparent m
2699 begin fun (env, fty, _) ->
2700 let fty = check_abstract_parent_meth
(snd m
) p fty in
2701 let env, _tel
, _tuel
, method_
= call
p env fty el uel
in
2706 make_call env (T.make_typed_expr fpos
this_ty
2707 (T.Class_const
(T.CIparent
, m
))) [] [] method_
2710 class_get ~is_method
:true ~is_const
:false env ty1 m CIparent
in
2711 let fty = check_abstract_parent_meth
(snd m
) p fty in
2712 let env, tel
, tuel
, ty = call
p env fty el uel
in
2713 make_call env (T.make_typed_expr fpos
fty
2714 (T.Class_const
(T.CIparent
, m
))) tel tuel
ty
2716 (* Call class method *)
2717 | Class_const
(e1
, m
) ->
2718 TUtils.process_static_find_ref e1 m
;
2719 let env, te1
, ty1 = static_class_id
p env e1
in
2721 class_get ~is_method
:true ~is_const
:false env ty1 m e1
in
2722 let () = match e1
with
2723 | CIself
when is_abstract_ft
fty ->
2724 (match Env.get_self
env with
2725 | _, Tclass
((_, self
), _) ->
2726 (* at runtime, self:: in a trait is a call to whatever
2727 * self:: is in the context of the non-trait "use"-ing
2728 * the trait's code *)
2729 (match Env.get_class
env self
with
2730 | Some
{ tc_kind
= Ast.Ctrait
; _ } -> ()
2731 | _ -> Errors.self_abstract_call
(snd m
) p (Reason.to_pos
(fst
fty))
2734 | CI
(c, _) when is_abstract_ft
fty ->
2735 Errors.classname_abstract_call
(snd
c) (snd m
) p (Reason.to_pos
(fst
fty))
2737 let env, tel
, tuel
, ty = call
p env fty el uel
in
2738 make_call env (T.make_typed_expr fpos
fty
2739 (T.Class_const
(te1
, m
))) tel tuel
ty
2741 (* Call instance method *)
2742 | Obj_get
(e1
, (pos_id
, Id m
), nullflavor
) ->
2743 let is_method = call_type
= Cnormal
in
2744 let env, te1
, ty1 = expr
env e1
in
2746 (match nullflavor
with
2747 | OG_nullthrows
-> None
2748 | OG_nullsafe
-> Some
p
2750 let fn = (fun (env, fty, _) ->
2751 let env, _tel
, _tuel
, method_
= call
p env fty el uel
in
2752 env, method_
, None
) in
2753 let env, ty = obj_get ~
is_method ~
nullsafe env ty1 (CIexpr e1
) m
fn in
2754 make_call env (T.make_implicitly_typed_expr fpos
(T.Obj_get
(te1
,
2755 T.make_implicitly_typed_expr pos_id
(T.Id m
), nullflavor
))) [] [] ty
2757 (* Function invocation *)
2759 Typing_hooks.dispatch_id_hook
x env;
2760 let env, fty = fun_type_of_id
env x in
2761 let env, tel
, tuel
, ty = call
p env fty el uel
in
2762 make_call env (T.make_typed_expr fpos
fty (T.Fun_id
x)) tel tuel
ty
2764 Typing_hooks.dispatch_id_hook
x env;
2765 let env, fty = fun_type_of_id
env x in
2766 let env, tel
, tuel
, ty = call
p env fty el uel
in
2767 make_call env (T.make_typed_expr fpos
fty (T.Id
x)) tel tuel
ty
2769 let env, te
, fty = expr
env e
in
2770 let env, tel
, tuel
, ty = call
p env fty el uel
in
2771 make_call env te tel tuel
ty
2773 and fun_type_of_id
env x =
2774 Typing_hooks.dispatch_fun_id_hook
x;
2776 match Env.get_fun
env (snd
x) with
2777 | None
-> let env, _, ty = unbound_name env x in env, ty
2779 let ety_env = Phase.env_with_self
env in
2780 let env, fty = Phase.localize_ft ~
ety_env env fty in
2781 env, (Reason.Rwitness
fty.ft_pos
, Tfun
fty)
2785 (*****************************************************************************)
2786 (* Function type-checking expressions accessing an array (example: $x[...]).
2787 * The parameter is_lvalue is true when the expression is on the left hand
2788 * side of an assignment (example: $x[...] = 0).
2790 (*****************************************************************************)
2791 and array_get
is_lvalue p env ty1 e2 ty2 =
2792 (* This is a little weird -- we enforce the right arity when you use certain
2793 * collections, even in partial mode (where normally completely omitting the
2794 * type parameter list is admitted). Basically the "omit type parameter"
2795 * hole was for compatibility with certain interfaces like ArrayAccess, not
2796 * for collections! But it's hard to go back on now, so since we've always
2797 * errored (with an inscrutable error message) when you try to actually use
2798 * a collection with omitted type parameters, we can continue to error and
2799 * give a more useful error message. *)
2800 let env, ety1
= Env.expand_type
env ty1 in
2801 let arity_error (_, name) =
2802 Errors.array_get_arity
p name (Reason.to_pos
(fst ety1
)) in
2804 | Tunresolved
tyl ->
2805 let env, tyl = List.map_env
env tyl begin fun env ty1 ->
2806 array_get
is_lvalue p env ty1 e2 ty2
2808 env, (fst ety1
, Tunresolved
tyl)
2809 | Tarraykind
(AKvarray
ty | AKvec
ty) ->
2810 let ty1 = Reason.Ridx
(fst
e2, fst ety1
), Tprim Tint
in
2811 let env = Type.sub_type
p Reason.index_array
env ty2 ty1 in
2813 | Tclass
((_, cn
) as id
, argl
)
2814 when cn
= SN.Collections.cVector
2815 || cn
= SN.Collections.cVec
->
2816 let ty = match argl
with
2818 | _ -> arity_error id
; err_witness p in
2819 let ty1 = Reason.Ridx_vector
(fst
e2), Tprim Tint
in
2820 let env = Type.sub_type
p (Reason.index_class cn
) env ty2 ty1 in
2822 | Tclass
((_, cn
) as id
, argl
)
2823 when cn
= SN.Collections.cMap
2824 || cn
= SN.Collections.cStableMap
2825 || cn
= SN.Collections.cDict
2826 || cn
= SN.Collections.cKeyset
->
2827 if cn
= SN.Collections.cKeyset
&& is_lvalue then begin
2828 Errors.keyset_set
p (Reason.to_pos
(fst ety1
));
2829 env, (Reason.Rwitness
p, Terr
)
2831 let (k
, v
) = match argl
with
2832 | [t
] when cn
= SN.Collections.cKeyset
-> (t
, t
)
2833 | [k
; v
] when cn
<> SN.Collections.cKeyset
-> (k
, v
)
2836 let any = err_witness p in
2839 let env, ty2 = TUtils.unresolved
env ty2 in
2840 let env = Type.sub_type
p (Reason.index_class cn
) env ty2 k
in
2842 (* Certain container/collection types are intended to be immutable/const,
2843 * thus they should never appear as a lvalue when indexing i.e.
2845 * $x[0] = 100; // ERROR
2848 | Tclass
((_, cn
) as id
, argl
)
2849 when cn
= SN.Collections.cConstMap
2850 || cn
= SN.Collections.cImmMap
2851 || cn
= SN.Collections.cIndexish
2852 || cn
= SN.Collections.cKeyedContainer
->
2854 error_const_mutation
env p ety1
2856 let (k
, v
) = match argl
with
2860 let any = err_witness p in
2863 let env = Type.sub_type
p (Reason.index_class cn
) env ty2 k
in
2865 | Tclass
((_, cn
) as id
, argl
)
2866 when not
is_lvalue &&
2867 (cn
= SN.Collections.cConstVector
|| cn
= SN.Collections.cImmVector
) ->
2868 let ty = match argl
with
2870 | _ -> arity_error id
; err_witness p in
2871 let ty1 = Reason.Ridx
(fst
e2, fst ety1
), Tprim Tint
in
2872 let env = Type.sub_type
p (Reason.index_class cn
) env ty2 ty1 in
2874 | Tclass
((_, cn
), _)
2876 (cn
= SN.Collections.cConstVector
|| cn
= SN.Collections.cImmVector
) ->
2877 error_const_mutation
env p ety1
2878 | Tarraykind
(AKdarray
(k
, v
) | AKmap
(k
, v
)) ->
2879 let env, ty2 = TUtils.unresolved
env ty2 in
2880 let env = Type.sub_type
p Reason.index_array
env ty2 k
in
2882 | Tarraykind
((AKshape
_ | AKtuple
_) as akind
) ->
2883 let key = Typing_arrays.static_array_access
env (Some
e2) in
2884 let env, result
= match key, akind
with
2885 | Typing_arrays.AKtuple_index index
, AKtuple fields
->
2886 begin match IMap.get index fields
with
2888 let ty1 = Reason.Ridx
(fst
e2, fst ety1
), Tprim Tint
in
2889 let env = Type.sub_type
p Reason.index_array
env ty2 ty1 in
2893 | Typing_arrays.AKshape_key
field_name, AKshape fdm
->
2894 begin match Nast.ShapeMap.get
field_name fdm
with
2896 let env, ty2 = TUtils.unresolved
env ty2 in
2897 let env = Type.sub_type
p Reason.index_array
env ty2 k
in
2902 begin match result
with
2903 | Some
ty -> env, ty
2905 (* Key is dynamic, or static and not in the array - treat it as
2906 regular map or vec like array *)
2907 let env, ty1 = Typing_arrays.downcast_aktypes
env ety1
in
2908 array_get
is_lvalue p env ty1 e2 ty2
2910 | Terr
-> env, (Reason.Rnone
, Terr
)
2911 | Tany
| Tarraykind
(AKany
| AKempty
) ->
2912 env, (Reason.Rnone
, Tany
)
2914 let ty = Reason.Rwitness
p, Tprim Tstring
in
2915 let int = Reason.Ridx
(fst
e2, fst ety1
), Tprim Tint
in
2916 let env = Type.sub_type
p Reason.index_array
env ty2 int in
2922 let idx = int_of_string
(snd n
) in
2923 let nth = List.nth_exn
tyl idx in
2926 Errors.typing_error
p (Reason.string_of_ureason
Reason.index_tuple
);
2927 env, (Reason.Rwitness
p, Terr
)
2930 Errors.typing_error
p (Reason.string_of_ureason
Reason.URtuple_access
);
2931 env, (Reason.Rwitness
p, Terr
)
2933 | Tclass
((_, cn
) as id
, argl
) when cn
= SN.Collections.cPair
->
2934 let (ty1, ty2) = match argl
with
2935 | [ty1; ty2] -> (ty1, ty2)
2938 let any = err_witness p in
2944 let idx = int_of_string
(snd n
) in
2945 let nth = List.nth_exn
[ty1; ty2] idx in
2948 Errors.typing_error
p @@
2949 Reason.string_of_ureason
(Reason.index_class cn
);
2950 env, (Reason.Rwitness
p, Terr
)
2953 Errors.typing_error
p (Reason.string_of_ureason
Reason.URpair_access
);
2954 env, (Reason.Rwitness
p, Terr
)
2956 | Tshape
(_, fdm
) ->
2958 (match TUtils.shape_field_name
env p e2'
with
2960 (* there was already an error in shape_field name,
2961 don't report another one for a missing field *)
2962 env, (Reason.Rwitness
p, Terr
)
2963 | Some field
-> (match ShapeMap.get field fdm
with
2965 Errors.undefined_field
2966 p (TUtils.get_printable_shape_field_name field
);
2967 env, (Reason.Rwitness
p, Terr
)
2968 | Some
{ sft_optional
= true; _ } ->
2969 let declared_field =
2971 ~
f:(fun x -> Ast.ShapeField.compare field
x = 0)
2972 (ShapeMap.keys fdm
) in
2973 let declaration_pos = match declared_field with
2974 | Ast.SFlit
(p, _) | Ast.SFclass_const
((p, _), _) -> p in
2975 Errors.array_get_with_optional_field
2978 (TUtils.get_printable_shape_field_name field
);
2979 env, (Reason.Rwitness
p, Terr
)
2980 | Some
{ sft_optional
= false; sft_ty
} -> env, sft_ty
)
2983 Errors.null_container
p
2985 "This is what makes me believe it can be null"
2988 env, (Reason.Rwitness
p, Terr
)
2990 if Env.is_strict
env
2991 then error_array
env p ety1
2992 else env, (Reason.Rnone
, Tany
)
2993 | Tabstract
(AKnewtype
(ts
, [ty]), Some
(r, Tshape
(fk
, fields
)))
2994 when ts
= SN.FB.cTypeStructure
->
2995 let env, fields
= TS.transform_shapemap
env ty fields
in
2996 let ty = r, Tshape
(fk
, fields
) in
2997 array_get
is_lvalue p env ty e2 ty2
3000 try_over_concrete_supertypes env ety1
3002 array_get
is_lvalue p env ty e2 ty2
3004 begin match resl with
3006 | _ -> error_array
env p ety1
3008 | Tmixed
| Tprim
_ | Tvar
_ | Tfun
_
3009 | Tclass
(_, _) | Tanon
(_, _) ->
3010 error_array
env p ety1
3012 and array_append
p env ty1 =
3013 let env, ty1 = TUtils.fold_unresolved
env ty1 in
3014 let resl = try_over_concrete_supertypes env ty1
3019 | Tany
| Tarraykind
(AKany
| AKempty
) ->
3020 env, (Reason.Rnone
, Tany
)
3023 env, (Reason.Rnone
, Terr
)
3025 | Tclass
((_, n
), [ty])
3026 when n
= SN.Collections.cVector
3027 || n
= SN.Collections.cSet
3028 || n
= SN.Collections.cVec
3029 || n
= SN.Collections.cKeyset
->
3031 | Tclass
((_, n
), [])
3032 when n
= SN.Collections.cVector
|| n
= SN.Collections.cSet
->
3033 (* Handle the case where "Vector" or "Set" was used as a typehint
3034 without type parameters *)
3035 env, (Reason.Rnone
, Tany
)
3036 | Tclass
((_, n
), [tkey
; tvalue
]) when n
= SN.Collections.cMap
->
3037 (* You can append a pair to a map *)
3038 env, (Reason.Rmap_append
p, Tclass
((p, SN.Collections.cPair
),
3040 | Tclass
((_, n
), []) when n
= SN.Collections.cMap
->
3041 (* Handle the case where "Map" was used as a typehint without
3043 env, (Reason.Rmap_append
p,
3044 Tclass
((p, SN.Collections.cPair
), []))
3045 | Tarraykind
(AKvec
ty) ->
3048 if Env.is_strict
env
3049 then error_array_append
env p ty1
3050 else env, (Reason.Rnone
, Tany
)
3051 | Tmixed
| Tarraykind
_ | Toption
_ | Tprim
_
3052 | Tvar
_ | Tfun
_ | Tclass
(_, _) | Ttuple
_
3053 | Tanon
(_, _) | Tunresolved
_ | Tshape
_ | Tabstract
_ ->
3054 error_array_append
env p ty1
3058 | _ -> error_array_append
env p ty1
3061 and error_array
env p (r, ty) =
3062 Errors.array_access
p (Reason.to_pos
r) (Typing_print.error
ty);
3065 and error_array_append
env p (r, ty) =
3066 Errors.array_append
p (Reason.to_pos
r) (Typing_print.error
ty);
3069 and error_const_mutation
env p (r, ty) =
3070 Errors.const_mutation
p (Reason.to_pos
r) (Typing_print.error
ty);
3074 * Checks if a class (given by cty) contains a given static method.
3076 * We could refactor this + class_get
3078 and class_contains_smethod
env cty
(_pos
, mid
) =
3079 let lookup_member ty =
3081 | _, Tclass
((_, c), _) ->
3082 (match Env.get_class
env c with
3085 Option.is_some
@@ Env.get_static_member
true env class_ mid
3088 let _env, tyl = TUtils.get_concrete_supertypes
env cty
in
3089 List.exists
tyl ~
f:lookup_member
3091 and class_get ~
is_method ~is_const ?
(incl_tc
=false) env cty
(p, mid
) cid =
3094 this_for_method
env cid cty
3098 type_expansions
= [];
3100 substs
= SMap.empty
;
3101 from_class
= Some
cid;
3103 class_get_ ~
is_method ~is_const ~
ety_env ~incl_tc
env cid cty
(p, mid
)
3105 and class_get_ ~
is_method ~is_const ~
ety_env ?
(incl_tc
=false) env cid cty
3107 let env, cty
= Env.expand_type
env cty
in
3109 | _, Tany
-> env, (Reason.Rnone
, Tany
), None
3110 | _, Terr
-> env, err_none, None
3111 | _, Tunresolved
tyl ->
3112 let env, tyl = List.map_env
env tyl begin fun env ty ->
3114 class_get_ ~
is_method ~is_const ~
ety_env ~incl_tc
env cid ty (p, mid
)
3117 let env, method_
= TUtils.in_var
env (fst cty
, Tunresolved
tyl) in
3121 begin match TUtils.get_concrete_supertypes
env cty
with
3123 class_get_ ~
is_method ~is_const ~
ety_env ~incl_tc
env cid cty
(p, mid
)
3125 env, (Reason.Rnone
, Tany
), None
3127 | _, Tclass
((_, c), paraml
) ->
3128 let class_ = Env.get_class
env c in
3130 | None
-> env, (Reason.Rnone
, Tany
), None
3132 Typing_hooks.dispatch_smethod_hook
class_ paraml
(p, mid
) env
3133 ety_env.from_class ~
is_method ~is_const
;
3134 (* We need to instantiate generic parameters in the method signature *)
3137 substs
= Subst.make
class_.tc_tparams paraml
} in
3138 if is_const
then begin
3140 if incl_tc
then Env.get_const
env class_ mid
else
3141 match Env.get_typeconst
env class_ mid
with
3143 Errors.illegal_typeconst_direct_access
p;
3146 Env.get_const
env class_ mid
3150 smember_not_found
p ~is_const ~
is_method class_ mid
;
3151 env, (Reason.Rnone
, Terr
), None
3152 | Some
{ cc_type
; cc_abstract
; cc_pos
; _ } ->
3153 let env, cc_type
= Phase.localize ~
ety_env env cc_type
in
3156 then Some
(cc_pos
, class_.tc_name ^
"::" ^ mid
)
3159 let smethod = Env.get_static_member
is_method env class_ mid
in
3162 (match Env.get_static_member
is_method env class_
3163 SN.Members.__callStatic
with
3165 smember_not_found
p ~is_const ~
is_method class_ mid
;
3166 env, (Reason.Rnone
, Terr
), None
3167 | Some
{ce_visibility
= vis
; ce_type
= lazy (r, Tfun
ft); _} ->
3168 let p_vis = Reason.to_pos
r in
3169 TVis.check_class_access
p env (p_vis, vis
) cid class_;
3170 let env, ft = Phase.localize_ft ~
ety_env env ft in
3172 ft_arity
= Fellipsis
0;
3173 ft_tparams
= []; ft_params
= [];
3175 env, (r, Tfun
ft), None
3176 | _ -> assert false)
3177 | Some
{ ce_visibility
= vis
; ce_type
= lazy method_
; _ } ->
3178 let p_vis = Reason.to_pos
(fst method_
) in
3179 TVis.check_class_access
p env (p_vis, vis
) cid class_;
3181 Phase.localize ~
ety_env env method_
in
3185 | _, (Tmixed
| Tarraykind
_ | Toption
_
3186 | Tprim
_ | Tvar
_ | Tfun
_ | Ttuple
_ | Tanon
(_, _) | Tobject
3188 (* should never happen; static_class_id takes care of these *)
3189 env, (Reason.Rnone
, Tany
), None
3191 and smember_not_found
pos ~is_const ~
is_method class_ member_name
=
3193 if is_const
then `class_constant
3194 else if is_method then `static_method
3195 else `class_variable
in
3197 let cid = (class_.tc_pos
, class_.tc_name
) in
3198 Errors.smember_not_found
kind pos cid member_name hint
3200 match Env.suggest_static_member
is_method class_ member_name
with
3202 (match Env.suggest_member
is_method class_ member_name
with
3203 | None
when not
class_.tc_members_fully_known
->
3204 (* no error in this case ... the member might be present
3205 * in one of the parents of class_ that the typing cannot see *)
3210 error (`closest
(pos2
, v
))
3213 error (`did_you_mean
(pos2
, v
))
3215 and member_not_found
pos ~
is_method class_ member_name
r =
3216 let kind = if is_method then `method_
else `member
in
3217 let cid = class_.tc_pos
, class_.tc_name
in
3218 let reason = Reason.to_string
3219 ("This is why I think it is an object of type "^strip_ns
class_.tc_name
) r
3222 Errors.member_not_found
kind pos cid member_name hint
reason in
3223 match Env.suggest_member
is_method class_ member_name
with
3225 (match Env.suggest_static_member
is_method class_ member_name
with
3226 | None
when not
class_.tc_members_fully_known
->
3227 (* no error in this case ... the member might be present
3228 * in one of the parents of class_ that the typing cannot see *)
3232 | Some
(def_pos
, v
) ->
3233 error (`closest
(def_pos
, v
))
3235 | Some
(def_pos
, v
) ->
3236 error (`did_you_mean
(def_pos
, v
))
3238 (* The type of the object member is passed into the continuation k. This is
3239 * useful for typing nullsafed method calls. Consider `$x?->f()`: obj_get will
3240 * pass `k` the type of f, and `k` will typecheck the method call and return
3241 * the method's return type. obj_get then wraps that type in a Toption. *)
3242 and obj_get ~
is_method ~
nullsafe env ty1 cid id k
=
3245 | Some
p when not
(type_could_be_null
env ty1) ->
3246 let env, (r, _) = Env.expand_type
env ty1 in
3247 Errors.nullsafe_not_needed
p
3249 "This is what makes me believe it cannot be null" r);
3252 let env, method_
, _ =
3253 obj_get_with_visibility ~
is_method ~
nullsafe env ty1 cid id k
in
3256 and obj_get_with_visibility ~
is_method ~
nullsafe env ty1
3258 obj_get_ ~
is_method ~
nullsafe env ty1 cid id k
(fun ty -> ty)
3260 (* We know that the receiver is a concrete class: not a generic with
3261 * bounds, or a Tunresolved. *)
3262 and obj_get_concrete_ty ~
is_method env concrete_ty class_id
3263 (id_pos
, id_str
as id
) k_lhs =
3264 let default () = env, (Reason.Rnone
, Tany
), None
in
3265 match concrete_ty
with
3266 | (r, Tclass
(x, paraml
)) ->
3268 match Env.get_class
env (snd
x) with
3272 | Some class_info
when not
is_method
3273 && not
(Env.is_strict
env)
3274 && class_info
.tc_name
= SN.Classes.cStdClass
->
3277 | Some class_info
->
3279 if List.length
paraml = 0
3280 then List.map class_info
.tc_tparams
3281 (fun _ -> Reason.Rwitness id_pos
, Tany
)
3283 let member_info = Env.get_member
is_method env class_info id_str
in
3284 Typing_hooks.dispatch_cmethod_hook class_info
paraml id
env None
3287 match member_info with
3288 | None
when not
is_method ->
3289 if not
(SN.Members.is_special_xhp_attribute id_str
)
3290 then member_not_found id_pos ~
is_method class_info id_str
r;
3295 match Env.get_member
is_method env class_info
SN.Members.__call
with
3297 member_not_found id_pos ~
is_method class_info id_str
r;
3300 | Some
{ce_visibility
= vis
; ce_type
= lazy (r, Tfun
ft); _} ->
3301 let mem_pos = Reason.to_pos
r in
3302 TVis.check_obj_access id_pos
env (mem_pos, vis
);
3304 (* the return type of __call can depend on the
3305 * class params or be this *)
3306 let this_ty = k_lhs (r, (Tclass
(x, paraml))) in
3308 type_expansions
= [];
3310 substs
= Subst.make class_info
.tc_tparams
paraml;
3311 from_class
= Some class_id
;
3313 let env, ft = Phase.localize_ft ~
ety_env env ft in
3315 (* we change the params of the underlying
3316 * declaration to act as a variadic function
3317 * ... this transform cannot be done when
3318 * processing the declaration of call because
3319 * direct calls to $inst->__call are also
3322 ft_arity
= Fellipsis
0; ft_tparams
= []; ft_params
= []; } in
3324 let member_ty = (r, Tfun
ft) in
3325 env, member_ty, Some
(mem_pos, vis
)
3330 | Some
({ce_visibility
= vis
; ce_type
= lazy member_
; _ } as member_ce
) ->
3331 let mem_pos = Reason.to_pos
(fst member_
) in
3332 TVis.check_obj_access id_pos
env (mem_pos, vis
);
3333 let member_ty = Typing_enum.member_type
env member_ce
in
3334 let this_ty = k_lhs (r, (Tclass
(x, paraml))) in
3336 type_expansions
= [];
3338 substs
= Subst.make class_info
.tc_tparams
paraml;
3339 from_class
= Some class_id
;
3341 let env, member_ty = Phase.localize ~
ety_env env member_ty in
3342 env, member_ty, Some
(mem_pos, vis
)
3351 Errors.non_object_member
3352 id_str id_pos
(Typing_print.error (snd concrete_ty
))
3353 (Reason.to_pos
(fst concrete_ty
));
3357 (* k_lhs takes the type of the object receiver *)
3358 and obj_get_ ~
is_method ~
nullsafe env ty1 cid (id_pos
, id_str
as id
) k
k_lhs =
3359 let env, ety1
= Env.expand_type
env ty1 in
3361 | _, Tunresolved
tyl ->
3362 let (env, vis
), tyl = List.map_env
(env, None
) tyl
3363 begin fun (env, vis
) ty ->
3365 obj_get_ ~
is_method ~
nullsafe env ty cid id k
k_lhs in
3366 (* There is one special case where we need to expose the
3367 * visibility outside of obj_get (checkout inst_meth special
3369 * We keep a witness of the "most restrictive" visibility
3370 * we encountered (position + visibility), to be able to
3371 * special case inst_meth.
3373 let vis = TVis.min_vis_opt
vis vis'
in
3376 let env, method_
= TUtils.in_var
env (fst ety1
, Tunresolved
(tyl)) in
3379 | p'
, (Tabstract
(ak
, Some
ty)) ->
3380 let k_lhs'
ty = match ak
with
3381 | AKnewtype
(_, _) -> k_lhs ty
3382 | _ -> k_lhs (p'
, Tabstract
(ak
, Some
ty)) in
3383 obj_get_ ~
is_method ~
nullsafe env ty cid id k
k_lhs'
3385 | p'
, (Tabstract
(ak
,_)) ->
3387 try_over_concrete_supertypes env ety1
3389 (* We probably don't want to rewrap new types for the 'this' closure *)
3390 (* TODO AKENN: we shouldn't refine constraints by changing
3391 * the type like this *)
3392 let k_lhs'
ty = match ak
with
3393 | AKnewtype
(_, _) -> k_lhs ty
3394 | _ -> k_lhs (p'
, Tabstract
(ak
, Some
ty)) in
3395 obj_get_concrete_ty ~
is_method env ty cid id
k_lhs'
3397 begin match resl with
3399 Errors.non_object_member
3400 id_str id_pos
(Typing_print.error (snd ety1
))
3401 (Reason.to_pos
(fst ety1
));
3402 k
(env, err_none, None
)
3404 | ((_env, (_, ty), _vis
) as res
)::rest ->
3405 if List.exists
rest (fun (_, (_,ty'
), _) -> ty'
<> ty)
3408 Errors.ambiguous_member
3409 id_str id_pos
(Typing_print.error (snd ety1
))
3410 (Reason.to_pos
(fst ety1
));
3411 k
(env, err_none, None
)
3416 | _, Toption
ty -> begin match nullsafe with
3418 let k'
(env, fty, x) = begin
3419 let env, method_
, x = k (env, fty, x) in
3420 let env, method_
= non_null
env method_
in
3421 env, (Reason.Rnullsafe_op
p1, Toption method_
), x
3423 obj_get_ ~
is_method ~
nullsafe env ty cid id
k'
k_lhs
3425 Errors.null_member id_str id_pos
3427 "This is what makes me believe it can be null"
3430 k (env, (fst ety1
, Terr
), None
)
3433 k (obj_get_concrete_ty ~
is_method env ety1
cid id
k_lhs)
3436 (* Return true if the type ty1 contains the null value *)
3437 and type_could_be_null
env ty1 =
3438 let _, tyl = TUtils.get_concrete_supertypes
env ty1 in
3442 Toption
_ | Tunresolved
_ | Tmixed
| Tany
| Terr
-> true
3443 | Tarraykind
_ | Tprim
_ | Tvar
_ | Tfun
_ | Tabstract
_
3444 | Tclass
(_, _) | Ttuple
_ | Tanon
(_, _) | Tobject
3445 | Tshape
_ -> false)
3447 and class_id_for_new
p env cid =
3448 let env, te
, ty = static_class_id
p env cid in
3449 (* Need to deal with union case *)
3450 let rec get_info res
tyl =
3452 | [] -> env, te
, res
3455 | Tunresolved
tyl'
->
3456 get_info res
(tyl'
@ tyl)
3458 (* Instantiation on an abstract class (e.g. from classname<T>) is
3459 * via the base type (to check constructor args), but the actual
3460 * type `ty` must be preserved. *)
3461 match TUtils.get_base_type
env ty with
3462 | _, Tclass
(sid
, _) ->
3464 let class_ = Env.get_class
env (snd sid
) in
3466 | None
-> get_info res
tyl
3467 | Some class_info
-> get_info ((sid
, class_info
, ty)::res
) tyl
3469 | _, (Tany
| Terr
| Tmixed
| Tarraykind
_ | Toption
_ | Tprim
_
3470 | Tvar
_ | Tfun
_ | Tabstract
(_, _) | Ttuple
_ | Tanon
(_, _)
3471 | Tunresolved
_ | Tobject
| Tshape
_) -> get_info res
tyl in
3474 (* To be a valid trait declaration, all of its 'require extends' must
3475 * match; since there's no multiple inheritance, it follows that all of
3476 * the 'require extends' must belong to the same inheritance hierarchy
3477 * and one of them should be the child of all the others *)
3478 and trait_most_concrete_req_class trait
env =
3479 List.fold_left trait
.tc_req_ancestors ~
f:begin fun acc
(_p
, ty) ->
3480 let _r, (_p
, name), _paraml
= TUtils.unwrap_class_type
ty in
3481 let keep = match acc
with
3482 | Some
(c, _ty
) -> SMap.mem
name c.tc_ancestors
3487 let class_ = Env.get_class
env name in
3490 | Some
{ tc_kind
= Ast.Cinterface
; _ } -> acc
3491 | Some
{ tc_kind
= Ast.Ctrait
; _ } ->
3492 (* this is an error case for which the nastCheck spits out
3493 * an error, but does *not* currently remove the offending
3494 * 'require extends' or 'require implements' *)
3496 | Some
c -> Some
(c, ty)
3500 (* For explicit type arguments we support a wildcard syntax `_` for which
3501 * Hack will generate a fresh type variable
3503 and type_argument
env hint
=
3505 | (_, Happly
((_, "_"), [])) ->
3506 Env.fresh_unresolved_type
env
3508 Phase.hint_locl
env hint
3510 (* If there are no explicit type arguments then generate fresh type variables
3511 * for all of them. Otherwise, check the arity, and use the explicit types *)
3512 and type_arguments
env p class_name tparams hintl
=
3513 let default () = List.map_env
env tparams
begin fun env _ ->
3514 Env.fresh_unresolved_type
env end in
3517 else if List.length hintl
!= List.length tparams
3519 Errors.type_arity
p class_name
(string_of_int
(List.length tparams
));
3523 List.map_env
env hintl type_argument
3525 (* When invoking a method the class_id is used to determine what class we
3526 * lookup the method in, but the type of 'this' will be the late bound type.
3530 * public static function get(): this { return new static(); }
3532 * public static function alias(): this { return self::get(); }
3535 * In C::alias, when we invoke self::get(), 'self' is resolved to the class
3536 * in the lexical scope (C), so call C::get. However the method is executed in
3537 * the current context, so static inside C::get will be resolved to the late
3538 * bound type (get_called_class() within C::alias).
3540 * This means when determining the type of this, CIparent and CIself should be
3541 * changed to CIstatic. For the other cases of C::get() or $c::get(), we only
3542 * look at the left hand side of the '::' and use the type type associated
3545 * Thus C::get() will return a type C, while $c::get() will return the same
3548 and this_for_method
env cid default_ty
= match cid with
3549 | CIparent
| CIself
| CIstatic
->
3550 let p = Reason.to_pos
(fst default_ty
) in
3551 let env, _te
, ty = static_class_id
p env CIstatic
in
3552 env, ExprDepTy.make
env CIstatic
ty
3556 and static_class_id
p env =
3557 let make_result env te
ty =
3561 (match Env.get_self
env with
3562 | _, Tclass
((_, self
), _) ->
3563 (match Env.get_class
env self
with
3565 {tc_kind
= Ast.Ctrait
; _}
3567 (match trait_most_concrete_req_class trait
env with
3569 Errors.parent_in_trait
p;
3570 make_result env T.CIparent
(Reason.Rwitness
p, Terr
)
3571 | Some
(_, parent_ty
) ->
3572 (* inside a trait, parent is SN.Typehints.this, but with the
3573 * type of the most concrete class that the trait has
3574 * "require extend"-ed *)
3575 let r = Reason.Rwitness
p in
3576 let env, parent_ty
= Phase.localize_with_self
env parent_ty
in
3577 make_result env T.CIparent
(r, TUtils.this_of parent_ty
)
3580 let parent = Env.get_parent
env in
3581 let parent_defined = snd
parent <> Tany
in
3582 if not
parent_defined
3583 then Errors.parent_undefined
p;
3584 let r = Reason.Rwitness
p in
3585 let env, parent = Phase.localize_with_self
env parent in
3586 (* parent is still technically the same object. *)
3587 make_result env T.CIparent
(r, TUtils.this_of
(r, snd
parent))
3589 | _, (Terr
| Tany
| Tmixed
| Tarraykind
_ | Toption
_ | Tprim
_
3590 | Tfun
_ | Ttuple
_ | Tshape
_ | Tvar
_
3591 | Tanon
(_, _) | Tunresolved
_ | Tabstract
(_, _) | Tobject
3593 let parent = Env.get_parent
env in
3594 let parent_defined = snd
parent <> Tany
in
3595 if not
parent_defined
3596 then Errors.parent_undefined
p;
3597 let r = Reason.Rwitness
p in
3598 let env, parent = Phase.localize_with_self
env parent in
3599 (* parent is still technically the same object. *)
3600 make_result env T.CIparent
(r, TUtils.this_of
(r, snd
parent))
3603 make_result env T.CIstatic
3604 (Reason.Rwitness
p, TUtils.this_of
(Env.get_self
env))
3606 make_result env T.CIself
3607 (Reason.Rwitness
p, snd
(Env.get_self
env))
3609 let class_ = Env.get_class
env (snd
c) in
3612 make_result env (T.CI
(c, hl
)) (Reason.Rnone
, Tany
)
3614 let env, tyl = type_arguments
env p (snd
c) class_.tc_tparams hl
in
3615 make_result env (T.CI
(c, hl
))
3616 (Reason.Rwitness
(fst
c), Tclass
(c, tyl))
3618 | CIexpr
(p, _ as e
) ->
3619 let env, te
, ty = expr
env e
in
3620 let rec resolve_ety ty =
3621 let env, ty = TUtils.fold_unresolved
env ty in
3622 let _, ty = Env.expand_type
env ty in
3623 match TUtils.get_base_type
env ty with
3624 | _, Tabstract
(AKnewtype
(classname
, [the_cls
]), _) when
3625 classname
= SN.Classes.cClassname
-> resolve_ety the_cls
3626 | _, Tabstract
(AKgeneric
_, _)
3628 | r, Tunresolved
tyl -> r, Tunresolved
(List.map
tyl resolve_ety)
3629 | _, Tvar
_ as ty -> resolve_ety ty
3630 | _, (Tany
| Tprim Tstring
| Tabstract
(_, None
) | Tmixed
| Tobject
)
3631 when not
(Env.is_strict
env) ->
3633 | _, (Terr
| Tany
| Tmixed
| Tarraykind
_ | Toption
_
3634 | Tprim
_ | Tfun
_ | Ttuple
_
3635 | Tabstract
((AKenum
_ | AKdependent
_ | AKnewtype
_), _)
3636 | Tanon
(_, _) | Tobject
| Tshape
_ as ty
3638 Errors.expected_class ~suffix
:(", but got "^
Typing_print.error ty) p;
3639 Reason.Rnone
, Terr
in
3640 let result_ty = resolve_ety ty in
3641 make_result env (T.CIexpr te
) result_ty
3643 and call_construct
p env class_ params el uel
cid =
3644 let cid = if cid = CIparent
then CIstatic
else cid in
3645 let env, tcid
, cid_ty
= static_class_id
p env cid in
3647 type_expansions
= [];
3649 substs
= Subst.make
class_.tc_tparams
params;
3650 from_class
= Some
cid;
3652 let env = Phase.check_tparams_constraints ~
ety_env env class_.tc_tparams
in
3653 if SSet.mem
"XHP" class_.tc_extends
then env, tcid
, [], [] else
3654 let cstr = Env.get_construct
env class_ in
3655 let mode = Env.get_mode
env in
3656 Typing_hooks.dispatch_constructor_hook
class_ params env p;
3657 match (fst
cstr) with
3660 (mode = FileInfo.Mstrict
|| mode = FileInfo.Mpartial
) &&
3661 class_.tc_members_fully_known
3662 then Errors.constructor_no_args
p;
3663 let env, tel
, _tyl
= exprs env el
in
3665 | Some
{ ce_visibility
= vis; ce_type
= lazy m
; _ } ->
3666 TVis.check_obj_access
p env (Reason.to_pos
(fst m
), vis);
3667 let env, m
= Phase.localize ~
ety_env env m
in
3668 let env, tel
, tuel
, _ty
= call
p env m el uel
in
3669 env, tcid
, tel
, tuel
3671 and check_arity ?
(check_min
=true) pos pos_def
(arity:int) exp_arity
=
3672 let exp_min = (Typing_defs.arity_min exp_arity
) in
3673 if check_min
&& arity < exp_min then
3674 Errors.typing_too_few_args
pos pos_def
;
3675 match exp_arity
with
3676 | Fstandard
(_, exp_max
) ->
3677 if (arity > exp_max
)
3678 then Errors.typing_too_many_args
pos pos_def
;
3679 | Fvariadic
_ | Fellipsis
_ -> ()
3681 and check_deprecated
p { ft_pos
; ft_deprecated
; _ } =
3682 match ft_deprecated
with
3683 | Some s
-> Errors.deprecated_use
p ft_pos s
3686 (* The variadic capture argument is an array listing the passed
3687 * variable arguments for the purposes of the function body; callsites
3688 * should not unify with it *)
3689 and variadic_param
env ft =
3690 match ft.ft_arity
with
3691 | Fvariadic
(_, p_ty
) -> env, Some p_ty
3692 | Fellipsis
_ | Fstandard
_ -> env, None
3694 and call
pos env fty el uel
=
3695 let env, tel
, tuel
, ty = call_
pos env fty el uel
in
3696 (* We need to solve the constraints after every single function call.
3697 * The type-checker is control-flow sensitive, the same value could
3698 * have different type depending on the branch that we are in.
3699 * When this is the case, a call could violate one of the constraints
3701 let env = fold_fun_list
env env.Env.todo
in
3704 (* Enforces that e is unpackable. If e is a tuple, appends its unpacked types
3705 * into the e_tyl returned.
3707 and unpack_expr
env e_tyl e
=
3708 let env, _te
, ety
= expr
env e
in
3711 (* Todo: Check that tuples are allowed - that is, disallow a tuple
3712 * unpacking after an array unpacking.
3714 let unpacked_e_tyl = List.map
tyl (fun ty -> e
, ty) in
3715 env, e_tyl
@ unpacked_e_tyl, true
3718 let unpack_r = Reason.Runpack_param
pos in
3719 let container_ty = (unpack_r, Tclass
((pos, SN.Collections.cContainer
),
3720 [unpack_r, Tany
])) in
3721 let env = Type.sub_type
pos Reason.URparam
env ety
container_ty in
3725 (* Unpacks uel. If tuples are found, unpacked types are appended to the
3728 and unpack_exprl
env e_tyl uel
=
3729 List.fold_left uel ~init
:(env, e_tyl
, false)
3730 ~
f: begin fun (env, e_tyl
, unpacked_tuple
) e
->
3731 let env, e_tyl
, is_tuple
= unpack_expr
env e_tyl e
in
3732 (env, e_tyl
, is_tuple
|| unpacked_tuple
)
3735 and call_
pos env fty el uel
=
3736 let env, efty
= Env.expand_type
env fty in
3738 | _, (Terr
| Tany
| Tunresolved
[]) ->
3739 let el = el @ uel
in
3740 let env, tel
= List.map_env
env el begin fun env elt
->
3741 let env, te
, arg_ty
= expr
env elt
in
3742 let env, _arg_ty
= check_valid_rvalue
pos env arg_ty
in
3745 Typing_hooks.dispatch_fun_call_hooks
[] (List.map
(el @ uel
) fst
) env;
3746 env, tel
, [], (Reason.Rnone
, Tany
)
3747 | r, Tunresolved
tyl ->
3748 let env, retl
= List.map_env
env tyl begin fun env ty ->
3749 let env, _, _, ty = call
pos env ty el uel
in env, ty
3751 let env, ty = TUtils.in_var
env (r, Tunresolved retl
) in
3754 (* Typing of format string functions. It is dependent on the arguments (el)
3755 * so it cannot be done earlier.
3757 let env, ft = Typing_exts.retype_magic_func
env ft el in
3758 check_deprecated
pos ft;
3759 let pos_def = Reason.to_pos r2
in
3760 let env, var_param
= variadic_param
env ft in
3761 let env, e_tyl
= List.map_env
env el begin fun env e
->
3762 let env, _te
, ty = expr
env e
in
3765 let env, e_tyl
, unpacked_tuple
= unpack_exprl
env e_tyl uel
in
3766 let arity = if unpacked_tuple
3767 then List.length e_tyl
3768 (* Each array unpacked corresponds with at least 1 param. *)
3769 else List.length
el + List.length uel
in
3770 (* If we unpacked an array, we don't check arity exactly. Since each
3771 * unpacked array consumes 1 or many parameters, it is nonsensical to say
3772 * that not enough args were passed in (so we don't do the min check).
3774 let () = check_arity ~check_min
:(uel
= [] || unpacked_tuple
)
3775 pos pos_def arity ft.ft_arity
in
3776 let todos = ref [] in
3777 let env = wfold_left_default (call_param
todos) (env, var_param
)
3778 ft.ft_params e_tyl
in
3779 let env = fold_fun_list
env !todos in
3780 Typing_hooks.dispatch_fun_call_hooks
3781 ft.ft_params
(List.map
(el @ uel
) fst
) env;
3782 env, [], [], ft.ft_ret
3783 | r2
, Tanon
(arity, id
) when uel
= [] ->
3784 let env, tel
, tyl = exprs env el in
3785 let anon = Env.get_anonymous
env id
in
3786 let fpos = Reason.to_pos r2
in
3789 Errors.anonymous_recursive_call
pos;
3790 env, tel
, [], err_none
3792 let () = check_arity
pos fpos (List.length
tyl) arity in
3793 let tyl = List.map
tyl (fun x -> None
, x) in
3794 let env, ty = anon env tyl in
3796 | _, Tarraykind
_ when not
(Env.is_strict
env) ->
3797 (* Relaxing call_user_func to work with an array in partial mode *)
3798 env, [], [], (Reason.Rnone
, Tany
)
3801 env, [], [], err_none
3804 and call_param
todos env (name, x) ((pos, _ as e
), arg_ty
) =
3807 | Some
name -> Typing_suggest.save_param
name env x arg_ty
3809 let env, arg_ty
= check_valid_rvalue
pos env arg_ty
in
3811 (* When checking params the type 'x' may be expression dependent. Since
3812 * we store the expression id in the local env for Lvar, we want to apply
3815 let dep_ty = match snd e
with
3816 | Lvar
_ -> ExprDepTy.make
env (CIexpr e
) arg_ty
3818 (* We solve for Tanon types after all the other params because we want to
3819 * typecheck the lambda bodies with as much type information as possible. For
3820 * example, in array_map(fn, x), we might be able to use the type of x to
3821 * infer the type of fn, but if we call sub_type on fn first, we end up
3822 * typechecking its body without the benefit of knowing its full type. If
3823 * fn is typehinted but not x, we could use fn to infer the type of x, but
3824 * in practice the reverse situation is more likely. This rearrangement is
3825 * particularly useful since higher-order functions usually put fn before x.
3829 todos := (fun env ->
3830 Type.sub_type
pos Reason.URparam
env arg_ty
x) :: !todos;
3832 | _, (Terr
| Tany
| Tmixed
| Tarraykind
_ | Toption
_ | Tprim
_
3833 | Tvar
_ | Tfun
_ | Tabstract
(_, _) | Tclass
(_, _) | Ttuple
_
3834 | Tunresolved
_ | Tobject
| Tshape
_) ->
3835 Type.sub_type
pos Reason.URparam
env dep_ty x
3838 Errors.bad_call
p (Typing_print.error ty)
3840 and unop
p env uop te
ty =
3841 let make_result env te
result_ty =
3842 env, T.make_typed_expr
p result_ty (T.Unop
(uop
, te
)), result_ty in
3845 Async.enforce_not_awaitable
env p ty;
3846 (* !$x (logical not) works with any type, so we just return Tbool *)
3847 make_result env te
(Reason.Rlogic_ret
p, Tprim Tbool
)
3849 (* ~$x (bitwise not) only works with int *)
3850 let env = Type.sub_type
p Reason.URnone
env ty
3851 (Reason.Rarith
p, Tprim Tint
) in
3852 make_result env te
(Reason.Rarith
p, Tprim Tint
)
3859 (* math operators work with int or floats, so we call sub_type *)
3860 let env = Type.sub_type
p Reason.URnone
env ty
3861 (Reason.Rarith
p, Tprim Tnum
) in
3862 make_result env te
ty
3864 (* We basically just ignore references in non-strict files *)
3865 if Env.is_strict
env then
3866 Errors.reference_expr
p;
3867 make_result env te
ty
3869 and binop in_cond
p env bop
p1 te1
ty1 p2 te2
ty2 =
3871 match Env.expand_type
env ty with
3872 | (_, (_, (Tany
| Terr
))) -> true
3873 | (_, (_, Tunresolved
tyl)) -> List.for_all
tyl is_any
3875 (* Test if `ty` is *not* the any type (or a variant thereof) and
3876 * is a subtype of the primitive type `prim`. *)
3877 let is_sub_prim env ty prim
=
3878 let ty_prim = (Reason.Rarith
p, Tprim prim
) in
3879 if not
(is_any ty) && SubType.is_sub_type
env ty ty_prim
3880 then Some
(fst
ty) else None
in
3881 (* Test if `ty` is *not* the any type (or a variant thereof) and
3882 * is a subtype of `num` but is not a subtype of `int` *)
3883 let is_sub_num_not_sub_int env ty =
3884 let ty_num = (Reason.Rarith
p, Tprim Tnum
) in
3885 let ty_int = (Reason.Rarith
p, Tprim Tint
) in
3886 if not
(is_any ty) && SubType.is_sub_type
env ty ty_num
3887 && not
(SubType.is_sub_type
env ty ty_int)
3888 then Some
(fst
ty) else None
in
3889 (* Force ty1 to be a subtype of ty2 (unless it is any) *)
3890 let enforce_sub_ty env ty1 ty2 =
3891 let env = Type.sub_type
p Reason.URnone
env ty1 ty2 in
3892 Env.expand_type
env ty1 in
3893 let make_result env te1 te2
ty =
3894 env, T.make_typed_expr
p ty (T.Binop
(bop
, te1
, te2
)), ty in
3897 let env, ty1 = TUtils.fold_unresolved
env ty1 in
3898 let env, ty2 = TUtils.fold_unresolved
env ty2 in
3899 let env, ety1
= Env.expand_type
env ty1 in
3900 let env, ety2
= Env.expand_type
env ty2 in
3901 (match ety1
, ety2
with
3902 (* For array<V1>+array<V2> and array<K1,V1>+array<K2,V2>, allow
3903 * the addition to produce a supertype. (We could also handle
3904 * when they have mismatching annotations, but we get better error
3905 * messages if we just let those get unified in the next case. *)
3906 (* The general types are:
3907 * function<Tk,Tv>(array<Tk,Tv>, array<Tk,Tv>): array<Tk,Tv>
3908 * function<T>(array<T>, array<T>): array<T>
3909 * and subtyping on the arguments deals with everything
3911 | (_, Tarraykind
(AKmap
_ as ak
)), (_, Tarraykind
(AKmap
_))
3912 | (_, Tarraykind
(AKvec
_ as ak
)), (_, Tarraykind
(AKvec
_)) ->
3913 let env, a_sup
= Env.fresh_unresolved_type
env in
3914 let env, b_sup
= Env.fresh_unresolved_type
env in
3915 let res_ty = Reason.Rarray_plus_ret
p, Tarraykind
(
3917 | AKvec
_ -> AKvec a_sup
3918 | AKmap
_ -> AKmap
(a_sup
, b_sup
)
3921 let env = Type.sub_type
p1 Reason.URnone
env ety1
res_ty in
3922 let env = Type.sub_type
p2 Reason.URnone
env ety2
res_ty in
3923 make_result env te1 te2
res_ty
3924 | (_, Tarraykind
_), (_, Tarraykind
(AKshape
_)) ->
3925 let env, ty2 = Typing_arrays.downcast_aktypes
env ty2 in
3926 binop in_cond
p env bop
p1 te1
ty1 p2 te2
ty2
3927 | (_, Tarraykind
(AKshape
_)), (_, Tarraykind
_) ->
3928 let env, ty1 = Typing_arrays.downcast_aktypes
env ty1 in
3929 binop in_cond
p env bop
p1 te1
ty1 p2 te2
ty2
3930 | (_, Tarraykind
_), (_, Tarraykind
_)
3931 | (_, (Tany
| Terr
)), (_, Tarraykind
_)
3932 | (_, Tarraykind
_), (_, Tany
) ->
3933 let env, ty = Type.unify
p Reason.URnone
env ty1 ty2 in
3934 make_result env te1 te2
ty
3935 | (_, (Tany
| Terr
| Tmixed
| Tarraykind
_ | Toption
_
3936 | Tprim
_ | Tvar
_ | Tfun
_ | Tabstract
(_, _) | Tclass
(_, _)
3937 | Ttuple
_ | Tanon
(_, _) | Tunresolved
_ | Tobject
| Tshape
_
3939 ), _ -> binop in_cond
p env Ast.Minus
p1 te1
ty1 p2 te2
ty2
3941 | Ast.Minus
| Ast.Star
->
3943 let env, ty1 = enforce_sub_ty env ty1 (Reason.Rarith
p1, Tprim Tnum
) in
3944 let env, ty2 = enforce_sub_ty env ty2 (Reason.Rarith
p2, Tprim Tnum
) in
3945 (* If either side is a float then float: 1.0 - 1 -> float *)
3947 * function(float, num): float
3948 * function(num, float): float
3950 match is_sub_prim env ty1 Tfloat
, is_sub_prim env ty2 Tfloat
with
3951 | (Some
r, _) | (_, Some
r) ->
3952 make_result env te1 te2
(r, Tprim Tfloat
)
3954 (* Both sides are integers, then integer: 1 - 1 -> int *)
3956 * function(int, int): int
3958 match is_sub_prim env ty1 Tint
, is_sub_prim env ty2 Tint
with
3959 | (Some
_, Some
_) ->
3960 make_result env te1 te2
(Reason.Rarith_ret
p, Tprim Tint
)
3962 (* Either side is a non-int num then num *)
3964 * function(num, num): num
3966 match is_sub_num_not_sub_int env ty1,
3967 is_sub_num_not_sub_int env ty2 with
3968 | (Some
r, _) | (_, Some
r) ->
3969 make_result env te1 te2
(r, Tprim Tnum
)
3971 | _, _ -> env, T.make_typed_expr
p ty1 T.Any
, ty1
3973 | Ast.Slash
| Ast.Starstar
->
3975 let env, ty1 = enforce_sub_ty env ty1 (Reason.Rarith
p1, Tprim Tnum
) in
3976 let env, ty2 = enforce_sub_ty env ty2 (Reason.Rarith
p2, Tprim Tnum
) in
3977 (* If either side is a float then float *)
3979 * function(float, num) : float
3980 * function(num, float) : float
3981 * [Actually, for division result can be false if second arg is zero]
3983 match is_sub_prim env ty1 Tfloat
, is_sub_prim env ty2 Tfloat
with
3984 | (Some
r, _) | (_, Some
r) ->
3985 make_result env te1 te2
(r, Tprim Tfloat
)
3986 (* Otherwise it has type
3987 * function(num, num) : num
3988 * [Actually, for division result can be false if second arg is zero]
3991 let r = match bop
with
3992 | Ast.Slash
-> Reason.Rret_div
p
3993 | _ -> Reason.Rarith_ret
p in
3994 make_result env te1 te2
(r, Tprim Tnum
)
3997 (* Integer remainder function has type
3998 * function(int, int) : int
3999 * [Actually, result can be false if second arg is zero]
4001 let env, _ = enforce_sub_ty env ty1 (Reason.Rarith
p1, Tprim Tint
) in
4002 let env, _ = enforce_sub_ty env ty2 (Reason.Rarith
p1, Tprim Tint
) in
4003 make_result env te1 te2
(Reason.Rarith_ret
p, Tprim Tint
)
4006 match is_sub_prim env ty1 Tbool
, is_sub_prim env ty2 Tbool
with
4007 | (Some
_, _) | (_, Some
_) ->
4009 * function(bool, bool) : bool
4012 enforce_sub_ty env ty1 (Reason.Rlogic_ret
p1, Tprim Tbool
) in
4014 enforce_sub_ty env ty2 (Reason.Rlogic_ret
p1, Tprim Tbool
) in
4015 make_result env te1 te2
(Reason.Rlogic_ret
p, Tprim Tbool
)
4018 * function(int, int) : int
4020 let env, _ = enforce_sub_ty env ty1 (Reason.Rarith
p1, Tprim Tint
) in
4021 let env, _ = enforce_sub_ty env ty2 (Reason.Rarith
p1, Tprim Tint
) in
4022 make_result env te1 te2
(Reason.Rarith_ret
p, Tprim Tint
)
4024 (* Equality and disequality:
4025 * function<T>(T, T): bool
4027 | Ast.Eqeq
| Ast.Diff
->
4028 make_result env te1 te2
(Reason.Rcomp
p, Tprim Tbool
)
4029 | Ast.EQeqeq
| Ast.Diff2
->
4031 then Typing_equality_check.assert_nontrivial
p bop
env ty1 ty2;
4032 make_result env te1 te2
(Reason.Rcomp
p, Tprim Tbool
)
4033 | Ast.Lt
| Ast.Lte
| Ast.Gt
| Ast.Gte
->
4034 let ty_num = (Reason.Rcomp
p, Tprim
Nast.Tnum
) in
4035 let ty_string = (Reason.Rcomp
p, Tprim
Nast.Tstring
) in
4037 (Reason.Rcomp
p, Tclass
((p, SN.Classes.cDateTime
), [])) in
4039 SubType.is_sub_type
env ty1 ty && SubType.is_sub_type
env ty2 ty in
4040 (* So we have three different types here:
4041 * function(num, num): bool
4042 * function(string, string): bool
4043 * function(DateTime, DateTime): bool
4045 if both_sub ty_num || both_sub ty_string || both_sub ty_datetime
4046 then make_result env te1 te2
(Reason.Rcomp
p, Tprim Tbool
)
4048 (* TODO this is questionable; PHP's semantics for conversions with "<"
4049 * are pretty crazy and we may want to just disallow this? *)
4050 (* This is universal:
4051 * function<T>(T, T): bool
4053 let env, _ = Type.unify
p Reason.URnone
env ty1 ty2 in
4054 make_result env te1 te2
(Reason.Rcomp
p, Tprim Tbool
)
4056 (* A bit weird, this one:
4057 * function(Stringish | string, Stringish | string) : string)
4059 let env = SubType.sub_string
p1 env ty1 in
4060 let env = SubType.sub_string
p2 env ty2 in
4061 make_result env te1 te2
(Reason.Rconcat_ret
p, Tprim Tstring
)
4064 make_result env te1 te2
(Reason.Rlogic_ret
p, Tprim Tbool
)
4065 | Ast.Amp
| Ast.Bar
| Ast.Ltlt
| Ast.Gtgt
->
4066 let env, _ = enforce_sub_ty env ty1 (Reason.Rbitwise
p1, Tprim Tint
) in
4067 let env, _ = enforce_sub_ty env ty2 (Reason.Rbitwise
p2, Tprim Tint
) in
4068 make_result env te1 te2
(Reason.Rbitwise_ret
p, Tprim Tint
)
4072 and non_null
env ty =
4073 let env, ty = Env.expand_type
env ty in
4076 (* When "??T" appears in the typing environment due to implicit
4077 * typing, the recursion here ensures that it's treated as
4078 * isomorphic to "?T"; that is, all nulls are created equal.
4081 | r, Tunresolved
tyl ->
4082 let env, tyl = List.map_env
env tyl
4083 (fun env e
-> non_null
env e
) in
4084 (* We need to flatten the unresolved types, otherwise we could
4085 * end up with "Tunresolved[Tunresolved _]" which is not supposed
4088 let tyl = List.fold_right
tyl ~
f:begin fun ty tyl ->
4090 | _, Tunresolved l
-> l
@ tyl
4093 env, (r, Tunresolved
tyl)
4095 | r, Tabstract
(ak
, _) ->
4096 begin match TUtils.get_concrete_supertypes
env ty with
4097 | env, [ty] -> let env, ty = non_null
env ty in
4098 env, (r, Tabstract
(ak
, Some
ty))
4101 | _, (Terr
| Tany
| Tmixed
| Tarraykind
_ | Tprim
_ | Tvar
_
4102 | Tclass
(_, _) | Ttuple
_ | Tanon
(_, _) | Tfun
_
4103 | Tobject
| Tshape
_) ->
4106 and condition_var_non_null
env = function
4108 | _, Dollardollar
(_, x) ->
4109 let env, x_ty
= Env.get_local
env x in
4110 let env, x_ty
= non_null
env x_ty
in
4111 Env.set_local
env x x_ty
4112 | p, Class_get
(cname
, (_, member_name
)) as e
->
4113 let env, _te
, ty = expr
env e
in
4114 let env, local = Env.FakeMembers.make_static
p env cname member_name
in
4115 let env = Env.set_local
env local ty in
4116 let local = p, Lvar
(p, local) in
4117 condition_var_non_null
env local
4118 (* TODO TAST: generate an assignment to the fake local in the TAST *)
4119 | p, Obj_get
((_, This
| _, Lvar
_ as obj
),
4120 (_, Id
(_, member_name
)),
4122 let env, _te
, ty = expr
env e
in
4123 let env, local = Env.FakeMembers.make
p env obj member_name
in
4124 let env = Env.set_local
env local ty in
4125 let local = p, Lvar
(p, local) in
4126 condition_var_non_null
env local
4129 and condition_isset
env = function
4130 | _, Array_get
(x, _) -> condition_isset
env x
4131 | v
-> condition_var_non_null
env v
4134 * Build an environment for the true or false branch of
4135 * conditional statements.
4137 and condition
env tparamet
=
4139 let env, _te
, ty = raw_expr ~in_cond
:true env x in
4140 Async.enforce_not_awaitable
env (fst
x) ty;
4143 | _, Expr_list
[] -> env
4144 | _, Expr_list
[x] ->
4145 let env, _ = expr env x in
4146 condition
env tparamet
x
4147 | r, Expr_list
(x::xs
) ->
4148 let env, _ = expr env x in
4149 condition
env tparamet
(r, Expr_list xs
)
4150 | _, Call
(Cnormal
, (_, Id
(_, func
)), [param
], [])
4151 when SN.PseudoFunctions.isset
= func
&& tparamet
&&
4152 not
(Env.is_strict
env) ->
4153 condition_isset
env param
4154 | _, Call
(Cnormal
, (_, Id
(_, func
)), [e
], [])
4155 when not tparamet
&& SN.StdlibFunctions.is_null
= func
->
4156 condition_var_non_null
env e
4157 | r, Binop
((Ast.Eqeq
| Ast.EQeqeq
as bop
),
4159 | r, Binop
((Ast.Eqeq
| Ast.EQeqeq
as bop
),
4160 e
, (_, Null
)) when not tparamet
->
4161 let env, x_ty
= expr env e
in
4163 if bop
== Ast.Eqeq
then check_null_wtf
env r x_ty
else env in
4164 condition_var_non_null
env e
4165 | (p, (Lvar
_ | Obj_get
_ | Class_get
_) as e
) ->
4166 let env, ty = expr env e
in
4167 let env, ety
= Env.expand_type
env ty in
4169 | _, Tarraykind
(AKany
| AKempty
)
4170 | _, Tprim Tbool
-> env
4171 | _, (Terr
| Tany
| Tmixed
| Tarraykind
_ | Toption
_
4172 | Tprim
_ | Tvar
_ | Tfun
_ | Tabstract
(_, _) | Tclass
(_, _)
4173 | Ttuple
_ | Tanon
(_, _) | Tunresolved
_ | Tobject
| Tshape
_
4175 condition
env (not tparamet
) (p, Binop
(Ast.Eqeq
, e
, (p, Null
))))
4176 | r, Binop
(Ast.Eq None
, var
, e
) when tparamet
->
4177 let env, e_ty
= expr env e
in
4178 let env = check_null_wtf
env r e_ty
in
4179 condition_var_non_null
env var
4180 | p1, Binop
(Ast.Eq None
, (_, (Lvar
_ | Obj_get
_) as lv
), (p2, _)) ->
4181 let env, _ = expr env (p1, Binop
(Ast.Eq None
, lv
, (p2, Null
))) in
4182 condition
env tparamet lv
4183 | p, Binop
((Ast.Diff
| Ast.Diff2
as op
), e1
, e2) ->
4184 let op = if op = Ast.Diff
then Ast.Eqeq
else Ast.EQeqeq
in
4185 condition
env (not tparamet
) (p, Binop
(op, e1
, e2))
4186 | _, Binop
(Ast.AMpamp
, e1
, e2) when tparamet
->
4187 let env = condition
env true e1
in
4188 let env = condition
env true e2 in
4190 | _, Binop
(Ast.BArbar
, e1
, e2) when not tparamet
->
4191 let env = condition
env false e1
in
4192 let env = condition
env false e2 in
4194 | _, Call
(Cnormal
, (p, Id
(_, f)), [lv
], [])
4195 when tparamet
&& f = SN.StdlibFunctions.is_array
->
4196 is_array
env `PHPArray
p f lv
4197 | _, Call
(Cnormal
, (p, Id
(_, f)), [lv
], [])
4198 when tparamet
&& f = SN.StdlibFunctions.is_vec ->
4199 is_array
env `HackVec
p f lv
4200 | _, Call
(Cnormal
, (p, Id
(_, f)), [lv
], [])
4201 when tparamet
&& f = SN.StdlibFunctions.is_dict
->
4202 is_array
env `HackDict
p f lv
4203 | _, Call
(Cnormal
, (p, Id
(_, f)), [lv
], [])
4204 when tparamet
&& f = SN.StdlibFunctions.is_keyset
->
4205 is_array
env `HackKeyset
p f lv
4206 | _, Call
(Cnormal
, (p, Id
(_, f)), [lv
], [])
4207 when tparamet
&& f = SN.StdlibFunctions.is_int
->
4208 is_type
env lv Tint
(Reason.Rpredicated
(p, f))
4209 | _, Call
(Cnormal
, (p, Id
(_, f)), [lv
], [])
4210 when tparamet
&& f = SN.StdlibFunctions.is_bool
->
4211 is_type
env lv Tbool
(Reason.Rpredicated
(p, f))
4212 | _, Call
(Cnormal
, (p, Id
(_, f)), [lv
], [])
4213 when tparamet
&& f = SN.StdlibFunctions.is_float
->
4214 is_type
env lv Tfloat
(Reason.Rpredicated
(p, f))
4215 | _, Call
(Cnormal
, (p, Id
(_, f)), [lv
], [])
4216 when tparamet
&& f = SN.StdlibFunctions.is_string
->
4217 is_type
env lv Tstring
(Reason.Rpredicated
(p, f))
4218 | _, Call
(Cnormal
, (p, Id
(_, f)), [lv
], [])
4219 when tparamet
&& f = SN.StdlibFunctions.is_resource
->
4220 is_type
env lv Tresource
(Reason.Rpredicated
(p, f))
4221 | _, Unop
(Ast.Unot
, e
) ->
4222 condition
env (not tparamet
) e
4223 | p, InstanceOf
(ivar
, cid) when tparamet
&& is_instance_var ivar
->
4224 (* Check the expession and determine its static type *)
4225 let env, _te
, x_ty
= raw_expr ~in_cond
:false env ivar
in
4227 (* What is the local variable bound to the expression? *)
4228 let env, (ivar_pos
, x) = get_instance_var
env ivar
in
4230 (* The position p here is not really correct... it's the position
4231 * of the instanceof expression, not the class id. But we don't store
4232 * position data for the latter. *)
4233 let env, _te
, obj_ty = static_class_id
p env cid in
4235 (* New implementation of instanceof that is statically safe *)
4236 let safe_instanceof env obj_ty _c class_info
=
4237 (* Generate fresh names consisting of formal type parameter name
4238 * with unique suffix *)
4239 let env, tparams_with_new_names
=
4240 List.map_env
env class_info
.tc_tparams
4241 (fun env ((_,(_,name),_) as tp
) ->
4242 let env, name = Env.add_fresh_generic_parameter
env name in
4246 String.concat
"," (List.map tparams_with_new_names ~
f:snd
)
4248 let reason = Reason.Rinstanceof
(ivar_pos
, s) in
4249 let tyl_fresh = List.map
4250 ~
f:(fun (_,newname
) -> (reason, Tabstract
(AKgeneric newname
, None
)))
4251 tparams_with_new_names
in
4253 (* Type of variable in block will be class name
4254 * with fresh type parameters *)
4255 let obj_ty = (fst
obj_ty, Tclass
(_c
, tyl_fresh)) in
4257 (* Add in constraints as assumptions on those type parameters *)
4259 type_expansions
= [];
4260 substs
= Subst.make class_info
.tc_tparams
tyl_fresh;
4261 this_ty = obj_ty; (* In case `this` appears in constraints *)
4264 let add_bounds env ((_, _, cstr_list
), ty_fresh
) =
4265 List.fold_left cstr_list ~init
:env ~
f:begin fun env (ck
, ty) ->
4266 (* Substitute fresh type parameters for
4267 * original formals in constraint *)
4268 let env, ty = Phase.localize ~
ety_env env ty in
4269 SubType.add_constraint
p env ck ty_fresh
ty end in
4271 List.fold_left
(List.zip_exn class_info
.tc_tparams
tyl_fresh)
4272 ~
f:add_bounds ~init
:env in
4274 (* Finally, if we have a class-test on something with static class type,
4275 * then we can chase the hierarchy and decompose the types to deduce
4276 * further assumptions on type parameters. For example, we might have
4277 * class B<Tb> { ... }
4278 * class C extends B<int>
4279 * and have obj_ty = C and x_ty = B<T> for a generic parameter T.
4280 * Then SubType.add_constraint will deduce that T=int and add int as
4281 * both lower and upper bound on T in env.lenv.tpenv
4283 let env = SubType.add_constraint
p env Ast.Constraint_as
obj_ty x_ty
in
4286 if SubType.is_sub_type
env obj_ty (
4287 Reason.none
, Tclass
((Pos.none
, SN.Classes.cAwaitable
), [Reason.none
, Tany
])
4288 ) then () else Async.enforce_not_awaitable
env (fst ivar
) x_ty
;
4290 let safe_instanceof_enabled =
4291 TypecheckerOptions.experimental_feature_enabled
4292 (Env.get_options
env) TypecheckerOptions.experimental_instanceof
in
4293 let rec resolve_obj env obj_ty =
4294 (* Expand so that we don't modify x *)
4295 let env, obj_ty = Env.expand_type
env obj_ty in
4297 | _, Tabstract
(AKgeneric
name, _) ->
4298 if safe_instanceof_enabled
4299 then Errors.instanceof_generic_classname
p name;
4301 | _, Tabstract
(AKdependent
(`this
, []), Some
(_, Tclass
_)) ->
4303 (* Technically instanceof static is not strong enough to prove
4304 * that a type is exactly the same as the late bound type.
4305 * For now we allow this lie to exist. To solve
4306 * this we either need to create a new type that means
4307 * subtype of static or provide a way of specifying exactly
4308 * the late bound type i.e. $x::class === static::class
4310 if cid = CIstatic
then
4311 ExprDepTy.make
env CIstatic
obj_ty
4315 | _, Tabstract
((AKdependent
_ | AKnewtype
_), Some
ty) ->
4317 | _, Tclass
((_, cid as _c
), tyl) ->
4318 begin match Env.get_class
env cid with
4319 (* Why would this happen? *)
4321 env, (Reason.Rwitness ivar_pos
, Tobject
)
4323 | Some class_info
->
4324 if SubType.is_sub_type
env x_ty
obj_ty
4326 (* If the right side of the `instanceof` object is
4327 * a super type of what we already knew. In this case,
4328 * since we already have a more specialized object, we
4329 * don't touch the original object. Check out the unit
4330 * test srecko.php if this is unclear.
4332 * Note that if x_ty is Tany, no amount of subtype
4333 * checking will be able to specify it
4334 * further. This is arguably desirable to maintain
4335 * the invariant that removing annotations gets rid
4336 * of typing errors in partial mode (See also
4340 (* We only implement the safe instanceof in strict mode *)
4341 (* Also: for generic types we implememt it only with
4342 * experimental feature enabled *)
4343 if Env.is_strict
env && (tyl = [] || safe_instanceof_enabled)
4344 then safe_instanceof env obj_ty _c class_info
4347 | r, Tunresolved
tyl ->
4348 let env, tyl = List.map_env
env tyl resolve_obj in
4349 env, (r, Tunresolved
tyl)
4350 | _, (Terr
| Tany
| Tmixed
| Tarraykind
_ | Tprim
_ | Tvar
_ | Tfun
_
4351 | Tabstract
((AKenum
_ | AKnewtype
_ | AKdependent
_), _)
4352 | Ttuple
_ | Tanon
(_, _) | Toption
_ | Tobject
| Tshape
_) ->
4353 env, (Reason.Rwitness ivar_pos
, Tobject
)
4355 let env, x_ty
= resolve_obj env obj_ty in
4356 Env.set_local
env x x_ty
4357 | _, Binop
((Ast.Eqeq
| Ast.EQeqeq
), e
, (_, Null
))
4358 | _, Binop
((Ast.Eqeq
| Ast.EQeqeq
), (_, Null
), e
) ->
4359 let env, _ = expr env e
in
4362 let env, _ = expr env e
in
4365 and is_instance_var
= function
4366 | _, (Lvar
_ | This
) -> true
4367 | _, Obj_get
((_, This
), (_, Id
_), _) -> true
4368 | _, Obj_get
((_, Lvar
_), (_, Id
_), _) -> true
4369 | _, Class_get
(_, _) -> true
4372 and get_instance_var
env = function
4373 | p, Class_get
(cname
, (_, member_name
)) ->
4374 let env, local = Env.FakeMembers.make_static
p env cname member_name
in
4376 | p, Obj_get
((_, This
| _, Lvar
_ as obj
), (_, Id
(_, member_name
)), _) ->
4377 let env, local = Env.FakeMembers.make
p env obj member_name
in
4379 | _, Lvar
(p, x) -> env, (p, x)
4380 | p, This
-> env, (p, this
)
4381 | _ -> failwith
"Should only be called when is_instance_var is true"
4383 and check_null_wtf
env p ty =
4384 if not
(Env.is_strict
env) then env else
4385 let env, ty = TUtils.fold_unresolved
env ty in
4386 let env, ety
= Env.expand_type
env ty in
4389 (* Find sketchy nulls hidden under singleton Tunresolved *)
4390 let env, ty = TUtils.fold_unresolved
env ty in
4393 Errors.sketchy_null_check
p
4395 Errors.sketchy_null_check_primitive
p
4396 | _, (Terr
| Tany
| Tarraykind
_ | Toption
_ | Tvar
_ | Tfun
_
4397 | Tabstract
(_, _) | Tclass
(_, _) | Ttuple
_ | Tanon
(_, _)
4398 | Tunresolved
_ | Tobject
| Tshape
_ ) -> ());
4400 | _, (Terr
| Tany
| Tmixed
| Tarraykind
_ | Tprim
_ | Tvar
_
4401 | Tfun
_ | Tabstract
(_, _) | Tclass
(_, _) | Ttuple
_ | Tanon
(_, _)
4402 | Tunresolved
_ | Tobject
| Tshape
_ ) -> env
4404 and is_type
env e tprim
r =
4406 | p, Class_get
(cname
, (_, member_name
)) ->
4407 let env, local = Env.FakeMembers.make_static
p env cname member_name
in
4408 Env.set_local
env local (r, Tprim tprim
)
4409 | p, Obj_get
((_, This
| _, Lvar
_ as obj
), (_, Id
(_, member_name
)), _) ->
4410 let env, local = Env.FakeMembers.make
p env obj member_name
in
4411 Env.set_local
env local (r, Tprim tprim
)
4412 | _, Lvar
(_px
, x) ->
4413 Env.set_local
env x (r, Tprim tprim
)
4416 (* Refine type for is_array, is_vec, is_keyset and is_dict tests
4417 * `pred_name` is the function name itself (e.g. 'is_vec')
4418 * `p` is position of the function name in the source
4419 * `arg_expr` is the argument to the function
4421 and is_array
env ty p pred_name arg_expr
=
4422 let env, _te
, arg_ty
= expr env arg_expr
in
4423 let r = Reason.Rpredicated
(p, pred_name
) in
4424 let env, tarrkey_name
= Env.add_fresh_generic_parameter
env "Tk" in
4425 let tarrkey = (r, Tabstract
(AKgeneric tarrkey_name
, None
)) in
4426 let env = SubType.add_constraint
p env Ast.Constraint_as
4427 tarrkey (r, Tprim Tarraykey
) in
4428 let env, tfresh_name
= Env.add_fresh_generic_parameter
env "T" in
4429 let tfresh = (r, Tabstract
(AKgeneric tfresh_name
, None
)) in
4430 (* This is the refined type of e inside the branch *)
4434 Tclass
((Pos.none
, SN.Collections.cDict
), [tarrkey; tfresh])
4436 Tclass
((Pos.none
, SN.Collections.cVec
), [tfresh])
4438 Tclass
((Pos.none
, SN.Collections.cKeyset
), [tarrkey])
4440 Tarraykind AKany
)) in
4441 (* Add constraints on generic parameters that must
4442 * hold for refined_ty <:arg_ty. For example, if arg_ty is Traversable<T>
4443 * and refined_ty is keyset<T#1> then we know T#1 <: T *)
4444 let env = SubType.add_constraint
p env Ast.Constraint_as
refined_ty arg_ty
in
4446 | (_, Class_get
(cname
, (_, member_name
))) ->
4447 let env, local = Env.FakeMembers.make_static
p env cname member_name
in
4448 Env.set_local
env local refined_ty
4449 | (_, Obj_get
((_, This
| _, Lvar
_ as obj
), (_, Id
(_, member_name
)), _)) ->
4450 let env, local = Env.FakeMembers.make
p env obj member_name
in
4451 Env.set_local
env local refined_ty
4452 | (_, Lvar
(_, x)) ->
4453 Env.set_local
env x refined_ty
4456 and string2
env idl
=
4458 List.fold_left idl ~init
:(env,[]) ~
f:begin fun (env,tel
) x ->
4459 let env, te
, ty = expr env x in
4461 let env = SubType.sub_string
p env ty in
4466 (* If the current class inherits from classes that take type arguments, we need
4467 * to check that the arguments provided are consistent with the constraints on
4468 * the type parameters. *)
4469 and check_implements_tparaml
(env: Env.env) ht
=
4470 let _r, (p, c), paraml = TUtils.unwrap_class_type ht
in
4471 let class_ = Decl_env.get_class_dep
env.Env.decl_env
c in
4474 (* The class lives in PHP land *)
4477 let size1 = List.length
class_.dc_tparams
in
4478 let size2 = List.length
paraml in
4479 if size1 <> size2 then Errors.class_arity
p class_.dc_pos
c size1;
4480 let subst = Inst.make_subst
class_.dc_tparams
paraml in
4481 iter2_shortest
begin fun (_, (p, _), cstrl
) ty ->
4482 List.iter cstrl
begin fun (ck
, cstr) ->
4483 (* Constraint might contain uses of generic type parameters *)
4484 let cstr = Inst.instantiate
subst cstr in
4486 | Ast.Constraint_as
->
4487 Type.sub_type_decl
p Reason.URnone
env ty cstr
4488 | Ast.Constraint_eq
->
4489 (* This code could well be unreachable, because we don't allow
4490 * equality constraints on class generics. *)
4491 Type.sub_type_decl
p Reason.URnone
env ty cstr;
4492 Type.sub_type_decl
p Reason.URnone
env cstr ty
4493 | Ast.Constraint_super
->
4494 Type.sub_type_decl
p Reason.URnone
env cstr ty
4496 end class_.dc_tparams
paraml
4498 (* In order to type-check a class, we need to know what "parent"
4499 * refers to. Sometimes people write "parent::", when that happens,
4500 * we need to know the type of parent.
4502 and class_def_parent
env class_def class_type
=
4503 match class_def
.c_extends
with
4504 | (_, Happly
((_, x), _) as parent_ty
) :: _ ->
4505 let parent_type = Decl_env.get_class_dep
env.Env.decl_env
x in
4506 (match parent_type with
4507 | Some
parent_type -> check_parent class_def class_type
parent_type
4509 let parent_ty = Decl_hint.hint
env.Env.decl_env
parent_ty in
4510 env, Some
x, parent_ty
4511 (* The only case where we have more than one parent class is when
4512 * dealing with interfaces and interfaces cannot use parent.
4515 | _ -> env, None
, (Reason.Rnone
, Tany
)
4517 and check_parent class_def class_type
parent_type =
4518 let position = fst class_def
.c_name
in
4519 (* Are all the parents in Hack? Do we know all their methods?
4520 * If so, let's check that the abstract methods have been implemented.
4522 if class_type
.tc_members_fully_known
4523 then check_parent_abstract
position parent_type class_type
;
4524 if parent_type.dc_final
4525 then Errors.extend_final
position parent_type.dc_pos
parent_type.dc_name
4528 and check_parent_abstract
position parent_type class_type
=
4529 let is_final = class_type
.tc_final
in
4530 if parent_type.dc_kind
= Ast.Cabstract
&&
4531 (class_type
.tc_kind
<> Ast.Cabstract
|| is_final)
4533 check_extend_abstract_meth ~
is_final position class_type
.tc_methods
;
4534 check_extend_abstract_meth ~
is_final position class_type
.tc_smethods
;
4535 check_extend_abstract_const ~
is_final position class_type
.tc_consts
;
4536 check_extend_abstract_typeconst
4537 ~
is_final position class_type
.tc_typeconsts
;
4540 and class_def tcopt
c =
4541 let filename = Pos.filename (fst
c.Nast.c_name
) in
4542 let dep = Dep.Class
(snd
c.c_name
) in
4543 let env = Env.empty tcopt
filename (Some
dep) in
4544 let c = TNBody.class_meth_bodies tcopt
c in
4545 if not
!auto_complete
then begin
4546 NastCheck.class_ env c;
4547 NastInitCheck.class_ env c;
4549 let tc = Env.get_class
env (snd
c.c_name
) in
4552 (* This can happen if there was an error during the declaration
4556 Typing_requirements.check_class
env tc;
4557 Some
(class_def_
env c tc)
4559 (* Given a class definition construct a type consisting of the
4560 * class instantiated at its generic parameters.
4562 and get_self_from_c
c =
4563 let tparams = List.map
(fst
c.c_tparams
) begin fun (_, (p, s), _) ->
4564 Reason.Rwitness
p, Tgeneric
s
4566 Reason.Rwitness
(fst
c.c_name
), Tapply
(c.c_name
, tparams)
4568 and class_def_
env c tc =
4569 Typing_hooks.dispatch_enter_class_def_hook
c tc;
4570 let env = Env.set_mode
env c.c_mode
in
4571 let pc, _ = c.c_name
in
4573 (c.c_extends
@ c.c_implements
@ c.c_uses
)
4574 (Decl_hint.hint
env.Env.decl_env
) in
4575 TI.check_tparams_instantiable
env (fst
c.c_tparams
);
4576 let env, constraints
=
4577 Phase.localize_generic_parameters_with_bounds
env (fst
c.c_tparams
)
4578 ~
ety_env:(Phase.env_with_self
env) in
4579 let env = add_constraints
(fst
c.c_name
) env constraints
in
4580 Typing_variance.class_ (Env.get_options
env) (snd
c.c_name
) tc impl;
4581 List.iter
impl (check_implements_tparaml
env);
4583 (* Set up self identifier and type *)
4584 let env = Env.set_self_id
env (snd
c.c_name
) in
4585 let self = get_self_from_c
c in
4586 (* For enums, localize makes self:: into an abstract type, which we don't
4588 let env, self = match c.c_kind
with
4589 | Ast.Cenum
-> env, (fst
self, Tclass
(c.c_name
, []))
4590 | Ast.Cinterface
| Ast.Cabstract
| Ast.Ctrait
4591 | Ast.Cnormal
-> Phase.localize_with_self
env self in
4592 let env = Env.set_self
env self in
4594 let env, parent_id
, parent = class_def_parent
env c tc in
4595 let is_final = tc.tc_final
in
4596 if (tc.tc_kind
= Ast.Cnormal
|| is_final) && tc.tc_members_fully_known
4598 check_extend_abstract_meth ~
is_final pc tc.tc_methods
;
4599 check_extend_abstract_meth ~
is_final pc tc.tc_smethods
;
4600 check_extend_abstract_const ~
is_final pc tc.tc_consts
;
4601 check_extend_abstract_typeconst ~
is_final pc tc.tc_typeconsts
;
4603 let env = Env.set_parent
env parent in
4604 let env = match parent_id
with
4606 | Some parent_id
-> Env.set_parent_id
env parent_id
in
4607 if tc.tc_final
then begin
4609 | Ast.Cinterface
-> Errors.interface_final
(fst
c.c_name
)
4610 | Ast.Cabstract
-> ()
4611 | Ast.Ctrait
-> Errors.trait_final
(fst
c.c_name
)
4613 Errors.internal_error
pc "The parser should not parse final on enums"
4616 SMap.iter
(check_static_method
tc.tc_methods
) tc.tc_smethods
;
4617 List.iter
impl (class_implements_type
env c);
4618 let typed_vars = List.map
c.c_vars
(class_var_def
env ~is_static
:false c) in
4619 let typed_methods = List.map
c.c_methods
(method_def
env) in
4620 let typed_typeconsts = List.map
c.c_typeconsts
(typeconst_def
env) in
4621 let typed_consts, const_types
=
4622 List.unzip
(List.map
c.c_consts
(class_const_def
env)) in
4623 let env = Typing_enum.enum_class_check
env tc c.c_consts const_types
in
4624 let typed_constructor = class_constr_def
env c in
4625 let env = Env.set_static
env in
4626 let typed_static_vars =
4627 List.map
c.c_static_vars
(class_var_def
env ~is_static
:true c) in
4628 let typed_static_methods = List.map
c.c_static_methods
(method_def
env) in
4629 Typing_hooks.dispatch_exit_class_def_hook
c tc;
4631 T.c_mode
= c.c_mode
;
4632 T.c_final
= c.c_final
;
4633 T.c_is_xhp
= c.c_is_xhp
;
4634 T.c_kind
= c.c_kind
;
4635 T.c_name
= c.c_name
;
4636 T.c_tparams
= c.c_tparams
;
4637 T.c_extends
= c.c_extends
;
4638 T.c_uses
= c.c_uses
;
4639 T.c_xhp_attr_uses
= c.c_xhp_attr_uses
;
4640 T.c_xhp_category
= c.c_xhp_category
;
4641 T.c_req_extends
= c.c_req_extends
;
4642 T.c_req_implements
= c.c_req_implements
;
4643 T.c_implements
= c.c_implements
;
4644 T.c_consts
= typed_consts;
4645 T.c_typeconsts
= typed_typeconsts;
4646 T.c_static_vars
= typed_static_vars;
4647 T.c_vars
= typed_vars;
4648 T.c_constructor
= typed_constructor;
4649 T.c_static_methods
= typed_static_methods;
4650 T.c_methods
= typed_methods;
4651 T.c_user_attributes
= List.map
c.c_user_attributes
(user_attribute
env);
4652 T.c_enum
= c.c_enum
;
4655 and check_static_method obj method_name static_method
=
4656 if SMap.mem method_name obj
4658 let lazy (static_method_reason
, _) = static_method
.ce_type
in
4659 let dyn_method = SMap.find_unsafe method_name obj
in
4660 let lazy (dyn_method_reason
, _) = dyn_method.ce_type
in
4661 Errors.static_dynamic
4662 (Reason.to_pos static_method_reason
)
4663 (Reason.to_pos dyn_method_reason
)
4668 and check_extend_abstract_meth ~
is_final p smap
=
4669 SMap.iter
begin fun x ce
->
4670 match ce
.ce_type
with
4671 | lazy (r, Tfun
{ ft_abstract
= true; _ }) ->
4672 Errors.implement_abstract ~
is_final p (Reason.to_pos
r) "method" x
4676 (* Type constants must be bound to a concrete type for non-abstract classes.
4678 and check_extend_abstract_typeconst ~
is_final p smap
=
4679 SMap.iter
begin fun x tc ->
4680 if tc.ttc_type
= None
then
4681 Errors.implement_abstract ~
is_final p (fst
tc.ttc_name
) "type constant" x
4684 and check_extend_abstract_const ~
is_final p smap
=
4685 SMap.iter
begin fun x cc
->
4686 match cc
.cc_type
with
4687 | r, _ when cc
.cc_abstract
&& not cc
.cc_synthesized
->
4688 Errors.implement_abstract ~
is_final p (Reason.to_pos
r) "constant" x
4709 and typeconst_def
env {
4710 c_tconst_name
= (pos, _) as id
;
4711 c_tconst_constraint
;
4714 let env, cstr = opt
Phase.hint_locl
env c_tconst_constraint
in
4715 let env, ty = opt
Phase.hint_locl
env c_tconst_type
in
4717 Option.map2
ty cstr ~
f:(Type.sub_type
pos Reason.URtypeconst_cstr
env)
4720 T.c_tconst_name
= id
;
4721 T.c_tconst_constraint
= c_tconst_constraint
;
4722 T.c_tconst_type
= c_tconst_type
;
4725 and class_const_def
env (h, id
, e
) =
4728 | None
-> env, Env.fresh_type
()
4730 Phase.hint_locl
env h
4734 let env, te
, ty'
= expr env e
in
4735 ignore
(Type.sub_type
(fst id
) Reason.URhint
env ty'
ty);
4736 (h, id
, Some te
), ty'
4740 and class_constr_def
env c =
4741 Option.map
c.c_constructor
(method_def
env)
4743 and class_implements_type
env c1 ctype2
=
4745 List.map
(fst c1
.c_tparams
) begin fun (_, (p, s), _) ->
4746 (Reason.Rwitness
p, Tgeneric
s)
4748 let r = Reason.Rwitness
(fst c1
.c_name
) in
4749 let ctype1 = r, Tapply
(c1
.c_name
, params) in
4750 Typing_extends.check_implements
env ctype2
ctype1;
4753 and class_var_def
env ~is_static
c cv
=
4754 let env, typed_cv_expr
, ty =
4755 match cv
.cv_expr
with
4756 | None
-> env, None
, Env.fresh_type
()
4757 | Some e
-> let env, te
, ty = expr env e
in env, Some te
, ty in
4758 (match cv
.cv_type
with
4759 | None
when Env.is_strict
env ->
4760 Errors.add_a_typehint
(fst cv
.cv_id
)
4762 let pos, name = cv
.cv_id
in
4763 let name = if is_static
then "$"^
name else name in
4764 let var_type = Reason.Rwitness
pos, Tany
in
4765 (match cv
.cv_expr
with
4767 Typing_suggest.uninitialized_member
(snd
c.c_name
) name env var_type ty;
4770 Typing_suggest.save_member
name env var_type ty;
4773 | Some
(p, _ as cty
) ->
4775 (* If this is an XHP attribute and we're in strict mode,
4776 relax to partial mode to allow the use of the "array"
4777 annotation without specifying type parameters. Until
4778 recently HHVM did not allow "array" with type parameters
4779 in XHP attribute declarations, so this is a temporary
4780 hack to support existing code for now. *)
4781 (* Task #5815945: Get rid of this Hack *)
4782 if cv
.cv_is_xhp
&& (Env.is_strict
env)
4783 then Env.set_mode
env FileInfo.Mpartial
4785 let cty = TI.instantiable_hint
env cty in
4786 let env, cty = Phase.localize_with_self
env cty in
4787 let _ = Type.sub_type
p Reason.URhint
env ty cty in
4790 T.cv_final
= cv
.cv_final
;
4791 T.cv_is_xhp
= cv
.cv_is_xhp
;
4792 T.cv_visibility
= cv
.cv_visibility
;
4793 T.cv_type
= cv
.cv_type
;
4795 T.cv_expr
= typed_cv_expr
;
4798 and localize_where_constraints
4799 ~
ety_env (env:Env.env) (where_constraints
:Nast.where_constraint list
) =
4800 let add_constraint env (h1
, ck
, h2
) =
4802 Phase.localize
env (Decl_hint.hint
env.Env.decl_env h1
) ~
ety_env in
4804 Phase.localize
env (Decl_hint.hint
env.Env.decl_env h2
) ~
ety_env in
4805 SubType.add_constraint (fst h1
) env ck
ty1 ty2
4807 List.fold_left where_constraints ~
f:add_constraint ~init
:env
4809 and add_constraints
p env constraints
=
4810 let add_constraint env (ty1, ck
, ty2) =
4811 SubType.add_constraint p env ck
ty1 ty2 in
4812 List.fold_left constraints ~
f:add_constraint ~init
: env
4814 and user_attribute
env ua
=
4815 let typed_ua_params =
4816 List.map ua
.ua_params
(fun e
-> let _env, te
, _ty
= expr env e
in te
) in
4818 T.ua_name
= ua
.ua_name
;
4819 T.ua_params
= typed_ua_params;
4822 and method_def
env m
=
4823 (* reset the expression dependent display ids for each method body *)
4824 Reason.expr_display_id_map
:= IMap.empty
;
4825 Typing_hooks.dispatch_enter_method_def_hook m
;
4827 Env.env_with_locals
env Typing_continuations.Map.empty
Local_id.Map.empty
4830 { (Phase.env_with_self
env) with from_class
= Some CIstatic
; } in
4831 let env, constraints
=
4832 Phase.localize_generic_parameters_with_bounds
env m
.m_tparams
4834 TI.check_tparams_instantiable
env m
.m_tparams
;
4835 let env = add_constraints
(fst m
.m_name
) env constraints
in
4837 localize_where_constraints ~
ety_env env m
.m_where_constraints
in
4838 let env = Env.set_local
env this
(Env.get_self
env) in
4839 let env, ret = match m
.m_ret
with
4840 | None
-> env, (Reason.Rwitness
(fst m
.m_name
), Tany
)
4842 let ret = TI.instantiable_hint
env ret in
4843 (* If a 'this' type appears it needs to be compatiable with the
4847 { (Phase.env_with_self
env) with
4848 from_class
= Some CIstatic
} in
4849 Phase.localize ~
ety_env env ret in
4850 let m_params = match m
.m_variadic
with
4851 | FVvariadicArg param
-> param
:: m
.m_params
4854 TI.check_params_instantiable
env m_params;
4855 let env, param_tys
= List.map_env
env m_params make_param_local_ty in
4856 if Env.is_strict
env then begin
4857 List.iter2_exn ~
f:(check_param
env) m_params param_tys
;
4859 if Attributes.mem
SN.UserAttributes.uaMemoize m
.m_user_attributes
then
4860 List.iter2_exn ~
f:(check_memoizable env) m_params param_tys
;
4861 let env, typed_params
=
4862 List.map_env
env (List.zip_exn param_tys
m_params) bind_param in
4863 let nb = Nast.assert_named_body m
.m_body
in
4865 fun_ ~abstract
:m
.m_abstract
env ret (fst m
.m_name
) nb m
.m_fun_kind
in
4867 List.fold_left
(Env.get_todo
env) ~
f:(fun env f -> f env) ~init
:env in
4869 | None
when Env.is_strict
env && snd m
.m_name
<> SN.Members.__destruct
->
4870 (* if we are in strict mode, the only case where we don't want to enforce
4871 * a return type is when the method is a destructor
4873 suggest_return env (fst m
.m_name
) ret
4876 Typing_hooks.dispatch_exit_method_def_hook m
;
4878 T.m_final
= m
.m_final
;
4879 T.m_abstract
= m
.m_abstract
;
4880 T.m_visibility
= m
.m_visibility
;
4881 T.m_name
= m
.m_name
;
4882 T.m_tparams
= m
.m_tparams
;
4883 T.m_where_constraints
= m
.m_where_constraints
;
4884 T.m_variadic
= T.FVnonVariadic
(* TODO TAST: get this right m.m_variadic *);
4885 T.m_params = typed_params
;
4886 T.m_fun_kind
= m
.m_fun_kind
;
4887 T.m_user_attributes
= List.map m
.m_user_attributes
(user_attribute
env);
4889 T.m_body
= T.NamedBody
{
4891 T.fnb_unsafe
= false (* TAST get this right *)
4895 and typedef_def tcopt typedef
=
4896 let tid = (snd typedef
.t_name
) in
4897 let filename = Pos.filename (fst typedef
.t_kind
) in
4898 let dep = Typing_deps.Dep.Class
tid in
4900 Typing_env.empty tcopt
filename (Some
dep) in
4901 (* Mode for typedefs themselves doesn't really matter right now, but
4902 * they can expand hints, so make it loose so that the typedef doesn't
4903 * fail. (The hint will get re-checked with the proper mode anyways.)
4904 * Ideally the typedef would carry the right mode with it, but it's a
4905 * slightly larger change than I want to deal with right now. *)
4906 let env = Typing_env.set_mode
env FileInfo.Mdecl
in
4907 let env, constraints
=
4908 Phase.localize_generic_parameters_with_bounds
env typedef
.t_tparams
4909 ~
ety_env:(Phase.env_with_self
env) in
4910 let env = add_constraints
(fst typedef
.t_name
) env constraints
in
4911 NastCheck.typedef
env typedef
;
4915 t_constraint
= tcstr
;
4917 t_user_attributes
= _;
4921 let ty = TI.instantiable_hint
env hint
in
4922 let env, ty = Phase.localize_with_self
env ty in
4923 begin match tcstr
with
4925 let cstr = TI.instantiable_hint
env tcstr
in
4926 let env, cstr = Phase.localize_with_self
env cstr in
4927 ignore
@@ Typing_ops.sub_type t_pos
Reason.URnewtype_cstr
env ty cstr
4931 | pos, Hshape
{ nsi_allows_unknown_fields
=_; nsi_field_map
} ->
4932 ignore
(check_shape_keys_validity
env pos (ShapeMap.keys nsi_field_map
))
4935 and gconst_def cst tcopt
=
4936 Typing_hooks.dispatch_global_const_hook cst
.cst_name
;
4937 let typed_cst_value =
4938 match cst
.cst_value
with
4941 let filename = Pos.filename (fst cst
.cst_name
) in
4942 let dep = Typing_deps.Dep.GConst
(snd cst
.cst_name
) in
4944 Typing_env.empty tcopt
filename (Some
dep) in
4945 let env = Typing_env.set_mode
env cst
.cst_mode
in
4946 let env, te
, value_type
= expr env value in
4947 begin match cst
.cst_type
with
4949 let ty = TI.instantiable_hint
env hint
in
4950 let env, dty
= Phase.localize_with_self
env ty in
4951 ignore
@@ Typing_utils.sub_type
env value_type dty
4954 { T.cst_mode
= cst
.cst_mode
;
4955 T.cst_name
= cst
.cst_name
;
4956 T.cst_type
= cst
.cst_type
;
4957 T.cst_value
= typed_cst_value
4960 (* Calls the method of a class, but allows the f callback to override the
4961 * return value type *)
4962 and overload_function
p env class_id method_id
el uel
f =
4963 let env, _ce
, ty = static_class_id
p env class_id
in
4964 let env, _tel
, _ = exprs env el in
4966 class_get ~
is_method:true ~is_const
:false env ty method_id class_id
in
4967 (* call the function as declared to validate arity and input types,
4968 but ignore the result and overwrite with custom one *)
4969 let (env, res
), has_error
= Errors.try_with_error
4970 (fun () -> (let env, _, _, ty = call
p env fty el uel
in env, ty), false)
4971 (fun () -> (env, (Reason.Rwitness
p, Tany
)), true) in
4972 (* if there are errors already stop here - going forward would
4973 * report them twice *)
4974 if has_error
then env, T.make_typed_expr
p res
T.Any
, res
4975 else let env, ty = f env fty res
el in
4976 (* TODO TAST: do this right *)
4977 env, T.make_typed_expr
p ty T.Any
, ty
4979 and update_array_type
p env e1
e2 valkind =
4980 let access_type = Typing_arrays.static_array_access
env e2 in
4982 Typing_arrays.update_array_type
p access_type in
4984 | `lvalue
| `lvalue_subexpr
->
4986 raw_expr ~
valkind:`lvalue_subexpr ~in_cond
:false env e1
in
4987 let env, ty1 = type_mapper env ty1 in
4989 | (_, Lvar
(_, x)) ->
4990 (* type_mapper has updated the type in ty1 typevars, but we
4991 need to update the local variable type too *)
4992 let env, ty1 = set_valid_rvalue
p env x ty1 in
4994 | _ -> env, te1
, ty1