2 * Copyright (c) 2015, Facebook, Inc.
5 * This source code is licensed under the BSD-style license found in the
6 * LICENSE file in the "hack" directory of this source tree. An additional grant
7 * of patent rights can be found in the PATENTS file in the same directory.
11 (* This module implements the typing.
13 * Given an Nast.program, it infers the type of all the local
14 * variables, and checks that all the types are correct (aka
22 module TUtils
= Typing_utils
23 module Reason
= Typing_reason
24 module Inst
= Decl_instantiate
25 module Type
= Typing_ops
26 module Env
= Typing_env
27 module LEnv
= Typing_lenv
28 module Dep
= Typing_deps.Dep
29 module Async
= Typing_async
30 module SubType
= Typing_subtype
31 module Unify
= Typing_unify
32 module TGen
= Typing_generic
33 module SN
= Naming_special_names
34 module TAccess
= Typing_taccess
35 module TI
= Typing_instantiability
36 module TVis
= Typing_visibility
37 module TNBody
= Typing_naming_body
38 module TS
= Typing_structure
39 module Phase
= Typing_phase
40 module Subst
= Decl_subst
41 module ExprDepTy
= Typing_dependent_type.ExprDepTy
43 (*****************************************************************************)
45 (*****************************************************************************)
47 (* A guess as to the last position we were typechecking, for use in debugging,
48 * such as figuring out what a runaway hh_server thread is doing. Updated
49 * only best-effort -- it's an approximation to point debugging in the right
50 * direction, nothing more. *)
51 let debug_last_pos = ref Pos.none
52 let debug_print_last_pos _
= print_endline
(Pos.string (Pos.to_absolute
55 (****************************************************************************)
57 (****************************************************************************)
59 let expr_hook = ref None
61 let with_expr_hook hook f
= with_context
62 ~enter
: (fun () -> expr_hook := Some hook
)
63 ~exit
: (fun () -> expr_hook := None
)
66 (*****************************************************************************)
68 (*****************************************************************************)
70 let suggest env p ty
=
71 let ty = Typing_expand.fully_expand env
ty in
72 (match Typing_print.suggest ty with
73 | "..." -> Errors.expecting_type_hint p
74 | ty -> Errors.expecting_type_hint_suggest p
ty
77 let suggest_return env p
ty =
78 let ty = Typing_expand.fully_expand env
ty in
79 (match Typing_print.suggest ty with
80 | "..." -> Errors.expecting_return_type_hint p
81 | ty -> Errors.expecting_return_type_hint_suggest p
ty
84 let any = Reason.Rnone
, Tany
86 let compare_field_kinds x y
=
88 | Nast.AFvalue
(p1
, _
), Nast.AFkvalue
((p2
, _
), _
)
89 | Nast.AFkvalue
((p2
, _
), _
), Nast.AFvalue
(p1
, _
) ->
90 Errors.field_kinds p1 p2
;
95 let check_consistent_fields x l
=
96 List.for_all l
(compare_field_kinds x
)
98 let unbound_name env
(pos
, name
) =
99 (match Env.get_mode env
with
100 | FileInfo.Mstrict
->
101 Errors.unbound_name_typing pos name
102 | FileInfo.Mdecl
| FileInfo.Mpartial
->
105 env
, (Reason.Rnone
, Tany
)
108 (*****************************************************************************)
109 (* Handling function/method arguments *)
110 (*****************************************************************************)
112 let rec wfold_left_default f
(env
, def1
) l1 l2
=
113 match l1
, def1
, l2
with
116 | [], Some d1
, x2
:: rl2
->
117 let env = f
env d1 x2
in
118 wfold_left_default f
(env, def1
) [] rl2
119 | x1
:: rl1
, _
, x2
:: rl2
->
120 let env = f
env x1 x2
in
121 wfold_left_default f
(env, def1
) rl1 rl2
123 let rec check_memoizable env param
(pname
, ty) =
124 let env, ty = Env.expand_type
env ty in
125 let p = param
.param_pos
in
127 | _
, Tprim
(Tarraykey
| Tbool
| Tint
| Tfloat
| Tstring
| Tnum
)
131 | _
, Tprim
(Tvoid
| Tresource
| Tnoreturn
) ->
132 let ty_str = Typing_print.error
(snd
ty) in
133 let msgl = Reason.to_string
("This is "^
ty_str) (fst
ty) in
134 Errors.invalid_memoized_param
p msgl
136 check_memoizable env param
(pname
, ty)
137 | _
, Tshape
(_
, fdm
) ->
138 ShapeMap.iter
begin fun name _
->
139 match ShapeMap.get name fdm
with
140 | Some
ty -> check_memoizable env param
(pname
, ty)
142 let ty_str = Typing_print.error
(snd
ty) in
143 let msgl = Reason.to_string
("This is "^
ty_str) (fst
ty) in
144 Errors.invalid_memoized_param
p msgl;
147 List.iter tyl
begin fun ty ->
148 check_memoizable env param
(pname
, ty)
150 | _
, Tabstract
(AKenum _
, _
) ->
152 | _
, Tabstract
(AKnewtype
(_
, _
), _
) ->
154 let ety_env = Phase.env_with_self
env in
155 Typing_tdef.force_expand_typedef ~
ety_env env ty in
156 check_memoizable env param
(pname
, t'
)
157 (* Just accept all generic types for now. Stricter checks to come later. *)
158 | _
, Tabstract
(AKgeneric _
, _
) ->
160 (* For parameter type 'this::TID' defined by 'type const TID as Bar' checks
163 | _
, Tabstract
(AKdependent _
, Some
ty) ->
164 check_memoizable env param
(pname
, ty)
165 (* Allow unconstrined dependent type `abstract type const TID` just as we
166 * allow unconstrained generics. *)
167 | _
, Tabstract
(AKdependent _
, None
) ->
169 (* Handling Tunresolved case here for completeness, even though it
170 * shouldn't be possible to have an unresolved type when checking
171 * the method declaration. No corresponding test case for this.
173 | _
, Tunresolved tyl
->
174 List.iter tyl
begin fun ty ->
175 check_memoizable env param
(pname
, ty)
177 (* Allow untyped arrays. *)
178 | _
, Tarraykind AKany
179 | _
, Tarraykind AKempty
->
181 | _
, Tarraykind
(AKvec
ty)
182 | _
, Tarraykind
(AKmap
(_
, ty)) ->
183 check_memoizable env param
(pname
, ty)
184 | _
, Tarraykind
(AKshape fdm
) ->
185 ShapeMap.iter
begin fun _
(_
, tv
) ->
186 check_memoizable env param
(pname
, tv
)
188 | _
, Tarraykind
(AKtuple fields
) ->
189 IMap.iter
begin fun _ tv
->
190 check_memoizable env param
(pname
, tv
)
192 | _
, Tclass
(_
, _
) ->
193 let type_param = Env.fresh_type
() in
196 Tclass
((Pos.none
, SN.Collections.cContainer
), [type_param]) in
197 let env, is_container
=
200 SubType.sub_type
env container_type ty, true)
201 (fun _
-> env, false) in
203 check_memoizable env param
(pname
, type_param)
206 let memoizable_type =
207 r, Tclass
((Pos.none
, SN.Classes.cIMemoizeParam
), []) in
208 if SubType.is_sub_type
env memoizable_type ty
211 let ty_str = Typing_print.error
(snd
ty) in
212 let msgl = Reason.to_string
("This is "^
ty_str) (fst
ty) in
213 Errors.invalid_memoized_param
p msgl;
218 let ty_str = Typing_print.error
(snd
ty) in
219 let msgl = Reason.to_string
("This is "^
ty_str) (fst
ty) in
220 Errors.invalid_memoized_param
p msgl
222 (* This function is used to determine the type of an argument.
223 * When we want to type-check the body of a function, we need to
224 * introduce the type of the arguments of the function in the environment
225 * Let's take an example, we want to check the code of foo:
227 * function foo(int $x): int {
228 * // CALL TO make_param_type on (int $x)
229 * // Now we know that the type of $x is int
231 * return $x; // in the environment $x is an int, the code is correct
234 * When we localize, we want to resolve to "static" or "$this" depending on
235 * the context. Even though we are passing in CIstatic, resolve_with_class_id
236 * is smart enough to know what to do. Why do this? Consider the following
239 * abstract const type T;
241 * private this::T $val;
243 * final public function __construct(this::T $x) {
247 * public static function create(this::T $x): this {
248 * return new static($x);
252 * class D extends C { const type T = int; }
254 * In __construct() we want to be able to assign $x to $this->val. The type of
255 * $this->val will expand to '$this::T', so we need $x to also be '$this::T'.
256 * We can do this soundly because when we construct a new class such as,
257 * 'new D(0)' we can determine the late static bound type (D) and resolve
258 * 'this::T' to 'D::T' which is int.
260 * A similar line of reasoning is applied for the static method create.
262 let make_param_local_ty env param
=
264 { (Phase.env_with_self
env) with from_class
= Some CIstatic
; } in
266 match param
.param_hint
with
268 (* if the type is missing, use an unbound type variable *)
269 let _r, ty = Env.fresh_type
() in
270 let r = Reason.Rwitness param
.param_pos
in
273 let ty = Decl_hint.hint
env.Env.decl_env x
in
274 Phase.localize ~
ety_env env ty
276 let ty = match ty with
277 | _
, t
when param
.param_is_variadic
->
278 (* when checking the body of a function with a variadic
279 * argument, "f(C ...$args)", $args is an array<C> *)
280 let r = Reason.Rvar_param param
.param_pos
in
281 let arr_values = r, t
in
282 r, Tarraykind
(AKvec
arr_values)
285 Typing_hooks.dispatch_infer_ty_hook
ty param
.param_pos
env;
286 env, (Some param
.param_name
, ty)
288 let rec bind_param env (_
, ty1
) param
=
289 let env, ty2
= opt expr
env param
.param_expr
in
290 Option.iter param
.param_expr
Typing_sequencing.sequence_check_expr
;
291 let ty2 = match ty2 with
292 | None
-> Reason.none
, Tany
295 Typing_suggest.save_param
(param
.param_name
) env ty1
ty2;
296 let env = Type.sub_type param
.param_pos
Reason.URhint
env ty1
ty2 in
297 Env.set_local
env (Local_id.get param
.param_name
) ty1
299 (* In strict mode, we force you to give a type declaration on a parameter *)
300 (* But the type checker is nice: it makes a suggestion :-) *)
301 and check_param
env param
(_
, ty) =
302 match (param
.param_hint
) with
303 | None
-> suggest env param
.param_pos
ty
306 (*****************************************************************************)
307 (* Now we are actually checking stuff! *)
308 (*****************************************************************************)
309 and fun_def tcopt f
=
310 (* reset the expression dependent display ids for each function body *)
311 Reason.expr_display_id_map
:= IMap.empty
;
312 Typing_hooks.dispatch_enter_fun_def_hook f
;
313 let nb = TNBody.func_body tcopt f
in
314 let dep = Typing_deps.Dep.Fun
(snd f
.f_name
) in
315 let env = Env.empty tcopt
(Pos.filename
(fst f
.f_name
)) (Some
dep) in
316 NastCheck.fun_
env f
nb;
317 (* Fresh type environment is actually unnecessary, but I prefer to
318 * have a guarantee that we are using a clean typing environment. *)
321 let env = { env_up
with Env.lenv
= Env.empty_local
} in
322 let env = Env.set_mode
env f
.f_mode
in
323 let env = Phase.localize_generic_parameters_with_bounds
env f
.f_tparams
324 ~
ety_env:(Phase.env_with_self
env) in
327 | None
-> env, (Reason.Rwitness
(fst f
.f_name
), Tany
)
329 let ty = TI.instantiable_hint
env ret
in
330 Phase.localize_with_self
env ty
332 let f_params = match f
.f_variadic
with
333 | FVvariadicArg param
-> param
:: f
.f_params
336 TI.check_params_instantiable
env f_params;
337 TI.check_tparams_instantiable
env f
.f_tparams
;
338 let env, params
= List.map_env
env f_params make_param_local_ty in
339 let env = List.fold2_exn ~f
:bind_param ~init
:env params
f_params in
340 let env = fun_
env hret
(fst f
.f_name
) nb f
.f_fun_kind
in
341 let env = fold_fun_list
env env.Env.todo
in
342 if Env.is_strict
env then begin
343 List.iter2_exn
f_params params
(check_param
env);
345 | None
-> suggest_return env (fst f
.f_name
) hret
349 Typing_hooks.dispatch_exit_fun_def_hook f
351 (*****************************************************************************)
352 (* function used to type closures, functions and methods *)
353 (*****************************************************************************)
355 and fun_ ?
(abstract
=false) env hret pos named_body f_kind
=
356 Env.with_return
env begin fun env ->
357 debug_last_pos := pos
;
358 let env = Env.set_return
env hret
in
359 let env = Env.set_fn_kind
env f_kind
in
360 let env = block
env named_body
.fnb_nast
in
361 Typing_sequencing.sequence_check_block named_body
.fnb_nast
;
362 let ret = Env.get_return
env in
364 if Nast_terminality.Terminal.block
env named_body
.fnb_nast
||
366 named_body
.fnb_unsafe
||
369 else fun_implicit_return
env pos
ret named_body
.fnb_nast f_kind
in
370 debug_last_pos := Pos.none
;
374 and fun_implicit_return
env pos
ret _b
= function
375 | Ast.FGenerator
| Ast.FAsyncGenerator
-> env
377 (* A function without a terminal block has an implicit return; the
379 let rty = Reason.Rno_return pos
, Tprim
Nast.Tvoid
in
380 Typing_suggest.save_return
env ret rty;
381 Type.sub_type pos
Reason.URreturn
env ret rty
383 (* An async function without a terminal block has an implicit return;
384 * the Awaitable<void> type *)
385 let r = Reason.Rno_return_async pos
in
386 let rty = r, Tclass
((pos
, SN.Classes.cAwaitable
), [r, Tprim
Nast.Tvoid
]) in
387 Typing_suggest.save_return
env ret rty;
388 Type.sub_type pos
Reason.URreturn
env ret rty
391 List.fold_left stl ~f
:stmt ~init
:env
393 and stmt
env = function
398 let env, ty = expr
env e
in
399 (* NB: this check does belong here and not in expr, even though it only
400 * applies to expressions -- we actually want to perform the check on
401 * statements that are expressions, e.g., "foo();" we want to check, but
402 * "return foo();" we do not even though the expression "foo()" is a
403 * subexpression of the statement "return foo();". *)
405 | Nast.Binop
(Ast.Eq _
, _
, _
) -> ()
406 | _
-> Async.enforce_not_awaitable
env (fst e
) ty);
409 let env, ty = expr
env e
in
410 Async.enforce_not_awaitable
env (fst e
) ty;
411 let parent_lenv = env.Env.lenv
in
412 let env = condition
env true e
in
413 let env = block
env b1
in
414 let lenv1 = env.Env.lenv
in
415 let env = { env with Env.lenv
= parent_lenv } in
416 let env = condition
env false e
in
417 let env = block
env b2
in
418 let lenv2 = env.Env.lenv
in
419 let terminal1 = Nast_terminality.Terminal.block
env b1
in
420 let terminal2 = Nast_terminality.Terminal.block
env b2
in
421 if terminal1 && terminal2
423 let env = LEnv.integrate
env parent_lenv lenv1 in
424 let env = LEnv.integrate
env env.Env.lenv
lenv2 in
425 LEnv.integrate
env env.Env.lenv
parent_lenv
428 let env = LEnv.integrate
env parent_lenv lenv1 in
429 LEnv.integrate
env env.Env.lenv
lenv2
433 let env = LEnv.integrate
env parent_lenv lenv2 in
434 LEnv.integrate
env env.Env.lenv
lenv1
436 else LEnv.intersect
env parent_lenv lenv1 lenv2
437 | Return
(p, None
) ->
438 let rty = match Env.get_fn_kind
env with
439 | Ast.FSync
-> (Reason.Rwitness
p, Tprim Tvoid
)
441 | Ast.FAsyncGenerator
-> any (* Return type checked against the "yield". *)
442 | Ast.FAsync
-> (Reason.Rwitness
p, Tclass
((p, SN.Classes.cAwaitable
), [(Reason.Rwitness
p, Tprim Tvoid
)])) in
443 let expected_return = Env.get_return
env in
444 Typing_suggest.save_return
env expected_return rty;
445 let env = Type.sub_type
p Reason.URreturn
env expected_return rty in
447 | Return
(p, Some e
) ->
449 let env, rty = expr
env e
in
450 let rty = match Env.get_fn_kind
env with
453 | Ast.FAsyncGenerator
-> any (* Is an error, but caught in NastCheck. *)
454 | Ast.FAsync
-> (Reason.Rwitness
p), Tclass
((p, SN.Classes.cAwaitable
), [rty]) in
455 let expected_return = Env.get_return
env in
456 (match snd
(Env.expand_type
env expected_return) with
458 (* Yell about returning a value from a void function. This catches
459 * more issues than just unifying with void would do -- in particular
460 * just unifying allows you to return a Tany from a void function,
461 * which is clearly wrong. Note this check is best-effort; if the
462 * function returns a generic type which later ends up being Tvoid
463 * then there's not much we can do here. *)
464 Errors.return_in_void
p (Reason.to_pos
r);
466 | _
, Tunresolved _
->
467 (* we allow return types to grow for anonymous functions *)
468 let env, rty = TUtils.unresolved
env rty in
469 let env, _
= Type.unify
pos Reason.URreturn
env expected_return rty in
471 | _
, (Tany
| Tmixed
| Tarraykind _
| Toption _
| Tprim _
472 | Tvar _
| Tfun _
| Tabstract
(_
, _
) | Tclass
(_
, _
) | Ttuple _
473 | Tanon
(_
, _
) | Tobject
| Tshape _
) ->
474 Typing_suggest.save_return
env expected_return rty;
475 let env = Type.sub_type
pos Reason.URreturn
env expected_return rty in
479 (* NOTE: leaks scope as currently implemented; this matches
480 the behavior in naming (cf. `do_stmt` in naming/naming.ml).
482 let parent_lenv = env.Env.lenv
in
483 let env = Env.freeze_local_env
env in
484 let env = block
env b
in
485 let env, ty = expr
env e
in
486 Async.enforce_not_awaitable
env (fst e
) ty;
487 let after_block = env.Env.lenv
in
489 if env.Env.in_loop
then 1 else Typing_alias.get_depth st
in
490 let env = Env.in_loop
env begin
491 iter_n_acc
alias_depth begin fun env ->
492 let env = condition
env true e
in
493 let env = block
env b
in
498 if Nast_visitor.HasContinue.block b
499 then LEnv.fully_integrate
env parent_lenv
501 let env = LEnv.integrate
env parent_lenv env.Env.lenv
in
502 let env = { env with Env.lenv
= after_block } in
505 condition
env false e
506 | While
(e
, b
) as st
->
507 let env, ty = expr
env e
in
508 Async.enforce_not_awaitable
env (fst e
) ty;
509 let parent_lenv = env.Env.lenv
in
510 let env = Env.freeze_local_env
env in
512 if env.Env.in_loop
then 1 else Typing_alias.get_depth st
in
513 let env = Env.in_loop
env begin
514 iter_n_acc
alias_depth begin fun env ->
515 let env = condition
env true e
in
516 let env = block
env b
in
520 let env = LEnv.fully_integrate
env parent_lenv in
521 condition
env false e
522 | For
(e1
, e2
, e3
, b
) as st
->
523 (* For loops leak their initalizer, but nothing that's defined in the
526 let (env, _
) = expr
env e1
in (* initializer *)
527 let (env, _
) = expr
env e2
in
528 let parent_lenv = env.Env.lenv
in
529 let env = Env.freeze_local_env
env in
531 if env.Env.in_loop
then 1 else Typing_alias.get_depth st
in
532 let env = Env.in_loop
env begin
533 iter_n_acc
alias_depth begin fun env ->
534 let env = condition
env true e2
in (* iteration 0 *)
535 let env = block
env b
in
536 let (env, _
) = expr
env e3
in
540 let env = LEnv.fully_integrate
env parent_lenv in
541 condition
env false e2
543 Nast_terminality.SafeCase.check
(fst e
) env cl
;
544 let env, ty = expr
env e
in
545 Async.enforce_not_awaitable
env (fst e
) ty;
546 let env = check_exhaustiveness
env (fst e
) ty cl
in
547 let parent_lenv = env.Env.lenv
in
548 let env, cl
= case_list
parent_lenv ty env cl
in
549 LEnv.intersect_list
env parent_lenv cl
550 | Foreach
(e1
, e2
, b
) as st
->
551 let env, ty1
= expr
env e1
in
552 let env, ty1
= TUtils.fold_unresolved
env ty1
in
553 let env, ety1
= Env.expand_type
env ty1
in
554 let parent_lenv = env.Env.lenv
in
555 let env = Env.freeze_local_env
env in
556 let env, ty2 = as_expr
env (fst e1
) e2
in
557 let env = Type.sub_type
(fst e1
) Reason.URforeach
env ty2 ety1
in
559 if env.Env.in_loop
then 1 else Typing_alias.get_depth st
in
560 let env = Env.in_loop
env begin
561 iter_n_acc
alias_depth begin fun env ->
562 let env = bind_as_expr
env ty2 e2
in
563 let env = block
env b
in
567 let env = LEnv.fully_integrate
env parent_lenv in
569 | Try
(tb
, cl
, fb
) ->
570 let env = try_catch
(tb
, cl
) env in
571 let env = block
env fb
in
574 let env = List.fold_left el ~f
:begin fun env e
->
576 | _
, Binop
(Ast.Eq _
, (_
, Lvar
(p, x
)), _
) ->
577 Env.add_todo
env (TGen.no_generic
p x
)
580 let env, _
= List.map_env
env el expr
in
584 let env, ty = expr
env e
in
585 exception_ty
p env ty
589 and check_exhaustiveness
env pos ty caselist
=
590 (* Right now we only do exhaustiveness checking for enums. *)
591 let env, (_
, ty) = Env.expand_type
env ty in
594 List.fold_left tyl ~init
:env ~f
:begin fun env ty ->
595 check_exhaustiveness
env pos ty caselist
597 | Tabstract
(AKenum id
, _
) ->
598 let tc = unsafe_opt
@@ Env.get_enum
env id
in
599 Typing_enum.check_enum_exhaustiveness
pos tc caselist
;
601 | Tany
| Tmixed
| Tarraykind _
| Tclass _
| Toption _
| Tprim _
602 | Tvar _
| Tfun _
| Tabstract
(_
, _
) | Ttuple _
| Tanon
(_
, _
)
603 | Tobject
| Tshape _
-> env
605 and case_list
parent_lenv ty env cl
=
606 let env = { env with Env.lenv
= parent_lenv } in
607 case_list_
parent_lenv ty env cl
609 and try_catch
(tb
, cl
) env =
610 let parent_lenv = env.Env.lenv
in
611 let env = Env.freeze_local_env
env in
612 let env = block
env tb
in
613 let after_try = env.Env.lenv
in
614 let env, term_lenv_l
= List.map_env
env cl
615 begin fun env (_
, _
, b
as catch_block
) ->
616 let env, lenv
= catch
parent_lenv after_try env catch_block
in
617 let term = Nast_terminality.Terminal.block
env b
in
621 (Nast_terminality.Terminal.block
env tb
, after_try) :: term_lenv_l in
622 LEnv.intersect_list
env parent_lenv term_lenv_l
624 and case_list_
parent_lenv ty env = function
627 (* TODO this is wrong, should continue on to the other cases, but it
628 * doesn't matter in practice since our parser won't parse default
629 * anywhere but in the last position :) Should fix all of this as well
630 * as totality detection for switch. *)
631 let env = block
env b
in
632 env, [Nast_terminality.Terminal.case
env (Default b
), env.Env.lenv
]
633 | Case
(e
, b
) :: rl
->
634 (* TODO - we should consider handling the comparisons the same
635 * way as Binop Ast.EqEq, since case statements work using ==
636 * comparison rules *)
638 (* The way we handle terminal/nonterminal here is not quite right, you
639 * can still break the type system with things like P3131824. *)
640 let ty_num = (Reason.Rnone
, Tprim
Nast.Tnum
) in
641 let ty_arraykey = (Reason.Rnone
, Tprim
Nast.Tarraykey
) in
642 let both_are_sub_types env tprim ty1
ty2 =
643 (SubType.is_sub_type
env tprim ty1
) &&
644 (SubType.is_sub_type
env tprim
ty2) in
645 if Nast_terminality.Terminal.block
env b
then
646 let env, ty2 = expr
env e
in
648 if (both_are_sub_types env ty_num ty ty2) ||
649 (both_are_sub_types env ty_arraykey ty ty2)
651 else Type.unify
(fst e
) Reason.URnone
env ty ty2 in
652 let env = block
env b
in
653 let lenv = env.Env.lenv in
654 let env, rl
= case_list
parent_lenv ty env rl
in
655 env, (Nast_terminality.Terminal.case
env (Case
(e
, b
)), lenv) :: rl
657 let env, ty2 = expr
env e
in
659 if (both_are_sub_types env ty_num ty ty2) ||
660 (both_are_sub_types env ty_arraykey ty ty2)
662 else Type.unify
(fst e
) Reason.URnone
env ty ty2 in
663 (* Since this block is not terminal we will end up falling through to the
664 * next block. This means the lenv will include what our current
665 * environment is, intersected (or integrated?) with the environment
666 * after executing the block. Example:
668 * $x = 0; // $x = int
671 * $x = ''; // $x = string
674 * $x; // $x = int & string
677 let lenv1 = env.Env.lenv in
678 let env = block
env b
in
679 (* PERF: If the case is empty or a Noop then we do not need to intersect
680 * the lenv since they will be the same.
682 * This saves the cost of intersecting the lenv for the common pattern of
688 let env = match b
with
690 | _
-> LEnv.intersect
env parent_lenv lenv1 env.Env.lenv in
691 case_list_
parent_lenv ty env rl
693 and catch
parent_lenv after_try env (ety
, exn
, b
) =
694 let env = { env with Env.lenv = after_try } in
695 let env = LEnv.fully_integrate
env parent_lenv in
697 let ety_p = (fst ety
) in
698 TUtils.process_class_id
cid;
699 let env, _
= instantiable_cid
ety_p env cid in
700 let env, ety
= static_class_id
ety_p env cid in
701 let env = exception_ty
ety_p env ety
in
702 let env = Env.set_local
env (snd exn
) ety
in
703 let env = block
env b
in
704 (* Only keep the local bindings if this catch is non-terminal *)
707 and as_expr
env pe
= function
709 let ty = Env.fresh_type
() in
710 let tvector = Tclass
((pe
, SN.Collections.cTraversable
), [ty]) in
711 env, (Reason.Rforeach pe
, tvector)
713 let ty1 = Env.fresh_type
() in
714 let ty2 = Env.fresh_type
() in
715 let tmap = Tclass
((pe
, SN.Collections.cKeyedTraversable
), [ty1; ty2]) in
716 env, (Reason.Rforeach pe
, tmap)
718 let ty = Env.fresh_type
() in
719 let tvector = Tclass
((pe
, SN.Classes.cAsyncIterator
), [ty]) in
720 env, (Reason.Rasyncforeach pe
, tvector)
722 let ty1 = Env.fresh_type
() in
723 let ty2 = Env.fresh_type
() in
724 let tmap = Tclass
((pe
, SN.Classes.cAsyncKeyedIterator
), [ty1; ty2]) in
725 env, (Reason.Rasyncforeach pe
, tmap)
727 and bind_as_expr
env ty aexpr
=
728 let env, ety
= Env.expand_type
env ty in
730 | _
, Tclass
((p, _
), [ty2]) ->
733 | Await_as_v
(_
, ev
) -> fst
(assign
p env ev
ty2)
734 | As_kv
((_
, Lvar
(_
, k
)), ev
)
735 | Await_as_kv
(_
, (_
, Lvar
(_
, k
)), ev
) ->
736 let env, _
= set_valid_rvalue
p env k
(Reason.Rnone
, Tmixed
) in
737 fst
(assign
p env ev
ty2)
738 | _
-> (* TODO Probably impossible, should check that *)
741 | _
, Tclass
((p, _
), [ty1; ty2]) ->
744 | Await_as_v
(_
, ev
) -> fst
(assign
p env ev
ty2)
745 | As_kv
((_
, Lvar
(_
, k
)), ev
)
746 | Await_as_kv
(_
, (_
, Lvar
(_
, k
)), ev
) ->
747 let env, _
= set_valid_rvalue
p env k
ty1 in
748 fst
(assign
p env ev
ty2)
749 | _
-> (* TODO Probably impossible, should check that *)
752 | _
, (Tany
| Tmixed
| Tarraykind _
| Toption _
| Tprim _
753 | Tvar _
| Tfun _
| Tabstract
(_
, _
) | Tclass
(_
, _
) | Ttuple _
754 | Tanon
(_
, _
) | Tunresolved _
| Tobject
| Tshape _
758 raw_expr ~in_cond
:false env e
760 and raw_expr ~in_cond ?valkind
:(valkind
=`other
) env e
=
761 debug_last_pos := fst e
;
762 let env, ty = expr_ ~in_cond ~valkind
env e
in
763 let () = match !expr_hook with
764 | Some f
-> f e
(Typing_expand.fully_expand
env ty)
766 Typing_hooks.dispatch_infer_ty_hook
ty (fst e
) env;
770 let valkind = `lvalue
in
771 expr_ ~in_cond
:false ~
valkind env e
773 (* $x ?? 0 is handled similarly to $x ?: 0, except that the latter will also
774 * look for sketchy null checks in the condition. *)
775 and eif
env ~coalesce ~in_cond
p c e1 e2
=
776 let env, tyc
= raw_expr in_cond
env c
in
777 Async.enforce_not_awaitable
env (fst c
) tyc
;
778 let _, parent_locals
as lenv = env.Env.lenv in
779 let c = if coalesce
then (p, Binop
(Ast.Diff2
, c, (p, Null
))) else c in
780 let env = condition
env true c in
781 let env, ty1 = match e1
with
787 let fake1, _locals1
= env.Env.lenv in
788 let env = { env with Env.lenv = lenv } in
789 let env = condition
env false c in
790 let env, ty2 = expr
env e2
in
791 let fake2, _locals2
= env.Env.lenv in
792 let fake_members = LEnv.intersect_fake
fake1 fake2 in
793 (* we restore the locals to their parent state so as not to leak the
794 * effects of the `condition` calls above *)
795 let env = { env with Env.lenv = fake_members, parent_locals
} in
796 (* This is a shortened form of what we do in Typing_lenv.intersect. The
797 * latter takes local environments as arguments, but our types here
798 * aren't assigned to local variables in an environment *)
799 let env, ty1 = TUtils.unresolved
env ty1 in
800 let env, ty2 = TUtils.unresolved
env ty2 in
801 Unify.unify
env ty1 ty2
805 ~
(valkind: [> `lvalue
| `lvalue_subexpr
| `other
])
808 | Any
-> env, (Reason.Rwitness
p, Tany
)
809 | Array
[] -> env, (Reason.Rwitness
p, Tarraykind AKempty
)
810 | Array l
when Typing_arrays.is_shape_like_array
env l
->
811 let env, fdm
= List.fold_left_env
env l ~init
:ShapeMap.empty
812 ~f
:begin fun env fdm x
->
813 let env, (key
, value) = akshape_field
env x
in
814 env, Nast.ShapeMap.add key
value fdm
816 env, (Reason.Rwitness
p, Tarraykind
(AKshape fdm
))
817 | Array
(x
:: rl
as l
) ->
818 let fields_consistent = check_consistent_fields x rl
in
819 let is_vec = match x
with
820 | Nast.AFvalue
_ -> true
821 | Nast.AFkvalue
_ -> false in
822 if fields_consistent && is_vec then
823 let env, fields
= List.foldi l ~f
:begin fun index
(env, acc
) e
->
824 let env, ty = aktuple_field
env e
in
825 env, IMap.add index
ty acc
826 end ~init
:(env, IMap.empty
) in
827 env, (Reason.Rwitness
p, Tarraykind
(AKtuple fields
))
829 let env, value = Env.fresh_unresolved_type
env in
830 let env, values
= List.rev_map_env
env l array_field_value
in
831 let has_unknown = List.exists values
(fun (_, ty) -> ty = Tany
) in
832 let env, values
= List.rev_map_env
env values
TUtils.unresolved
in
833 let unify_value = Type.unify
p Reason.URarray_value
in
835 if has_unknown (* If one of the values comes from PHP land,
836 * we have to be conservative and consider that
837 * we don't know what the type of the values are.
839 then env, (Reason.Rnone
, Tany
)
840 else List.fold_left_env
env values ~init
:value ~f
:unify_value in
842 env, (Reason.Rwitness
p, Tarraykind
(AKvec
value))
844 let env, key
= Env.fresh_unresolved_type
env in
845 let env, keys
= List.rev_map_env
env l array_field_key
in
846 let env, keys
= List.rev_map_env
env keys
TUtils.unresolved
in
847 let unify_key = Type.unify
p Reason.URarray_key
in
848 let env, key
= List.fold_left_env
env keys ~init
:key ~f
:unify_key in
849 env, (Reason.Rwitness
p, Tarraykind
(AKmap
(key
, value)))
850 | ValCollection
(name
, el
) ->
851 let env, x
= Env.fresh_unresolved_type
env in
852 let env, tyl
= List.map_env
env el expr
in
853 let env, tyl
= List.map_env
env tyl
Typing_env.unbind
in
854 let env, tyl
= List.map_env
env tyl
TUtils.unresolved
in
856 List.fold_left_env
env tyl ~init
:x ~f
:(Type.unify
p Reason.URvector
) in
857 let tvector = Tclass
((p, name
), [v
]) in
858 let ty = Reason.Rwitness
p, tvector in
860 | KeyValCollection
(kind
, l
) ->
861 let kl, vl
= List.unzip l
in
862 let env, kl = List.map_env
env kl expr
in
863 let env, kl = List.map_env
env kl Typing_env.unbind
in
864 let env, vl
= List.map_env
env vl expr
in
865 let env, vl
= List.map_env
env vl
Typing_env.unbind
in
866 let env, k
= Env.fresh_unresolved_type
env in
867 let env, v
= Env.fresh_unresolved_type
env in
868 let env, kl = List.map_env
env kl TUtils.unresolved
in
870 List.fold_left_env
env kl ~init
:k ~f
:(Type.unify
p Reason.URkey
) in
871 let env, vl
= List.map_env
env vl
TUtils.unresolved
in
873 List.fold_left_env
env vl ~init
:v ~f
:(Type.unify
p Reason.URvalue
) in
874 let ty = Tclass
((p, kvc_kind_to_name kind
), [k
; v
])
876 env, (Reason.Rwitness
p, ty)
877 | Clone e
-> expr
env e
878 | This
when Env.is_static
env ->
879 Errors.this_in_static
p;
880 env, (Reason.Rwitness
p, Tany
)
881 | This
when valkind = `lvalue
->
882 Errors.this_lvalue
p;
883 env, (Reason.Rwitness
p, Tany
)
885 let r, _ = Env.get_self
env in
887 then Errors.this_var_outside_class
p;
888 let env, (_, ty) = Env.get_local
env this
in
889 let r = Reason.Rwitness
p in
891 let ty = r, TUtils.this_of
ty in
892 (* '$this' always refers to the late bound static type *)
893 env, ExprDepTy.make
env CIstatic
ty
894 | Assert
(AE_assert e
) ->
895 let env = condition
env true e
in
896 env, (Reason.Rwitness
p, Tprim Tvoid
)
899 env, (Reason.Rwitness
p, Tprim Tbool
)
901 env, (Reason.Rwitness
p, Tprim Tint
)
903 env, (Reason.Rwitness
p, Tprim Tfloat
)
905 let ty = Env.fresh_type
() in
906 env, (Reason.Rwitness
p, Toption
ty)
908 env, (Reason.Rwitness
p, Tprim Tstring
)
910 let env = string2
env idl
in
911 env, (Reason.Rwitness
p, Tprim Tstring
)
913 Typing_hooks.dispatch_id_hook x
env;
914 let env, fty
= fun_type_of_id
env x
in
916 | _, Tfun fty
-> check_deprecated
(fst x
) fty
;
920 | Id
((cst_pos
, cst_name
) as id
) ->
921 Typing_hooks.dispatch_id_hook id
env;
922 (match Env.get_gconst
env cst_name
with
923 | None
when Env.is_strict
env ->
924 Errors.unbound_global cst_pos
;
925 env, (Reason.Rwitness cst_pos
, Tany
)
927 env, (Reason.Rnone
, Tany
)
929 Phase.localize_with_self
env ty
931 | Method_id
(instance
, meth
) ->
932 (* Method_id is used when creating a "method pointer" using the magic
933 * inst_meth function.
935 * Typing this is pretty simple, we just need to check that instance->meth
936 * is public+not static and then return its type.
938 let env, ty1 = expr
env instance
in
939 let env, result
, vis
=
940 obj_get_with_visibility ~is_method
:true ~nullsafe
:None
env ty1
941 (CIexpr instance
) meth
(fun x
-> x
) in
942 let has_lost_info = Env.FakeMembers.is_invalid
env instance
(snd meth
) in
945 let name = "the method "^snd meth
in
946 let env, result
= Env.lost_info
name env result
in
951 | _, Tfun fty
-> check_deprecated
p fty
954 | Some
(method_pos
, Vprivate
_) ->
955 Errors.private_inst_meth method_pos
p
956 | Some
(method_pos
, Vprotected
_) ->
957 Errors.protected_inst_meth method_pos
p
962 | Method_caller
((pos, class_name
) as pos_cname
, meth_name
) ->
963 (* meth_caller('X', 'foo') desugars to:
966 let class_ = Env.get_class
env class_name
in
968 | None
-> unbound_name env pos_cname
970 (* Create a class type for the given object instantiated with unresolved
971 * types for its type parameters.
974 List.map_env
env class_.tc_tparams
TUtils.unresolved_tparam
in
975 let params = List.map
class_.tc_tparams
begin fun (_, (p, n
), cstr
) ->
976 Reason.Rwitness
p, Tgeneric
(n
, cstr
)
978 let obj_type = Reason.Rwitness
p, Tapply
(pos_cname
, params) in
980 (Phase.env_with_self
env) with
981 substs
= Subst.make
class_.tc_tparams tvarl
;
983 (* AKENN: what about the bounds on the generic parameters? *)
984 let env, local_obj_ty
= Phase.localize ~
ety_env env obj_type in
986 obj_get ~is_method
:true ~nullsafe
:None
env local_obj_ty
987 (CI
(pos, class_name
)) meth_name
(fun x
-> x
) in
989 | reason
, Tfun fty
->
990 check_deprecated
p fty
;
991 (* We are creating a fake closure:
992 * function<T as Class>(T $x): return_type_of(Class:meth_name)
994 let tparam = Ast.Invariant
, pos_cname
,
995 [(Ast.Constraint_as
, obj_type)] in
996 let env, tvar
= TUtils.unresolved_tparam
env tparam in
997 let param = Reason.Rwitness
pos,
998 Tgeneric
(class_name
, [(Ast.Constraint_as
, obj_type)]) in
1001 substs
= Subst.make
(tparam :: class_.tc_tparams
) (tvar
:: tvarl
)
1003 (* AKENN: do we need to generate bounds here? *)
1004 let env, param = Phase.localize ~
ety_env env param in
1005 let fty = { fty with
1006 ft_params
= (None
, param) :: fty.ft_params
} in
1007 let fun_arity = match fty.ft_arity
with
1008 | Fstandard
(min
, max
) -> Fstandard
(min
+ 1, max
+ 1)
1009 | Fvariadic
(min
, x
) -> Fvariadic
(min
+ 1, x
)
1010 | Fellipsis min
-> Fellipsis
(min
+ 1) in
1013 ft_deprecated
= None
;
1014 ft_abstract
= false;
1015 ft_arity
= fun_arity;
1016 ft_tparams
= fty.ft_tparams
;
1017 ft_params
= fty.ft_params
;
1018 ft_ret
= fty.ft_ret
;
1020 env, (reason
, Tfun
caller)
1022 (* This can happen if the method lives in PHP *)
1023 env, (Reason.Rwitness
pos, Tany
)
1026 | Smethod_id
(c, meth
) ->
1027 (* Smethod_id is used when creating a "method pointer" using the magic
1028 * class_meth function.
1030 * Typing this is pretty simple, we just need to check that c::meth is
1031 * public+static and then return its type.
1033 let class_ = Env.get_class
env (snd
c) in
1036 (* The class given as a static string was not found. *)
1039 let smethod = Env.get_static_member
true env class_ (snd meth
) in
1041 | None
-> (* The static method wasn't found. *)
1042 smember_not_found
p ~is_const
:false ~is_method
:true class_ (snd meth
);
1043 env, (Reason.Rnone
, Tany
)
1046 let env, cid_ty
= static_class_id
(fst
c) env cid in
1048 type_expansions
= [];
1049 substs
= SMap.empty
;
1051 from_class
= Some
cid;
1053 let env, smethod_type
= Phase.localize ~
ety_env env smethod.ce_type
in
1054 (match smethod_type
with
1055 | _, Tfun
fty -> check_deprecated
p fty
1057 (match smethod_type
, smethod.ce_visibility
with
1058 | (r, (Tfun
_ as ty)), Vpublic
->
1060 | (r, Tfun
_), Vprivate
_ ->
1061 Errors.private_class_meth
(Reason.to_pos
r) p;
1063 | (r, Tfun
_), Vprotected
_ ->
1064 Errors.protected_class_meth
(Reason.to_pos
r) p;
1067 Errors.internal_error
p "We have a method which isn't callable";
1073 let r = Reason.Rplaceholder
p in
1074 let ty = r, Tprim Tvoid
in
1076 | Dollardollar
(_, x
) ->
1078 | Lvar
((_, x
) as id
) ->
1079 Typing_hooks.dispatch_lvar_hook id
env;
1080 let env, x
= Env.get_local
env x
in
1083 let env, tyl
= List.map_env
env el expr
in
1084 let env, tyl
= List.map_env
env tyl
Typing_env.unbind
in
1085 let ty = Reason.Rwitness
p, Ttuple tyl
in
1088 let env, ty1 = expr
env e1
in
1089 let env, ty1 = Typing_env.unbind
env ty1 in
1090 let env, ty2 = expr
env e2
in
1091 let env, ty2 = Typing_env.unbind
env ty2 in
1092 let ty = Reason.Rwitness
p, Tclass
((p, SN.Collections.cPair
), [ty1; ty2]) in
1095 let env, tyl
= List.map_env
env el expr
in
1096 let ty = Reason.Rwitness
p, Ttuple tyl
in
1098 | Array_get
(e
, None
) ->
1099 let env, ty1 = update_array_type
p env e None
valkind in
1100 let is_lvalue = (valkind == `lvalue
) in
1101 array_append
is_lvalue p env ty1
1102 | Array_get
(e1
, Some e2
) ->
1103 let env, ty1 = update_array_type
p env e1
(Some e2
) valkind in
1104 let env, ty1 = TUtils.fold_unresolved
env ty1 in
1105 let env, ety1
= Env.expand_type
env ty1 in
1106 let env, ty2 = expr
env e2
in
1107 let is_lvalue = (valkind == `lvalue
) in
1108 array_get
is_lvalue p env ty1 ety1 e2
ty2
1109 | Call
(Cnormal
, (_, Id
(_, hh_show
)), [x
], [])
1110 when hh_show
= SN.PseudoFunctions.hh_show
->
1111 let env, ty = expr
env x
in
1113 env, Env.fresh_type
()
1114 | Call
(call_type
, e
, el
, uel
) ->
1115 let env, result
= dispatch_call
p env call_type e el uel
in
1116 let env = Env.forget_members
env p in
1118 | Binop
(Ast.Eq
(Some op
), e1
, e2
) ->
1119 let e2 = p, Binop
(op
, e1
, e2) in
1120 let env, ty = raw_expr in_cond
env (p, Binop
(Ast.Eq None
, e1
, e2)) in
1122 | Binop
(Ast.Eq None
, e1
, e2) ->
1123 let env, ty2 = raw_expr in_cond
env e2 in
1124 let env, ty = assign
p env e1
ty2 in
1125 Typing_hooks.dispatch_assign_hook
p ty2 env;
1126 (* If we are assigning a local variable to another local variable then
1127 * the expression ID associated with e2 is transferred to e1
1130 | (_, Lvar
(_, x1
)), (_, Lvar
(_, x2
)) ->
1131 let eid2 = Env.get_local_expr_id
env x2
in
1135 ~f
:(Env.set_local_expr_id
env x1
) in
1139 | Binop
((Ast.AMpamp
| Ast.BArbar
as bop
), e1
, e2) ->
1140 let c = bop
= Ast.AMpamp
in
1141 let lenv = env.Env.lenv in
1142 let env, ty1 = expr
env e1
in
1143 let env = condition
env c e1
in
1144 let env, ty2 = raw_expr in_cond
env e2 in
1145 let env = { env with Env.lenv = lenv } in
1146 Typing_hooks.dispatch_binop_hook
p bop
ty1 ty2;
1147 env, (Reason.Rlogic_ret
p, Tprim Tbool
)
1148 | Binop
(bop
, e
, (_, Null
))
1149 | Binop
(bop
, (_, Null
), e
)
1150 when Env.is_strict
env && (bop
= Ast.EQeqeq
|| bop
= Ast.Diff2
) ->
1151 let _, ty = raw_expr in_cond
env e
in
1153 then Typing_equality_check.assert_nullable
p bop
env ty;
1154 env, (Reason.Rcomp
p, Tprim Tbool
)
1155 | Binop
(bop
, e1
, e2) ->
1156 let env, ty1 = raw_expr in_cond
env e1
in
1157 let env, ty2 = raw_expr in_cond
env e2 in
1158 let env, ty = binop in_cond
p env bop
(fst e1
) ty1 (fst
e2) ty2 in
1159 Typing_hooks.dispatch_binop_hook
p bop
ty1 ty2;
1161 | Pipe
((_, id
), e1
, e2) ->
1162 let env, ty = expr
env e1
in
1163 (** id is the ID of the $$ that is implicitly declared by the pipe.
1164 * Set the local type for the $$ in the RHS. *)
1165 let env = Env.set_local
env id
ty in
1166 let env, ty2 = expr
env e2 in
1168 * Return ty2 since the type of the pipe expression is the type of the
1171 * Note: env does have the type of this Pipe's $$, but it doesn't
1172 * override the outer one since they have different ID's.
1175 * a() |> ( inner1($$) |> inner2($$) ) + $$
1177 * The rightmost $$ refers to the result of a()
1181 let env, ty = raw_expr in_cond
env e
in
1183 | Eif
(c, e1
, e2) -> eif
env ~coalesce
:false ~in_cond
p c e1
e2
1184 | NullCoalesce
(e1
, e2) -> eif
env ~coalesce
:true ~in_cond
p e1 None
e2
1186 begin match Env.get_typedef
env (snd sid
) with
1187 | Some
{td_tparams
= tparaml
; _} ->
1188 let params = List.map ~f
:begin fun (_, (p, x
), cstr
) ->
1189 Reason.Rwitness
p, Tgeneric
(x
, cstr
)
1191 let tdef = Reason.Rwitness
(fst sid
), Tapply
(sid
, params) in
1193 Reason.Rwitness
p, Tapply
((p, SN.Classes.cTypename
), [tdef]) in
1194 let env, tparams
= List.map_env
env tparaml
begin fun env _ ->
1195 Env.fresh_unresolved_type
env
1197 let ety_env = { (Phase.env_with_self
env) with
1198 substs
= Subst.make tparaml tparams
} in
1199 (* AKENN: do we generate bounds here from the Tgeneric above? *)
1200 Phase.localize ~
ety_env env typename
1202 (* Should never hit this case since we only construct this AST node
1203 * if in the expression Foo::class, Foo is a type def.
1205 env, (Reason.Rwitness
p, Tany
)
1207 | Class_const
(cid, mid
) -> class_const
env p (cid, mid
)
1208 | Class_get
(x
, (_, y
))
1209 when Env.FakeMembers.get_static
env x y
<> None
->
1210 let env, local
= Env.FakeMembers.make_static
p env x y
in
1211 let local = p, Lvar
(p, local) in
1213 | Class_get
(cid, mid
) ->
1214 TUtils.process_static_find_ref
cid mid
;
1215 let env, cty
= static_class_id
p env cid in
1216 let env, cty
= Env.expand_type
env cty
in
1217 let env, ty = class_get ~is_method
:false ~is_const
:false env cty mid
cid in
1218 if Env.FakeMembers.is_static_invalid
env cid (snd mid
)
1220 let fake_name = Env.FakeMembers.make_static_id
cid (snd mid
) in
1221 let env, ty = Env.lost_info
fake_name env ty in
1224 | Obj_get
(e
, (_, Id
(_, y
)), _)
1225 when Env.FakeMembers.get
env e y
<> None
->
1226 let env, local = Env.FakeMembers.make
p env e y
in
1227 let local = p, Lvar
(p, local) in
1229 | Obj_get
(e1
, (_, Id m
), nullflavor
) ->
1231 (match nullflavor
with
1232 | OG_nullthrows
-> None
1233 | OG_nullsafe
-> Some
p
1235 let env, ty1 = expr
env e1
in
1237 obj_get ~is_method
:false ~
nullsafe env ty1 (CIexpr e1
) m
(fun x
-> x
) in
1238 let has_lost_info = Env.FakeMembers.is_invalid
env e1
(snd m
) in
1241 let name = "the member "^snd m
in
1242 let env, result
= Env.lost_info
name env result
in
1245 | Obj_get
(e1
, _, _) ->
1246 let env, _ = expr
env e1
in
1247 env, (Reason.Rwitness
p, Tany
)
1249 env, (Reason.Rwitness
p, Tany
)
1251 let env, key
= yield_field_key
env af
in
1252 let env, value = yield_field_value
env af
in
1253 let send = Env.fresh_type
() in
1254 let rty = match Env.get_fn_kind
env with
1256 Reason.Ryield_gen
p,
1257 Tclass
((p, SN.Classes.cGenerator
), [key
; value; send])
1258 | Ast.FAsyncGenerator
->
1259 Reason.Ryield_asyncgen
p,
1260 Tclass
((p, SN.Classes.cAsyncGenerator
), [key
; value; send])
1261 | Ast.FSync
| Ast.FAsync
->
1262 failwith
"Parsing should never allow this" in
1264 Type.sub_type
p (Reason.URyield
) env (Env.get_return
env) rty in
1265 let env = Env.forget_members
env p in
1266 env, (Reason.Ryield_send
p, Toption
send)
1268 let env, rty = expr
env e
in
1269 Async.overload_extract_from_awaitable
env p rty
1270 | Special_func func
-> special_func
env p func
1271 | New
(c, el
, uel
) ->
1272 Typing_hooks.dispatch_new_id_hook
c env p;
1273 TUtils.process_static_find_ref
c (p, SN.Members.__construct
);
1274 let check_not_abstract = true in
1275 let env, ty = new_object ~
check_not_abstract p env c el uel
in
1276 let env = Env.forget_members
env p in
1277 env, ExprDepTy.make
env c ty
1278 | Cast
((_, Harray
(None
, None
)), _) when Env.is_strict
env ->
1279 Errors.array_cast
p;
1280 env, (Reason.Rwitness
p, Tany
)
1282 let env, _ = expr
env e
in
1283 Phase.hint_locl
env ty
1284 | InstanceOf
(e
, cid) ->
1285 let env, _ = expr
env e
in
1286 TUtils.process_class_id
cid;
1287 let env, _class
= instantiable_cid
p env cid in
1288 env, (Reason.Rwitness
p, Tprim Tbool
)
1290 let ft = Decl.fun_decl_in_env
env.Env.decl_env f
in
1291 (* When creating a closure, the 'this' type will mean the late bound type
1292 * of the current enclosing class
1295 { (Phase.env_with_self
env) with from_class
= Some CIstatic
} in
1296 let env, ft = Phase.localize_ft ~
ety_env env ft in
1297 (* check for recursive function calls *)
1298 let anon = anon_make
env p f
in
1299 let env, anon_id
= Env.add_anonymous
env anon in
1300 let env = Errors.try_with_error
1302 ignore
(anon env ft.ft_params
); env)
1304 (* If the anonymous function declaration has errors itself, silence
1305 them in any subsequent usages. *)
1306 let anon env fun_params
=
1307 Errors.ignore_
(fun () -> (anon env fun_params
)) in
1308 Env.set_anonymous
env anon_id
anon) in
1309 env, (Reason.Rwitness
p, Tanon
(ft.ft_arity
, anon_id
))
1310 | Xml
(sid
, attrl
, el
) ->
1312 let env, obj
= expr
env (fst sid
, New
(cid, [], [])) in
1313 let env, attr_ptyl
= List.map_env
env attrl
begin fun env attr
->
1314 (* Typecheck the expressions - this just checks that the expressions are
1315 * valid, not that they match the declared type for the attribute *)
1316 let namepstr, valexpr
= attr
in
1317 let valp, _ = valexpr
in
1318 let env, valty
= expr
env valexpr
in
1319 env, (namepstr, (valp, valty
))
1321 let env, _body
= List.map_env
env el expr
in
1322 let env, class_ = class_id_for_new
p env cid in
1324 | None
-> env, (Reason.Runknown_class
p, Tobject
)
1325 | Some
(_, class_, _) ->
1326 if TypecheckerOptions.unsafe_xhp
(Env.get_options
env) then
1329 let env = List.fold_left attr_ptyl ~f
:begin fun env attr
->
1330 let namepstr, valpty
= attr
in
1331 let valp, valty
= valpty
in
1332 (* We pretend that XHP attributes are stored as member variables,
1333 * prefixed with a colon.
1335 * This converts the member name to an attribute name. *)
1336 let name = ":" ^
(snd
namepstr) in
1338 obj_get ~is_method
:false ~
nullsafe:None
env obj
cid
1339 (fst
namepstr, name) (fun x
-> x
) in
1340 let ureason = Reason.URxhp
(class_.tc_name
, snd
namepstr) in
1341 Type.sub_type
valp ureason env declty valty
1347 let env, fdm
= ShapeMap.map_env expr
env fdm
in
1348 (* allow_inter adds a type-variable *)
1349 let env, fdm
= ShapeMap.map_env
TUtils.unresolved
env fdm
in
1350 let env = check_shape_keys_validity
env p (ShapeMap.keys fdm
) in
1351 (* Fields are fully known, because this shape is constructed
1352 * using shape keyword and we know exactly what fields are set. *)
1353 env, (Reason.Rwitness
p, Tshape
(FieldsFullyKnown
, fdm
))
1355 and class_const ?
(incl_tc
=false) env p (cid, mid
) =
1356 TUtils.process_static_find_ref
cid mid
;
1357 let env, cty
= static_class_id
p env cid in
1358 let env, cty
= Env.expand_type
env cty
in
1360 class_get ~is_method
:false ~is_const
:true ~incl_tc
env cty mid
cid in
1362 | r, Tabstract
(AKgeneric n
, _) ->
1363 let () = match cid with
1364 | CIstatic
| CIexpr
_ -> ();
1365 | _ -> Errors.abstract_const_usage
p (Reason.to_pos
r) n
; ()
1370 (*****************************************************************************)
1371 (* Anonymous functions. *)
1372 (*****************************************************************************)
1374 and anon_bind_param
params env (param_name
, ty as pname_ty
) =
1377 (* This code cannot be executed normally, because the arity is wrong
1378 * and it will error later. Bind as many parameters as we can and carry
1381 | param :: paraml
->
1383 match param.param_hint
with
1385 let env, h
= Phase.hint_locl
env h
in
1386 let pos = Reason.to_pos
(fst
ty) in
1387 let env = Type.sub_type
pos Reason.URparam
env h
ty in
1388 (* Closures are allowed to have explicit type-hints. When
1389 * that is the case we should check that the argument passed
1390 * is compatible with the type-hint.
1391 * The body of the function should be type-checked with the
1392 * hint and not the type of the argument passed.
1393 * Otherwise it leads to strange results where
1394 * foo(?string $x = null) is called with a string and fails to
1395 * type-check. If $x is a string instead of ?string, null is not
1396 * subtype of string ...
1398 bind_param env (param_name
, h
) param
1399 | None
-> bind_param env pname_ty
param
1401 and anon_bind_opt_param
env param =
1402 match param.param_expr
with
1404 let ty = Reason.Rnone
, Tany
in
1405 bind_param env (None
, ty) param
1407 let env, ty = expr
env default
in
1408 Typing_sequencing.sequence_check_expr default
;
1409 bind_param env (None
, ty) param
1411 and anon_check_param
env param =
1412 match param.param_hint
with
1415 let env, hty
= Phase.hint_locl
env hty
in
1416 let env, paramty
= Env.get_local
env (Local_id.get
param.param_name
) in
1417 let hint_pos = Reason.to_pos
(fst hty
) in
1418 let env = Type.sub_type
hint_pos Reason.URhint
env hty paramty
in
1421 and anon_make tenv
p f
=
1422 let anon_lenv = tenv
.Env.lenv in
1423 let is_typing_self = ref false in
1424 let nb = Nast.assert_named_body f
.f_body
in
1428 Errors.anonymous_recursive
p;
1429 env, (Reason.Rwitness
p, Tany
)
1432 is_typing_self := true;
1433 Env.anon anon_lenv env begin fun env ->
1434 let params = ref f
.f_params in
1435 let env = List.fold_left ~f
:(anon_bind_param
params) ~init
:env tyl
in
1436 let env = List.fold_left ~f
:anon_bind_opt_param ~init
:env !params in
1437 let env = List.fold_left ~f
:anon_check_param ~init
:env f
.f_params in
1440 | None
-> Env.fresh_unresolved_type
env
1442 let ret = TI.instantiable_hint
env x
in
1443 (* If a 'this' type appears it needs to be compatible with the
1447 { (Phase.env_with_self
env) with
1448 from_class
= Some CIstatic
} in
1449 Phase.localize ~
ety_env env ret in
1450 let env = Env.set_return
env hret
in
1451 let env = Env.set_fn_kind
env f
.f_fun_kind
in
1452 let env = block
env nb.fnb_nast
in
1454 if Nast_terminality.Terminal.block tenv
nb.fnb_nast
1455 || nb.fnb_unsafe
|| !auto_complete
1457 else fun_implicit_return
env p hret
nb.fnb_nast f
.f_fun_kind
1459 is_typing_self := false;
1464 (*****************************************************************************)
1465 (* End of anonymous functions. *)
1466 (*****************************************************************************)
1468 and special_func
env p func
=
1469 let env, ty = (match func
with
1471 let env, ety
= expr
env e
in
1472 Async.gena
env p ety
1474 let env, etyl
= List.map_env
env el expr
in
1475 Async.genva
env p etyl
1476 | Gen_array_rec e
->
1477 let env, ety
= expr
env e
in
1478 Async.gen_array_rec
env p ety
1480 env, (Reason.Rwitness
p, Tclass
((p, SN.Classes.cAwaitable
), [ty]))
1482 and requires_consistent_construct
= function
1489 and new_object ~
check_not_abstract p env c el uel
=
1490 let env, class_ = instantiable_cid
p env c in
1493 let _ = List.map_env
env el expr
in
1494 let _ = List.map_env
env el expr
in
1495 env, (Reason.Runknown_class
p, Tobject
)
1496 | Some
(cname
, class_, c_ty
) ->
1497 if check_not_abstract && class_.tc_abstract
1498 && not
(requires_consistent_construct
c) then
1499 uninstantiable_error
p c class_.tc_pos
class_.tc_name
p c_ty
;
1500 let env, params = List.map_env
env class_.tc_tparams
begin fun env _ ->
1501 Env.fresh_unresolved_type
env
1504 if SSet.mem
"XHP" class_.tc_extends
then env else
1505 let env = call_construct
p env class_ params el uel
c in
1508 let r_witness = Reason.Rwitness
p in
1509 let obj_ty = r_witness, Tclass
(cname
, params) in
1510 if not
(snd
class_.tc_construct
) then
1512 | CIstatic
-> Errors.new_inconsistent_construct
p cname `static
1513 | CIexpr
_ -> Errors.new_inconsistent_construct
p cname `classname
1517 env, (r_witness, TUtils.this_of
obj_ty)
1519 (match (fst
class_.tc_construct
) with
1522 type_expansions
= [];
1523 substs
= SMap.empty
;
1527 let _, ce_type
= Phase.localize ~
ety_env env ce
.ce_type
in
1528 ignore
(check_abstract_parent_meth
SN.Members.__construct
p ce_type
)
1531 | CI
_ | CIself
-> env, obj_ty
1533 let c_ty = r_witness, snd
c_ty in
1534 (* When constructing from a (classname) variable, the variable
1535 * dictates what the constructed object is going to be. This allows
1536 * for generic and dependent types to be correctly carried
1537 * through the 'new $foo()' iff the constructed obj_ty is a
1538 * supertype of the variable-dictated c_ty *)
1539 let env = SubType.sub_type
env obj_ty c_ty in
1543 (* FIXME: we need to separate our instantiability into two parts. Currently,
1544 * all this function is doing is checking if a given type is inhabited --
1545 * that is, whether there are runtime values of type T. However,
1546 * instantiability should be the stricter notion that T has a runtime
1547 * constructor; that is, `new T()` should be valid. In particular, interfaces
1548 * are inhabited, but not instantiable.
1549 * To make this work with classname, we likely need to add something like
1550 * concrete_classname<T>, where T cannot be an interface.
1552 and instantiable_cid
p env cid =
1553 let env, class_id
= class_id_for_new
p env cid in
1554 (match class_id
with
1555 | Some
((pos, name), class_, c_ty) when
1556 class_.tc_kind
= Ast.Ctrait
|| class_.tc_kind
= Ast.Cenum
->
1558 | CIexpr
_ | CI
_ ->
1559 uninstantiable_error
p cid class_.tc_pos
name pos c_ty;
1561 | CIstatic
| CIparent
| CIself
-> env, class_id
1563 | Some
((pos, name), class_, c_ty) when
1564 class_.tc_kind
= Ast.Cabstract
&& class_.tc_final
->
1565 uninstantiable_error
p cid class_.tc_pos
name pos c_ty;
1567 | None
| Some
_ -> env, class_id
)
1569 and uninstantiable_error reason_pos
cid c_tc_pos c_name c_usage_pos
c_ty =
1570 let reason_msgl = match cid with
1572 let ty_str = "This would be "^
Typing_print.error
(snd
c_ty) in
1573 [(reason_pos
, ty_str)]
1575 Errors.uninstantiable_class c_usage_pos c_tc_pos c_name
reason_msgl
1577 and exception_ty
pos env ty =
1578 let exn_ty = Reason.Rthrow
pos, Tclass
((pos, SN.Classes.cException
), []) in
1579 Type.sub_type
pos (Reason.URthrow
) env exn_ty ty
1581 and shape_field_pos
= function
1583 | SFclass_const
((cls_pos
, _), (member_pos
, _)) -> Pos.btw cls_pos member_pos
1585 and check_shape_keys_validity
env pos keys
=
1586 (* If the key is a class constant, get its class name and type. *)
1587 let get_field_info env key
=
1588 let key_pos = shape_field_pos key
in
1589 (* Empty strings or literals that start with numbers are not
1590 permitted as shape field names. *)
1592 | SFlit
(_, key_name
) ->
1593 if (String.length key_name
= 0) then
1594 (Errors.invalid_shape_field_name_empty
key_pos)
1595 else if (key_name
.[0] >= '
0'
&& key_name
.[0] <='
9'
) then
1596 (Errors.invalid_shape_field_name_number
key_pos);
1598 | SFclass_const
(_, cls
as x
, y
) ->
1599 let env, ty = class_const
env pos (CI x
, y
) in
1600 let env = Typing_enum.check_valid_array_key_type
1601 Errors.invalid_shape_field_type ~allow_any
:false
1603 env, key_pos, Some
(cls
, ty))
1606 let check_field witness_pos witness_info
env key
=
1607 let env, key_pos, key_info
= get_field_info env key
in
1608 (match witness_info
, key_info
with
1610 Errors.invalid_shape_field_literal
key_pos witness_pos
; env
1612 Errors.invalid_shape_field_const
key_pos witness_pos
; env
1614 | Some
(cls1
, ty1), Some
(cls2
, ty2) ->
1615 if cls1
<> cls2
then
1616 Errors.shape_field_class_mismatch
1617 key_pos witness_pos
(strip_ns cls2
) (strip_ns cls1
);
1618 (* We want to use our own error message here instead of the normal
1619 * unification one. *)
1621 (fun () -> Unify.iunify
env ty1 ty2)
1623 Errors.shape_field_type_mismatch
1625 (Typing_print.error
(snd
ty2)) (Typing_print.error
(snd
ty1));
1629 (* Sort the keys by their positions since the error messages will make
1630 * more sense if we take the one that appears first as canonical and if
1631 * they are processed in source order. *)
1632 let cmp_keys x y
= Pos.compare
(shape_field_pos x
) (shape_field_pos y
) in
1633 let keys = List.sort
cmp_keys keys in
1637 | witness
:: rest_keys
->
1638 let env, pos, info
= get_field_info env witness
in
1639 List.fold_left ~f
:(check_field pos info
) ~init
:env rest_keys
1641 and check_valid_rvalue
p env ty =
1642 let env, folded_ty
= TUtils.fold_unresolved
env ty in
1643 let _deliberately_discarded_env, folded_ety
=
1644 Env.expand_type
env folded_ty
in
1645 match folded_ety
with
1646 | r, Tprim Tnoreturn
->
1647 let () = Errors.noreturn_usage
p
1648 (Reason.to_string
"A noreturn function always throws or exits" r)
1651 let () = Errors.void_usage
p
1652 (Reason.to_string
"A void function doesn't return a value" r)
1656 and set_valid_rvalue
p env x
ty =
1657 let ty = check_valid_rvalue
p env ty in
1658 let env = Env.set_local
env x
ty in
1659 (* We are assigning a new value to the local variable, so we need to
1660 * generate a new expression id
1662 let env = Env.set_local_expr_id
env x
(Ident.tmp
()) in
1665 (* Deal with assignment of a value of type ty2 to lvalue e1 *)
1666 and assign
p env e1
ty2 =
1668 | (_, Lvar
(_, x
)) ->
1669 set_valid_rvalue
p env x
ty2
1670 | (_, Lplaceholder
_) ->
1671 let placeholder_ty = Reason.Rplaceholder
p, (Tprim Tvoid
) in
1674 let env, folded_ty2
= TUtils.fold_unresolved
env ty2 in
1675 let env, folded_ety2
= Env.expand_type
env folded_ty2
in
1676 (match folded_ety2
with
1677 | _, Tclass
((_, x
), [elt_type
])
1678 when x
= SN.Collections.cVector
1679 || x
= SN.Collections.cImmVector
1680 || x
= SN.Collections.cConstVector
->
1681 let env, _ = List.map_env
env el
begin fun env e
->
1682 assign
(fst e
) env e elt_type
1685 | _, Tarraykind
(AKvec elt_type
) ->
1686 let env, _ = List.map_env
env el
begin fun env e
->
1687 assign
(fst e
) env e elt_type
1690 | r, Tarraykind AKany
1691 | r, Tarraykind AKempty
1693 let env, _ = List.map_env
env el
begin fun env e
->
1694 assign
(fst e
) env e
(r, Tany
)
1697 | r, Tclass
((_, coll
), [ty1; ty2]) when coll
= SN.Collections.cPair
->
1700 let env, _ = assign
p env x1
ty1 in
1701 let env, _ = assign
p env x2
ty2 in
1704 Errors.pair_arity
p;
1707 | r, (Ttuple
_ | Tarraykind
(AKtuple
_) as tuple
) ->
1709 let p2 = Reason.to_pos
r in
1710 let tyl = match tuple
with
1712 | Tarraykind
(AKtuple fields
) -> List.rev
(IMap.values fields
)
1713 | _ -> Errors.internal_error
p2 "Unexpected tuple type"; [] in
1714 let size1 = List.length el
in
1715 let size2 = List.length
tyl in
1718 Errors.tuple_arity
p2 size2 p1 size1;
1722 let env = List.fold2_exn el
tyl ~f
:begin fun env lvalue
ty2 ->
1723 fst
(assign
p env lvalue
ty2)
1726 | _, Tabstract
(ak
, tyopt
) ->
1727 begin match TUtils.get_as_constraints
env ak tyopt
with
1728 | None
-> assign_simple
p env e1
ty2
1729 | Some
ty -> assign
p env e1
ty
1731 | _, (Tmixed
| Tarraykind
_ | Toption
_ | Tprim
_
1732 | Tvar
_ | Tfun
_ | Tanon
(_, _)
1733 | Tunresolved
_ | Tclass
(_, _) | Tobject
| Tshape
_) ->
1734 assign_simple
p env e1
ty2
1738 let _, locals
as lenv = env.Env.lenv in
1739 let no_fakes = Env.empty_fake_members
, locals
in
1740 (* In this section, we check that the assignment is compatible with
1741 * the real type of a member. Remember that members can change
1742 * type (cf fake_members). But when we assign a value to $this->x,
1743 * we want to make sure that the type assign to $this->x is compatible
1744 * with the actual type hint. In this portion of the code, type-check
1745 * the assignment in an environment without fakes, and therefore
1746 * check that the assignment is compatible with the type of
1749 let env, real_type
= lvalue
{ env with Env.lenv = no_fakes } e1
in
1750 let env, exp_real_type
= Env.expand_type
env real_type
in
1751 let env = { env with Env.lenv = lenv } in
1752 let env, ety2
= Env.expand_type
env ty2 in
1753 let real_type_list =
1754 match exp_real_type
with
1755 | _, Tunresolved
tyl -> tyl
1758 let env = List.fold_left
real_type_list ~f
:begin fun env real_type
->
1759 Type.sub_type
p (Reason.URassign
) env real_type ety2
1762 | _, Obj_get
((_, This
| _, Lvar
_ as obj
),
1763 (_, Id
(_, member_name
)),
1765 let env, local = Env.FakeMembers.make
p env obj member_name
in
1766 let () = (match obj
with
1768 Typing_suggest.save_member member_name
env exp_real_type
ty2
1771 set_valid_rvalue
p env local ty2
1772 | _, Class_get
(x
, (_, y
)) ->
1773 let env, local = Env.FakeMembers.make_static
p env x y
in
1774 let env, ty3
= set_valid_rvalue
p env local ty2 in
1778 Typing_suggest.save_member y
env exp_real_type
ty2;
1783 | _, Array_get
((_, Lvar
(_, lvar
)) as shape
, ((Some
_) as e2)) ->
1784 let access_type = Typing_arrays.static_array_access
env e2 in
1785 (* In the case of an assignment of the form $x['new_field'] = ...;
1786 * $x could be a shape where the field 'new_field' is not yet defined.
1787 * When that is the case we want to add the field to its type.
1789 let env, shape_ty
= expr
env shape
in
1790 let env, shape_ty
= Typing_arrays.update_array_type_on_lvar_assignment
1791 p access_type env shape_ty
in
1792 let env, _ = set_valid_rvalue
p env lvar shape_ty
in
1793 (* We still need to call assign_simple in order to bind the freshly
1794 * created variable in added shape field. Moreover, it's needed because
1795 * shape_ty could be more than just a shape. It could be an unresolved
1796 * type where some elements are shapes and some others are not.
1798 assign_simple
p env e1
ty2
1800 Errors.this_lvalue
p;
1801 env, (Reason.Rwitness
p, Tany
)
1802 | pref
, Unop
(Ast.Uref
, e1'
) ->
1803 (* references can be "lvalues" in foreach bindings *)
1804 if Env.is_strict
env then
1805 Errors.reference_expr pref
;
1806 assign
p env e1'
ty2
1808 assign_simple
p env e1
ty2
1810 and assign_simple
pos env e1
ty2 =
1811 let env, ty1 = lvalue
env e1
in
1813 let ty2 = check_valid_rvalue
pos env ty2 in
1815 let env, ty2 = TUtils.unresolved
env ty2 in
1816 let env = Type.sub_type
pos (Reason.URassign
) env ty1 ty2 in
1819 and array_field_value
env = function
1821 | Nast.AFkvalue
(_, x
) ->
1822 let env, ty = expr
env x
in
1823 Typing_env.unbind
env ty
1825 and yield_field_value
env x
= array_field_value
env x
1827 and array_field_key
env = function
1828 | Nast.AFvalue
(p, _) ->
1829 env, (Reason.Rwitness
p, Tprim Tint
)
1830 | Nast.AFkvalue
(x
, _) ->
1831 let env, ty = expr
env x
in
1832 Typing_env.unbind
env ty
1834 and yield_field_key
env = function
1835 | Nast.AFvalue
(p, _) ->
1836 env, (match Env.get_fn_kind
env with
1839 Errors.internal_error
p "yield found in non-generator";
1842 (Reason.Rwitness
p, Tprim Tint
)
1843 | Ast.FAsyncGenerator
->
1844 (Reason.Ryield_asyncnull
p, Toption
(Env.fresh_type
())))
1845 | Nast.AFkvalue
(x
, _) ->
1848 and akshape_field
env = function
1849 | Nast.AFkvalue
(k
, v
) ->
1850 let env, tk
= expr
env k
in
1851 let env, tk
= Typing_env.unbind
env tk
in
1852 let env, tk
= TUtils.unresolved
env tk
in
1853 let env, tv
= expr
env v
in
1854 let env, tv
= Typing_env.unbind
env tv
in
1855 let env, tv
= TUtils.unresolved
env tv
in
1856 let field_name = match TUtils.shape_field_name
env Pos.none
(snd k
) with
1857 | Some
field_name -> field_name
1858 | None
-> assert false in (* Typing_arrays.is_shape_like_array
1859 * should have prevented this *)
1860 env, (field_name, (tk
, tv
))
1861 | Nast.AFvalue
_ -> assert false (* Typing_arrays.is_shape_like_array
1862 * should have prevented this *)
1863 and aktuple_field
env = function
1865 let env, tv
= expr
env v
in
1866 let env, tv
= Typing_env.unbind
env tv
in
1867 TUtils.unresolved
env tv
1868 | Nast.AFkvalue
_ -> assert false (* check_consistent_fields
1869 * should have prevented this *)
1870 and check_parent_construct
pos env el uel env_parent
=
1871 let check_not_abstract = false in
1872 let env, env_parent
= Phase.localize_with_self
env env_parent
in
1873 let env, parent
= new_object ~
check_not_abstract pos env CIparent el uel
in
1874 let env, _ = Type.unify
pos (Reason.URnone
) env env_parent parent
in
1875 env, (Reason.Rwitness
pos, Tprim Tvoid
)
1877 and call_parent_construct
pos env el uel
=
1878 let parent = Env.get_parent
env in
1881 check_parent_construct
pos env el uel
parent
1882 | _, (Tany
| Tmixed
| Tarray
(_, _) | Tgeneric
(_, _) | Toption
_ | Tprim
_
1883 | Tfun
_ | Ttuple
_ | Tshape
_ | Taccess
(_, _) | Tthis
1884 ) -> (* continue here *)
1885 let default = env, (Reason.Rnone
, Tany
) in
1886 match Env.get_self
env with
1887 | _, Tclass
((_, self
), _) ->
1888 (match Env.get_class
env self
with
1889 | Some
({tc_kind
= Ast.Ctrait
; _}
1891 (match trait_most_concrete_req_class trait
env with
1892 | None
-> Errors.parent_in_trait
pos; default
1893 | Some
(_, parent_ty
) ->
1894 check_parent_construct
pos env el uel parent_ty
1897 if not self_tc
.tc_members_fully_known
1898 then () (* Don't know the hierarchy, assume it's correct *)
1899 else Errors.undefined_parent
pos;
1901 | None
-> assert false)
1902 | _, (Tany
| Tmixed
| Tarraykind
_ | Toption
_
1903 | Tprim
_ | Tfun
_ | Ttuple
_ | Tshape
_ | Tvar
_
1904 | Tabstract
(_, _) | Tanon
(_, _) | Tunresolved
_ | Tobject
1906 Errors.parent_outside_class
pos; default
1908 (* parent::method() in a class definition invokes the specific parent
1909 * version of the method ... it better be callable *)
1910 and check_abstract_parent_meth mname
pos fty =
1911 if is_abstract_ft
fty then Errors.parent_abstract_call mname
pos (Reason.to_pos
(fst
fty)) ;
1914 and is_abstract_ft
fty = match fty with
1915 | _r, Tfun
{ ft_abstract
= true; _ } -> true
1916 | _r, (Tany
| Tmixed
| Tarraykind
_ | Toption
_ | Tprim
_
1917 | Tvar
_ | Tfun
_ | Tclass
(_, _) | Tabstract
(_, _) | Ttuple
_
1918 | Tanon
_ | Tunresolved
_ | Tobject
| Tshape
_
1922 (* Depending on the kind of expression we are dealing with
1923 * The typing of call is different.
1925 and dispatch_call
p env call_type
(fpos
, fun_expr
as e
) el uel
=
1927 | Id
(_, pseudo_func
) when pseudo_func
= SN.SpecialFunctions.echo
->
1928 let env, _ = List.map_env
env el expr
in
1929 env, (Reason.Rwitness
p, Tprim Tvoid
)
1930 | Id
(_, pseudo_func
)
1932 pseudo_func
= SN.PseudoFunctions.isset
1933 || pseudo_func
= SN.PseudoFunctions.empty
->
1934 let env, _ = List.map_env
env el expr
in
1936 Errors.unpacking_disallowed_builtin_function
p pseudo_func
;
1937 if Env.is_strict
env then
1938 Errors.isset_empty_in_strict
p pseudo_func
;
1939 env, (Reason.Rwitness
p, Tprim Tbool
)
1940 | Id
(_, pseudo_func
) when pseudo_func
= SN.PseudoFunctions.unset
->
1941 let env, _ = List.map_env
env el expr
in
1943 Errors.unpacking_disallowed_builtin_function
p pseudo_func
;
1944 let env = if Env.is_strict
env then
1946 | [(_, Array_get
(ea
, Some
_))], [] ->
1947 let env, ty = expr
env ea
in
1948 if List.exists ~f
:(fun super
-> SubType.is_sub_type
env super
ty) [
1949 (Reason.Rnone
, (Tclass
((Pos.none
, SN.Collections.cDict
),
1950 [(Reason.Rnone
, Tany
); (Reason.Rnone
, Tany
)])));
1951 (Reason.Rnone
, Tarraykind AKany
)
1954 let env, (r, ety
) = Env.expand_type
env ty in
1955 Errors.unset_nonidx_in_strict
1957 (Reason.to_string
("This is " ^
Typing_print.error ety
) r);
1960 | _ -> Errors.unset_nonidx_in_strict
p []; env)
1963 | [(p, Obj_get
(_, _, OG_nullsafe
))] ->
1965 Errors.nullsafe_property_write_context
p;
1966 env, (Reason.Rwitness
p, Tany
)
1968 | _ -> env, (Reason.Rwitness
p, Tprim Tvoid
))
1969 | Id
(cp
, get_called_class
) when
1970 get_called_class
= SN.StdlibFunctions.get_called_class
1971 && el
= [] && uel
= [] ->
1972 (* get_called_class fetches the late-bound class *)
1973 if Env.is_outside_class
env then Errors.static_outside_class
p;
1974 class_const
env p (CIstatic
, (cp
, SN.Members.mClass
))
1975 | Id
((_, array_filter
) as id
)
1976 when array_filter
= SN.StdlibFunctions.array_filter
&& el
<> [] && uel
= [] ->
1977 (* dispatch the call to typecheck the arguments *)
1978 let env, fty = fun_type_of_id
env id
in
1979 let env, fty = Env.expand_type
env fty in
1980 let env, res
= call
p env fty el uel
in
1981 (* but ignore the result and overwrite it with custom return type *)
1982 let x = List.hd_exn el
in
1983 let env, ty = expr
env x in
1984 let explain_array_filter (r, t
) =
1985 (Reason.Rarray_filter
(p, r), t
) in
1986 let get_value_type env tv
=
1987 let env, tv
= if List.length el
> 1 then env, tv
else non_null
env tv
in
1988 env, explain_array_filter tv
in
1989 let rec get_array_filter_return_type env ty =
1990 let env, ety
= Env.expand_type
env ty in
1992 | (_, Tarraykind
(AKany
| AKempty
)) as array_type
->
1994 | (_, Tarraykind
(AKtuple
_)) ->
1995 let env, ty = Typing_arrays.downcast_aktypes
env ty in
1996 get_array_filter_return_type env ty
1997 | (r, Tarraykind
(AKvec tv
)) ->
1998 let env, tv
= get_value_type env tv
in
1999 env, (r, Tarraykind
(AKvec tv
))
2000 | (r, Tunresolved
x) ->
2001 let env, x = List.map_env
env x get_array_filter_return_type in
2002 env, (r, Tunresolved
x)
2006 let tk, tv
= Env.fresh_type
(), Env.fresh_type
() in
2009 let keyed_container = (
2012 (Pos.none
, SN.Collections.cKeyedContainer
), [tk; tv
]
2015 let env = SubType.sub_type
env keyed_container ety
in
2016 let env, tv
= get_value_type env tv
in
2017 env, (r, Tarraykind
(AKmap
(
2018 (explain_array_filter tk),
2021 (fun _ -> Errors.try_
2026 (Pos.none
, SN.Collections.cContainer
), [tv
]
2029 let env = SubType.sub_type
env container ety
in
2030 let env, tv
= get_value_type env tv
in
2031 env, (r, Tarraykind
(AKmap
(
2032 (explain_array_filter (r, Tprim Tarraykey
)),
2034 (fun _ -> env, res
)))
2035 in get_array_filter_return_type env ty
2036 | Id
(p, type_structure
)
2037 when type_structure
= SN.StdlibFunctions.type_structure
2038 && (List.length el
= 2) && uel
= [] ->
2042 | p, Nast.String cst
->
2043 (* find the class constant implicitly defined by the typeconst *)
2044 let cid = (match e1
with
2045 | _, Class_const
(cid, (_, x))
2046 | _, Class_get
(cid, (_, x)) when x = SN.Members.mClass
-> cid
2047 | _ -> Nast.CIexpr e1
) in
2048 class_const ~incl_tc
:true env p (cid, cst
)
2050 Errors.illegal_type_structure
p "second argument is not a string";
2051 env, (Reason.Rnone
, Tany
))
2052 | _ -> assert false)
2053 | Id
((_, array_map
) as x)
2054 when array_map
= SN.StdlibFunctions.array_map
&& el
<> [] && uel
= [] ->
2055 let env, fty = fun_type_of_id
env x in
2056 let env, fty = Env.expand_type
env fty in
2057 let env, fty = match fty, el
with
2058 | ((r_fty
, Tfun
fty), _::args
) when args
<> [] ->
2059 let arity = List.length args
in
2061 Builds a function with signature:
2063 function<T1, ..., Tn, Tr>(
2064 (function(T1, ..., Tn):Tr),
2070 where R is constructed by build_output_container applied to Tr
2072 let build_function build_output_container
=
2073 let vars = List.map args
(fun _ -> Env.fresh_type
()) in
2074 let tr = Env.fresh_type
() in
2078 ft_pos
= fty.ft_pos
;
2079 ft_deprecated
= None
;
2080 ft_abstract
= false;
2081 ft_arity
= Fstandard
(arity, arity); ft_tparams
= [];
2082 ft_params
= List.map
vars (fun x -> (None
, x));
2086 let containers = List.map
vars (fun var
->
2089 Tclass
((fty.ft_pos
, SN.Collections.cContainer
), [var
])
2093 (r_fty
, Tfun
{fty with
2094 ft_arity
= Fstandard
(arity+1, arity+1);
2095 ft_params
= f::containers;
2096 ft_ret
= build_output_container
tr;
2100 Takes a Container type and returns a function that can "pack" a type
2101 into an array of appropriate shape, preserving the key type, i.e.:
2102 array -> f, where f R = array
2103 array<X> -> f, where f R = array<R>
2104 array<X, Y> -> f, where f R = array<X, R>
2105 Vector<X> -> f where f R = array<R>
2106 KeyedContainer<X, Y> -> f, where f R = array<X, R>
2107 Container<X> -> f, where f R = array<arraykey, R>
2108 X -> f, where f R = Y
2110 let rec build_output_container
2111 (env:Env.env) (x:locl
ty) : (Env.env * (locl
ty -> locl
ty)) =
2112 let env, x = Env.expand_type
env x in (match x with
2113 | (_, Tarraykind
(AKany
| AKempty
)) as array_type
->
2114 env, (fun _ -> array_type
)
2115 | (_, Tarraykind
(AKtuple
_ )) ->
2116 let env, x = Typing_arrays.downcast_aktypes
env x in
2117 build_output_container env x
2118 | (r, Tarraykind AKvec
_) ->
2119 env, (fun tr -> (r, Tarraykind
(AKvec
(tr))) )
2120 | ((_, Tany
) as any) ->
2122 | (r, Tunresolved
x) ->
2123 let env, x = List.map_env
env x build_output_container in
2124 env, (fun tr -> (r, Tunresolved
(List.map
x (fun f -> f tr))))
2126 let tk, tv
= Env.fresh_type
(), Env.fresh_type
() in
2127 let try_vector env =
2131 (fty.ft_pos
, SN.Collections.cConstVector
), [tv
]
2134 let env = SubType.sub_type
env vector x in
2135 env, (fun tr -> (r, Tarraykind
(
2138 let try_keyed_container env =
2139 let keyed_container = (
2142 (fty.ft_pos
, SN.Collections.cKeyedContainer
), [tk; tv
]
2145 let env = SubType.sub_type
env keyed_container x in
2146 env, (fun tr -> (r, Tarraykind
(AKmap
(
2150 let try_container env =
2154 (fty.ft_pos
, SN.Collections.cContainer
), [tv
]
2157 let env = SubType.sub_type
env container x in
2158 env, (fun tr -> (r, Tarraykind
(AKmap
(
2159 (r, Tprim Tarraykey
),
2164 (fun _ -> Errors.try_
2166 try_keyed_container env)
2167 (fun _ -> Errors.try_
2170 (fun _ -> env, (fun _ -> (Reason.Rwitness
p, Tany
)))))) in
2172 Single argument calls preserve the key type, multi argument
2173 calls always return an array<Tr>
2177 let env, x = expr
env x in
2178 let env, output_container
= build_output_container env x in
2179 env, build_function output_container
2181 env, build_function (fun tr ->
2182 (r_fty
, Tarraykind
(AKvec
(tr)))))
2184 call
p env fty el
[]
2185 | Id
((_, idx
) as id
) when idx
= SN.FB.idx
->
2186 (* Directly call get_fun so that we can muck with the type before
2187 * instantiation -- much easier to work in terms of Tgeneric Tk/Tv than
2188 * trying to figure out which Tvar is which. *)
2189 (match Env.get_fun
env (snd id
) with
2191 let param1, (name2
, (r2
, _)), (name3
, (r3
, _)) =
2192 match fty.ft_params
with
2193 | [param1; param2
; param3
] -> param1, param2
, param3
2194 | _ -> assert false in
2195 let params, ret = match List.length el
with
2197 let param2 = (name2
, (r2
, Toption
(r2
, Tgeneric
("Tk", [])))) in
2198 let rret = fst
fty.ft_ret
in
2199 let ret = (rret, Toption
(rret, Tgeneric
("Tv", []))) in
2200 [param1; param2], ret
2202 let param2 = (name2
, (r2
, Tgeneric
("Tk", []))) in
2203 let param3 = (name3
, (r3
, Tgeneric
("Tv", []))) in
2204 let ret = (fst
fty.ft_ret
, Tgeneric
("Tv", [])) in
2205 [param1; param2; param3], ret
2206 | _ -> fty.ft_params
, fty.ft_ret
in
2207 let fty = { fty with ft_params
= params; ft_ret
= ret } in
2208 let ety_env = Phase.env_with_self
env in
2209 let env, fty = Phase.localize_ft ~
ety_env env fty in
2210 let tfun = Reason.Rwitness
fty.ft_pos
, Tfun
fty in
2211 call
p env tfun el
[]
2212 | None
-> unbound_name env id
)
2213 | Class_const
(CI
(_, shapes
) as class_id
, ((_, idx
) as method_id
))
2214 when shapes
= SN.Shapes.cShapes
&& idx
= SN.Shapes.idx
->
2215 overload_function
p env class_id method_id el uel
2216 begin fun env fty res el
-> match el
with
2218 let env, shape_ty
= expr
env shape
in
2219 Typing_shapes.idx
env fty shape_ty field None
2220 | [shape
; field
; default] ->
2221 let env, shape_ty
= expr
env shape
in
2222 let env, default_ty
= expr
env default in
2223 Typing_shapes.idx
env fty shape_ty field
2224 (Some
((fst
default), default_ty
))
2227 | Class_const
(CI
(_, shapes
) as class_id
, ((_, key_exists
) as method_id
))
2228 when shapes
= SN.Shapes.cShapes
&& key_exists
= SN.Shapes.keyExists
->
2229 overload_function
p env class_id method_id el uel
2230 begin fun env fty res el
-> match el
with
2232 let env, shape_ty
= expr
env shape
in
2233 (* try acessing the field, to verify existence, but ignore
2234 * the returned type and keep the one coming from function
2235 * return type hint *)
2236 let env, _ = Typing_shapes.idx
env fty shape_ty field None
in
2240 | Class_const
(CI
(_, shapes
) as class_id
, ((_, remove_key
) as method_id
))
2241 when shapes
= SN.Shapes.cShapes
&& remove_key
= SN.Shapes.removeKey
->
2242 overload_function
p env class_id method_id el uel
2243 begin fun env _ res el
-> match el
with
2244 | [shape
; field
] -> begin match shape
with
2245 | (_, Lvar
(_, lvar
)) ->
2246 let env, shape_ty
= expr
env shape
in
2248 Typing_shapes.remove_key
p env shape_ty field
in
2249 let env, _ = set_valid_rvalue
p env lvar shape_ty
in
2252 Errors.invalid_shape_remove_key
(fst shape
);
2257 | Class_const
(CI
(_, shapes
) as class_id
, ((_, to_array
) as method_id
))
2258 when shapes
= SN.Shapes.cShapes
&& to_array
= SN.Shapes.toArray
->
2259 overload_function
p env class_id method_id el uel
2260 begin fun env _ res el
-> match el
with
2262 let env, shape_ty
= expr
env shape
in
2263 Typing_shapes.to_array
env shape_ty res
2266 | Class_const
(CIparent
, (_, construct
))
2267 when construct
= SN.Members.__construct
->
2268 Typing_hooks.dispatch_parent_construct_hook
env p;
2269 call_parent_construct
p env el uel
2270 | Class_const
(CIparent
, m
) ->
2271 let env, ty1 = static_class_id
p env CIparent
in
2272 if Env.is_static
env
2274 (* in static context, you can only call parent::foo() on static
2276 let env, fty = class_get ~is_method
:true ~is_const
:false env ty1 m CIparent
in
2277 let env, fty = Env.expand_type
env fty in
2278 let fty = check_abstract_parent_meth
(snd m
) p fty in
2279 call
p env fty el uel
2282 (* in instance context, you can call parent:foo() on static
2283 * methods as well as instance methods *)
2284 (match class_contains_smethod
env ty1 m
with
2286 (* parent::nonStaticFunc() is really weird. It's calling a method
2287 * defined on the parent class, but $this is still the child class.
2288 * We can deal with this by hijacking the continuation that
2289 * calculates the SN.Typehints.this type *)
2290 let this_ty = ExprDepTy.make
env CIstatic
2291 (Reason.Rwitness fpos
, TUtils.this_of
(Env.get_self
env)) in
2292 let k_lhs _ = this_ty in
2293 let env, method_
, _ =
2294 obj_get_ ~is_method
:true ~
nullsafe:None
env ty1 CIparent m
2295 begin fun (env, fty, _) ->
2296 let env, fty = Env.expand_type
env fty in
2297 let fty = check_abstract_parent_meth
(snd m
) p fty in
2298 let env, method_
= call
p env fty el uel
in
2305 let env, fty = class_get ~is_method
:true ~is_const
:false env ty1 m CIparent
in
2306 let env, fty = Env.expand_type
env fty in
2307 let fty = check_abstract_parent_meth
(snd m
) p fty in
2308 call
p env fty el uel
2311 | Class_const
(e1
, m
) ->
2312 TUtils.process_static_find_ref e1 m
;
2313 let env, ty1 = static_class_id
p env e1
in
2314 let env, fty = class_get ~is_method
:true ~is_const
:false env ty1 m e1
in
2315 let env, fty = Env.expand_type
env fty in
2316 let () = match e1
with
2317 | CIself
when is_abstract_ft
fty ->
2318 (match Env.get_self
env with
2319 | _, Tclass
((_, self
), _) ->
2320 (* at runtime, self:: in a trait is a call to whatever
2321 * self:: is in the context of the non-trait "use"-ing
2322 * the trait's code *)
2323 (match Env.get_class
env self
with
2324 | Some
{ tc_kind
= Ast.Ctrait
; _ } -> ()
2325 | _ -> Errors.self_abstract_call
(snd m
) p (Reason.to_pos
(fst
fty))
2328 | CI
c when is_abstract_ft
fty ->
2329 Errors.classname_abstract_call
(snd
c) (snd m
) p (Reason.to_pos
(fst
fty))
2331 call
p env fty el uel
2332 | Obj_get
(e1
, (_, Id m
), nullflavor
) ->
2333 let is_method = call_type
= Cnormal
in
2334 let env, ty1 = expr
env e1
in
2336 (match nullflavor
with
2337 | OG_nullthrows
-> None
2338 | OG_nullsafe
-> Some
p
2340 let fn = (fun (env, fty, _) ->
2341 let env, fty = Env.expand_type
env fty in
2342 let env, method_
= call
p env fty el uel
in
2343 env, method_
, None
) in
2344 obj_get ~
is_method ~
nullsafe env ty1 (CIexpr e1
) m
fn
2347 Typing_hooks.dispatch_id_hook
x env;
2348 let env, fty = fun_type_of_id
env x in
2349 let env, fty = Env.expand_type
env fty in
2350 call
p env fty el uel
2352 let env, fty = expr
env e
in
2353 let env, fty = Env.expand_type
env fty in
2354 call
p env fty el uel
2356 and fun_type_of_id
env x =
2357 Typing_hooks.dispatch_fun_id_hook
x;
2359 match Env.get_fun
env (snd
x) with
2360 | None
-> unbound_name env x
2362 let ety_env = Phase.env_with_self
env in
2363 let env, fty = Phase.localize_ft ~
ety_env env fty in
2364 env, (Reason.Rwitness
fty.ft_pos
, Tfun
fty)
2368 (*****************************************************************************)
2369 (* Function type-checking expressions accessing an array (example: $x[...]).
2370 * The parameter is_lvalue is true when the expression is on the left hand
2371 * side of an assignment (example: $x[...] = 0).
2373 (*****************************************************************************)
2374 and array_get
is_lvalue p env ty1 ety1
e2 ty2 =
2375 (* This is a little weird -- we enforce the right arity when you use certain
2376 * collections, even in partial mode (where normally completely omitting the
2377 * type parameter list is admitted). Basically the "omit type parameter"
2378 * hole was for compatibility with certain interfaces like ArrayAccess, not
2379 * for collections! But it's hard to go back on now, so since we've always
2380 * errored (with an inscrutable error message) when you try to actually use
2381 * a collection with omitted type parameters, we can continue to error and
2382 * give a more useful error message. *)
2383 let arity_error (_, name) =
2384 Errors.array_get_arity
p name (Reason.to_pos
(fst
ty1))
2387 | Tunresolved
tyl ->
2388 let env, tyl = List.map_env
env tyl begin fun env ty1 ->
2389 let env, ety1
= Env.expand_type
env ty1 in
2390 array_get
is_lvalue p env ty1 ety1
e2 ty2
2392 env, (fst ety1
, Tunresolved
tyl)
2393 | Tarraykind
(AKvec
ty) ->
2394 let ty1 = Reason.Ridx
(fst
e2, fst ety1
), Tprim Tint
in
2395 let env = Type.sub_type
p Reason.index_array
env ty1 ty2 in
2397 | Tclass
((_, cn
) as id
, argl
)
2398 when cn
= SN.Collections.cVector
->
2399 let ty = match argl
with
2401 | _ -> arity_error id
; Reason.Rwitness
p, Tany
in
2402 let ty1 = Reason.Ridx_vector
(fst
e2), Tprim Tint
in
2403 let env = Type.sub_type
p (Reason.index_class cn
) env ty1 ty2 in
2405 | Tclass
((_, cn
) as id
, argl
)
2406 when cn
= SN.Collections.cMap
2407 || cn
= SN.Collections.cStableMap
2408 || cn
= SN.Collections.cDict
->
2409 let (k
, v
) = match argl
with
2413 let any = (Reason.Rwitness
p, Tany
) in
2416 let env, ty2 = TUtils.unresolved
env ty2 in
2417 let env, _ = Type.unify
p (Reason.index_class cn
) env k
ty2 in
2419 (* Certain container/collection types are intended to be immutable/const,
2420 * thus they should never appear as a lvalue when indexing i.e.
2422 * $x[0] = 100; // ERROR
2425 | Tclass
((_, cn
) as id
, argl
)
2426 when cn
= SN.Collections.cConstMap
2427 || cn
= SN.Collections.cImmMap
2428 || cn
= SN.Collections.cIndexish
2429 || cn
= SN.Collections.cKeyedContainer
->
2431 error_const_mutation
env p ety1
2433 let (k
, v
) = match argl
with
2437 let any = (Reason.Rwitness
p, Tany
) in
2440 let env = Type.sub_type
p (Reason.index_class cn
) env k
ty2 in
2442 | Tclass
((_, cn
) as id
, argl
)
2443 when not
is_lvalue &&
2444 (cn
= SN.Collections.cConstVector
|| cn
= SN.Collections.cImmVector
) ->
2445 let ty = match argl
with
2447 | _ -> arity_error id
; Reason.Rwitness
p, Tany
in
2448 let ty1 = Reason.Ridx
(fst
e2, fst ety1
), Tprim Tint
in
2449 let env, _ = Type.unify
p (Reason.index_class cn
) env ty2 ty1 in
2451 | Tclass
((_, cn
), _)
2453 (cn
= SN.Collections.cConstVector
|| cn
= SN.Collections.cImmVector
) ->
2454 error_const_mutation
env p ety1
2455 | Tarraykind
(AKmap
(k
, v
)) ->
2456 let env, ty2 = TUtils.unresolved
env ty2 in
2457 let env, _ = Type.unify
p Reason.index_array
env k
ty2 in
2459 | Tarraykind
((AKshape
_ | AKtuple
_) as akind
) ->
2460 let key = Typing_arrays.static_array_access
env (Some
e2) in
2461 let env, result
= match key, akind
with
2462 | Typing_arrays.AKtuple_index index
, AKtuple fields
->
2463 begin match IMap.get index fields
with
2465 let ty1 = Reason.Ridx
(fst
e2, fst ety1
), Tprim Tint
in
2466 let env = Type.sub_type
p Reason.index_array
env ty1 ty2 in
2470 | Typing_arrays.AKshape_key
field_name, AKshape fdm
->
2471 begin match Nast.ShapeMap.get
field_name fdm
with
2473 let env, ty2 = TUtils.unresolved
env ty2 in
2474 let env, _ = Type.unify
p Reason.index_array
env k
ty2 in
2479 begin match result
with
2480 | Some
ty -> env, ty
2482 (* Key is dynamic, or static and not in the array - treat it as
2483 regular map or vec like array *)
2484 let env, ty1 = Typing_arrays.downcast_aktypes
env ty1 in
2485 let env, ety1
= Env.expand_type
env ty1 in
2486 array_get
is_lvalue p env ty1 ety1
e2 ty2
2488 | Tany
| Tarraykind
(AKany
| AKempty
)-> env, (Reason.Rnone
, Tany
)
2490 let ty = Reason.Rwitness
p, Tprim Tstring
in
2491 let env, ty = Type.unify
p Reason.URnone
env ty1 ty in
2492 let int = Reason.Ridx
(fst
e2, fst ety1
), Tprim Tint
in
2493 let env, _ = Type.unify
p Reason.index_array
env ty2 int in
2499 let idx = int_of_string
(snd n
) in
2500 let nth = List.nth_exn
tyl idx in
2503 Errors.typing_error
p (Reason.string_of_ureason
Reason.index_tuple
);
2504 env, (Reason.Rwitness
p, Tany
)
2507 Errors.typing_error
p (Reason.string_of_ureason
Reason.URtuple_access
);
2508 env, (Reason.Rwitness
p, Tany
)
2510 | Tclass
((_, cn
) as id
, argl
) when cn
= SN.Collections.cPair
->
2511 let (ty1, ty2) = match argl
with
2512 | [ty1; ty2] -> (ty1, ty2)
2515 let any = (Reason.Rwitness
p, Tany
) in
2521 let idx = int_of_string
(snd n
) in
2522 let nth = List.nth_exn
[ty1; ty2] idx in
2525 Errors.typing_error
p @@
2526 Reason.string_of_ureason
(Reason.index_class cn
);
2527 env, (Reason.Rwitness
p, Tany
)
2530 Errors.typing_error
p (Reason.string_of_ureason
Reason.URpair_access
);
2531 env, (Reason.Rwitness
p, Tany
)
2533 | Tshape
(_, fdm
) ->
2535 (match TUtils.shape_field_name
env p e2'
with
2537 (* there was already an error in shape_field name,
2538 don't report another one for a missing field *)
2539 env, (Reason.Rwitness
p, Tany
)
2540 | Some field
-> (match ShapeMap.get field fdm
with
2542 Errors.undefined_field
2543 p (TUtils.get_printable_shape_field_name field
);
2544 env, (Reason.Rwitness
p, Tany
)
2545 | Some
ty -> env, ty)
2548 Errors.null_container
p
2550 "This is what makes me believe it can be null"
2553 env, (Reason.Rwitness
p, Tany
)
2555 if Env.is_strict
env
2556 then error_array
env p ety1
2557 else env, (Reason.Rnone
, Tany
)
2558 | Tabstract
(AKnewtype
(ts
, [ty]), Some
(r, Tshape
(fk
, fields
)))
2559 when ts
= SN.FB.cTypeStructure
->
2560 let env, fields
= TS.transform_shapemap
env ty fields
in
2561 let ty = r, Tshape
(fk
, fields
) in
2562 array_get
is_lvalue p env ty ty e2 ty2
2563 | Tabstract
(ak
, tyopt
) ->
2564 begin match TUtils.get_as_constraints
env ak tyopt
with
2565 | None
-> error_array
env p ety1
2567 let env, ety
= Env.expand_type
env ty in
2569 (fun () -> array_get
is_lvalue p env ty ety
e2 ty2)
2570 (fun _ -> error_array
env p ety1
)
2572 | Tmixed
| Tprim
_ | Tvar
_ | Tfun
_
2573 | Tclass
(_, _) | Tanon
(_, _) ->
2574 error_array
env p ety1
2576 and array_append
is_lvalue p env ty1 =
2577 let env, ty1 = TUtils.fold_unresolved
env ty1 in
2578 let env, ety1
= Env.expand_type
env ty1 in
2580 | Tany
| Tarraykind
(AKany
| AKempty
) -> env, (Reason.Rnone
, Tany
)
2581 | Tclass
((_, n
), [ty])
2582 when n
= SN.Collections.cVector
|| n
= SN.Collections.cSet
->
2584 | Tclass
((_, n
), [])
2585 when n
= SN.Collections.cVector
|| n
= SN.Collections.cSet
->
2586 (* Handle the case where "Vector" or "Set" was used as a typehint
2587 without type parameters *)
2588 env, (Reason.Rnone
, Tany
)
2589 | Tclass
((_, n
), [tkey
; tvalue
]) when n
= SN.Collections.cMap
->
2590 (* You can append a pair to a map *)
2591 env, (Reason.Rmap_append
p, Tclass
((p, SN.Collections.cPair
), [tkey
; tvalue
]))
2592 | Tclass
((_, n
), []) when n
= SN.Collections.cMap
->
2593 (* Handle the case where "Map" was used as a typehint without
2595 env, (Reason.Rmap_append
p, Tclass
((p, SN.Collections.cPair
), []))
2596 | Tarraykind
(AKvec
ty) ->
2599 if Env.is_strict
env
2600 then error_array_append
env p ety1
2601 else env, (Reason.Rnone
, Tany
)
2602 | Tabstract
(ak
, tyopt
) ->
2603 begin match TUtils.get_as_constraints
env ak tyopt
with
2604 | None
-> error_array_append
env p ety1
2607 (fun () -> array_append
is_lvalue p env ty)
2608 (fun _ -> error_array_append
env p ety1
)
2610 | Tmixed
| Tarraykind
_ | Toption
_ | Tprim
_
2611 | Tvar
_ | Tfun
_ | Tclass
(_, _) | Ttuple
_
2612 | Tanon
(_, _) | Tunresolved
_ | Tshape
_ ->
2613 error_array_append
env p ety1
2615 and error_array
env p (r, ty) =
2616 Errors.array_access
p (Reason.to_pos
r) (Typing_print.error
ty);
2617 env, (Reason.Rwitness
p, Tany
)
2619 and error_array_append
env p (r, ty) =
2620 Errors.array_append
p (Reason.to_pos
r) (Typing_print.error
ty);
2621 env, (Reason.Rwitness
p, Tany
)
2623 and error_const_mutation
env p (r, ty) =
2624 Errors.const_mutation
p (Reason.to_pos
r) (Typing_print.error
ty);
2625 env, (Reason.Rwitness
p, Tany
)
2628 * Checks if a class (given by cty) contains a given static method.
2630 * We could refactor this + class_get
2632 and class_contains_smethod
env cty
(_pos
, mid
) =
2633 let lookup_member c =
2634 let class_ = Env.get_class
env c in
2638 Env.get_static_member
true env class_ mid
2641 | _, Tabstract
(ak
, tyopt
) ->
2642 begin match TUtils.get_as_constraints
env ak tyopt
with
2643 | Some
(_, Tclass
((_, c), _)) -> lookup_member c
2646 | _, Tclass
((_, c), _) -> lookup_member c
2647 | _, (Tany
| Tmixed
| Tarraykind
_ | Toption
_ | Tprim
_
2648 | Tvar
_ | Tfun
_ | Ttuple
_ | Tanon
(_, _)
2649 | Tunresolved
_ | Tobject
| Tshape
_)-> None
2651 and class_get ~
is_method ~is_const ?
(incl_tc
=false) env cty
(p, mid
) cid =
2654 this_for_method
env cid cty
2658 type_expansions
= [];
2660 substs
= SMap.empty
;
2661 from_class
= Some
cid;
2663 class_get_ ~
is_method ~is_const ~
ety_env ~incl_tc
env cid cty
(p, mid
)
2665 and class_get_ ~
is_method ~is_const ~
ety_env ?
(incl_tc
=false) env cid cty
2667 let env, cty
= Env.expand_type
env cty
in
2669 | _, Tany
-> env, (Reason.Rnone
, Tany
)
2670 | _, Tunresolved
tyl ->
2671 let env, tyl = List.map_env
env tyl begin fun env ty ->
2672 class_get_ ~
is_method ~is_const ~
ety_env ~incl_tc
env cid ty (p, mid
)
2674 let env, method_
= TUtils.in_var
env (fst cty
, Tunresolved
tyl) in
2676 | _, Tabstract
(ak
, tyopt
) ->
2677 begin match TUtils.get_as_constraints
env ak tyopt
with
2679 class_get_ ~
is_method ~is_const ~
ety_env ~incl_tc
env cid cty
(p, mid
)
2681 env, (Reason.Rnone
, Tany
)
2683 | _, Tclass
((_, c), paraml
) ->
2684 let class_ = Env.get_class
env c in
2686 | None
-> env, (Reason.Rnone
, Tany
)
2688 if !Typing_defs.accumulate_method_calls
then
2689 Typing_defs.accumulate_method_calls_result
:=
2690 (p, (class_.tc_name^
"::"^mid
)) ::
2691 !Typing_defs.accumulate_method_calls_result
;
2692 Typing_hooks.dispatch_smethod_hook
2693 class_ (p, mid
) env ety_env.from_class ~
is_method ~is_const
;
2696 substs
= Subst.make
class_.tc_tparams paraml
} in
2697 if is_const
then begin
2699 if incl_tc
then Env.get_const
env class_ mid
else
2700 match Env.get_typeconst
env class_ mid
with
2702 Errors.illegal_typeconst_direct_access
p;
2705 Env.get_const
env class_ mid
2709 smember_not_found
p ~is_const ~
is_method class_ mid
;
2710 env, (Reason.Rnone
, Tany
)
2711 | Some
{ cc_type
; _ } ->
2712 let env, cc_type
= Phase.localize ~
ety_env env cc_type
in
2715 let smethod = Env.get_static_member
is_method env class_ mid
in
2718 (match Env.get_static_member
is_method env class_ SN.Members.__callStatic
with
2720 smember_not_found
p ~is_const ~
is_method class_ mid
;
2721 env, (Reason.Rnone
, Tany
)
2722 | Some
{ce_visibility
= vis
; ce_type
= (r, Tfun
ft); _} ->
2723 let p_vis = Reason.to_pos
r in
2724 TVis.check_class_access
p env (p_vis, vis
) cid class_;
2725 let env, ft = Phase.localize_ft ~
ety_env env ft in
2727 ft_arity
= Fellipsis
0;
2728 ft_tparams
= []; ft_params
= [];
2731 | _ -> assert false)
2732 | Some
{ ce_visibility
= vis
; ce_type
= method_
; _ } ->
2733 let p_vis = Reason.to_pos
(fst method_
) in
2734 TVis.check_class_access
p env (p_vis, vis
) cid class_;
2736 Phase.localize ~
ety_env env method_
in
2740 | _, (Tmixed
| Tarraykind
_ | Toption
_
2741 | Tprim
_ | Tvar
_ | Tfun
_ | Ttuple
_ | Tanon
(_, _) | Tobject
2743 (* should never happen; static_class_id takes care of these *)
2744 env, (Reason.Rnone
, Tany
)
2746 and smember_not_found
pos ~is_const ~
is_method class_ member_name
=
2748 if is_const
then `class_constant
2749 else if is_method then `static_method
2750 else `class_variable
in
2752 let cid = (class_.tc_pos
, class_.tc_name
) in
2753 Errors.smember_not_found
kind pos cid member_name hint
2755 match Env.suggest_static_member
is_method class_ member_name
with
2757 (match Env.suggest_member
is_method class_ member_name
with
2758 | None
when not
class_.tc_members_fully_known
->
2759 (* no error in this case ... the member might be present
2760 * in one of the parents of class_ that the typing cannot see *)
2765 error (`closest
(pos2
, v
))
2768 error (`did_you_mean
(pos2
, v
))
2770 and member_not_found
pos ~
is_method class_ member_name
r =
2771 let kind = if is_method then `method_
else `member
in
2772 let cid = class_.tc_pos
, class_.tc_name
in
2773 let reason = Reason.to_string
2774 ("This is why I think it is an object of type "^strip_ns
class_.tc_name
) r
2777 Errors.member_not_found
kind pos cid member_name hint
reason in
2778 match Env.suggest_member
is_method class_ member_name
with
2780 (match Env.suggest_static_member
is_method class_ member_name
with
2781 | None
when not
class_.tc_members_fully_known
->
2782 (* no error in this case ... the member might be present
2783 * in one of the parents of class_ that the typing cannot see *)
2787 | Some
(def_pos
, v
) ->
2788 error (`closest
(def_pos
, v
))
2790 | Some
(def_pos
, v
) ->
2791 error (`did_you_mean
(def_pos
, v
))
2793 (* The type of the object member is passed into the continuation k. This is
2794 * useful for typing nullsafed method calls. Consider `$x?->f()`: obj_get will
2795 * pass `k` the type of f, and `k` will typecheck the method call and return
2796 * the method's return type. obj_get then wraps that type in a Toption. *)
2797 and obj_get ~
is_method ~
nullsafe env ty1 cid id k
=
2800 | Some
p when not
(type_could_be_null
env ty1) ->
2801 let env, (r, _) = Env.expand_type
env ty1 in
2802 Errors.nullsafe_not_needed
p
2804 "This is what makes me believe it cannot be null" r);
2807 let env, method_
, _ =
2808 obj_get_with_visibility ~
is_method ~
nullsafe env ty1 cid id k
in
2811 and obj_get_with_visibility ~
is_method ~
nullsafe env ty1
2813 obj_get_ ~
is_method ~
nullsafe env ty1 cid id k
(fun ty -> ty)
2815 (* k_lhs takes the type of the object receiver *)
2816 and obj_get_ ~
is_method ~
nullsafe env ty1 cid (p, s
as id
)
2818 let env, ety1
= Env.expand_type
env ty1 in
2819 let default_case () =
2822 | Tclass
(x, paraml
) ->
2823 let class_ = Env.get_class
env (snd
x) in
2826 env, (Reason.Rnone
, Tany
), None
2827 | Some
class_ when not
is_method
2828 && not
(Env.is_strict
env)
2829 && class_.tc_name
= SN.Classes.cStdClass
->
2830 env, (Reason.Rnone
, Tany
), None
2833 if List.length
paraml = 0
2834 then List.map
class_.tc_tparams
(fun _ -> Reason.Rwitness
p, Tany
)
2837 let member_ = Env.get_member
is_method env class_ s
in
2838 if !Typing_defs.accumulate_method_calls
then
2839 Typing_defs.accumulate_method_calls_result
:=
2840 (p, (class_.tc_name^
"::"^s
)) ::
2841 !Typing_defs.accumulate_method_calls_result
;
2842 Typing_hooks.dispatch_cmethod_hook
2843 class_ (p, s
) env None ~
is_method;
2845 | None
when not
is_method ->
2846 if not
(SN.Members.is_special_xhp_attribute s
)
2847 then member_not_found
p ~
is_method class_ s
(fst ety1
);
2848 env, (Reason.Rnone
, Tany
), None
2850 (match Env.get_member
is_method env class_ SN.Members.__call
with
2852 member_not_found
p ~
is_method class_ s
(fst ety1
);
2853 env, (Reason.Rnone
, Tany
), None
2854 | Some
{ce_visibility
= vis
; ce_type
= (r, Tfun
ft); _} ->
2855 let mem_pos = Reason.to_pos
r in
2856 TVis.check_obj_access
p env (mem_pos, vis
);
2857 (* the return type of __call can depend on the
2858 * class params or be this *)
2859 let this_ty = k_lhs ety1
in
2861 type_expansions
= [];
2863 substs
= Subst.make
class_.tc_tparams
paraml;
2864 from_class
= Some
cid;
2866 let env, ft = Phase.localize_ft ~
ety_env env ft in
2868 (* we change the params of the underlying
2869 * declaration to act as a variadic function
2870 * ... this transform cannot be done when
2871 * processing the declaration of call because
2872 * direct calls to $inst->__call are also
2875 ft_arity
= Fellipsis
0;
2876 ft_tparams
= []; ft_params
= [];
2878 let member_ = (r, Tfun
ft) in
2879 env, member_, Some
(mem_pos, vis
)
2882 | Some
({ce_visibility
= vis
; ce_type
= member_; _ } as member_ce
) ->
2883 let mem_pos = Reason.to_pos
(fst
member_) in
2884 TVis.check_obj_access
p env (mem_pos, vis
);
2885 let member_ = Typing_enum.member_type
env member_ce
in
2886 let this_ty = k_lhs ety1
in
2888 type_expansions
= [];
2890 substs
= Subst.make
class_.tc_tparams
paraml;
2891 from_class
= Some
cid;
2893 let env, member_ = Phase.localize ~
ety_env env member_ in
2894 env, member_, Some
(mem_pos, vis
)
2898 | Tany
-> env, (fst ety1
, Tany
), None
2899 | (Tmixed
| Tarraykind
_ | Tprim
_ | Toption
_
2900 | Tvar
_ | Tabstract
(_, _) | Ttuple
_ | Tanon
(_, _)
2901 | Tfun
_ | Tunresolved
_ | Tshape
_) as ty ->
2902 Errors.non_object_member
2903 s
p (Typing_print.error ty) (Reason.to_pos
(fst ety1
));
2904 env, (fst ety1
, Tany
), None
2907 | _, Tunresolved
tyl ->
2908 let (env, vis
), tyl = List.map_env
(env, None
) tyl
2909 begin fun (env, vis
) ty ->
2911 obj_get_ ~
is_method ~
nullsafe env ty cid id k
k_lhs in
2912 (* There is one special case where we need to expose the
2913 * visibility outside of obj_get (checkout inst_meth special
2915 * We keep a witness of the "most restrictive" visibility
2916 * we encountered (position + visibility), to be able to
2917 * special case inst_meth.
2919 let vis = TVis.min_vis_opt
vis vis'
in
2922 let env, method_
= TUtils.in_var
env (fst ety1
, Tunresolved
(tyl)) in
2924 | p'
, (Tabstract
(ak
, tyopt
)) ->
2925 begin match TUtils.get_as_constraints
env ak tyopt
with
2927 (* We probably don't want to rewrap new types for the 'this' closure *)
2928 (* TODO AKENN: we shouldn't refine constraints by changing
2929 * the type like this *)
2930 let k_lhs'
ty = match ak
with
2931 | AKnewtype
(_, _) -> k_lhs ty
2932 | _ -> k_lhs (p'
, Tabstract
(ak
, Some
ty)) in
2933 obj_get_ ~
is_method ~
nullsafe env ty cid id k
k_lhs'
2934 | None
-> default_case ()
2936 | _, Toption
ty -> begin match nullsafe with
2938 let k'
(env, fty, x) = begin
2939 let env, method_
, x = k (env, fty, x) in
2940 let env, method_
= non_null
env method_
in
2941 env, (Reason.Rnullsafe_op
p1, Toption method_
), x
2943 obj_get_ ~
is_method ~
nullsafe env ty cid id
k'
k_lhs
2945 Errors.null_member s
p
2947 "This is what makes me believe it can be null"
2950 k (env, (fst ety1
, Tany
), None
)
2952 | _, (Tany
| Tmixed
| Tarraykind
_ | Tprim
_ | Tvar
_
2953 | Tfun
_ | Tclass
(_, _) | Ttuple
_ | Tanon
(_, _)
2954 | Tobject
| Tshape
_) -> default_case ()
2956 (* Return true if the type ty1 contains the null value *)
2957 and type_could_be_null
env ty1 =
2958 let env, ety1
= Env.expand_type
env ty1 in
2959 match (snd ety1
) with
2960 | Tabstract
(ak
, tyopt
) ->
2961 begin match TUtils.get_as_constraints
env ak tyopt
with
2963 | Some
ty -> type_could_be_null
env ty
2965 | Toption
_ | Tunresolved
_ | Tmixed
| Tany
-> true
2966 | Tarraykind
_ | Tprim
_ | Tvar
_ | Tfun
_
2967 | Tclass
(_, _) | Ttuple
_ | Tanon
(_, _) | Tobject
2970 and class_id_for_new
p env cid =
2971 let env, ty = static_class_id
p env cid in
2972 (* Instantiation on an abstract class (e.g. from classname<T>) is via the
2973 * base type (to check constructor args), but the actual type `ty` must be
2975 match TUtils.get_base_type
env ty with
2976 | _, Tclass
(sid
, _) ->
2977 let class_ = Env.get_class
env (snd sid
) in
2978 env, (match class_ with
2980 | Some
class_ -> Some
(sid
, class_, ty)
2982 | _, (Tany
| Tmixed
| Tarraykind
_ | Toption
_ | Tprim
_
2983 | Tvar
_ | Tfun
_ | Tabstract
(_, _) | Ttuple
_ | Tanon
(_, _)
2984 | Tunresolved
_ | Tobject
| Tshape
_) -> env, None
2986 (* To be a valid trait declaration, all of its 'require extends' must
2987 * match; since there's no multiple inheritance, it follows that all of
2988 * the 'require extends' must belong to the same inheritance hierarchy
2989 * and one of them should be the child of all the others *)
2990 and trait_most_concrete_req_class trait
env =
2991 List.fold_left trait
.tc_req_ancestors ~
f:begin fun acc
(_p
, ty) ->
2992 let _r, (_p
, name), _paraml
= TUtils.unwrap_class_type
ty in
2993 let keep = match acc
with
2994 | Some
(c, _ty
) -> SMap.mem
name c.tc_ancestors
2999 let class_ = Env.get_class
env name in
3002 | Some
{ tc_kind
= Ast.Cinterface
; _ } -> acc
3003 | Some
{ tc_kind
= Ast.Ctrait
; _ } ->
3004 (* this is an error case for which the nastCheck spits out
3005 * an error, but does *not* currently remove the offending
3006 * 'require extends' or 'require implements' *)
3008 | Some
c -> Some
(c, ty)
3012 (* When invoking a method the class_id is used to determine what class we
3013 * lookup the method in, but the type of 'this' will be the late bound type.
3017 * public static function get(): this { return new static(); }
3019 * public static function alias(): this { return self::get(); }
3022 * In C::alias, when we invoke self::get(), 'self' is resolved to the class
3023 * in the lexical scope (C), so call C::get. However the method is executed in
3024 * the current context, so static inside C::get will be resolved to the late
3025 * bound type (get_called_class() within C::alias).
3027 * This means when determining the type of this, CIparent and CIself should be
3028 * changed to CIstatic. For the other cases of C::get() or $c::get(), we only
3029 * look at the left hand side of the '::' and use the type type associated
3032 * Thus C::get() will return a type C, while $c::get() will return the same
3035 and this_for_method
env cid default_ty
= match cid with
3036 | CIparent
| CIself
| CIstatic
->
3037 let p = Reason.to_pos
(fst default_ty
) in
3038 let env, ty = static_class_id
p env CIstatic
in
3039 env, ExprDepTy.make
env CIstatic
ty
3043 and static_class_id
p env = function
3045 (match Env.get_self
env with
3046 | _, Tclass
((_, self
), _) ->
3047 (match Env.get_class
env self
with
3049 {tc_kind
= Ast.Ctrait
; _}
3051 (match trait_most_concrete_req_class trait
env with
3053 Errors.parent_in_trait
p;
3054 env, (Reason.Rwitness
p, Tany
)
3055 | Some
(_, parent_ty
) ->
3056 (* inside a trait, parent is SN.Typehints.this, but with the
3057 * type of the most concrete class that the trait has
3058 * "require extend"-ed *)
3059 let r = Reason.Rwitness
p in
3060 let env, parent_ty
= Phase.localize_with_self
env parent_ty
in
3061 env, (r, TUtils.this_of parent_ty
)
3064 let parent = Env.get_parent
env in
3065 let parent_defined = snd
parent <> Tany
in
3066 if not
parent_defined
3067 then Errors.parent_undefined
p;
3068 let r = Reason.Rwitness
p in
3069 let env, parent = Phase.localize_with_self
env parent in
3070 (* parent is still technically the same object. *)
3071 env, (r, TUtils.this_of
(r, snd
parent))
3073 | _, (Tany
| Tmixed
| Tarraykind
_ | Toption
_ | Tprim
_
3074 | Tfun
_ | Ttuple
_ | Tshape
_ | Tvar
_
3075 | Tanon
(_, _) | Tunresolved
_ | Tabstract
(_, _) | Tobject
3077 let parent = Env.get_parent
env in
3078 let parent_defined = snd
parent <> Tany
in
3079 if not
parent_defined
3080 then Errors.parent_undefined
p;
3081 let r = Reason.Rwitness
p in
3082 let env, parent = Phase.localize_with_self
env parent in
3083 (* parent is still technically the same object. *)
3084 env, (r, TUtils.this_of
(r, snd
parent))
3087 env, (Reason.Rwitness
p, TUtils.this_of
(Env.get_self
env))
3089 env, (Reason.Rwitness
p, snd
(Env.get_self
env))
3091 let class_ = Env.get_class
env (snd
c) in
3093 | None
-> env, (Reason.Rnone
, Tany
) (* Tobject *)
3095 let env, params = List.map_env
env class_.tc_tparams
begin fun env _ ->
3096 Env.fresh_unresolved_type
env
3098 env, (Reason.Rwitness
(fst
c), Tclass
(c, params))
3100 | CIexpr
(p, _ as e
) ->
3101 let env, ty = expr
env e
in
3102 let rec resolve_ety ty =
3103 let env, ty = TUtils.fold_unresolved
env ty in
3104 let _, ty = Env.expand_type
env ty in
3105 match TUtils.get_base_type
env ty with
3106 | _, Tabstract
(AKnewtype
(classname
, [the_cls
]), _) when
3107 classname
= SN.Classes.cClassname
-> resolve_ety the_cls
3108 | _, Tabstract
(AKgeneric
_, _)
3110 | r, Tunresolved
tyl -> r, Tunresolved
(List.map
tyl resolve_ety)
3111 | _, Tvar
_ as ty -> resolve_ety ty
3112 | _, (Tany
| Tprim Tstring
| Tabstract
(_, None
) | Tmixed
| Tobject
)
3113 when not
(Env.is_strict
env) ->
3115 | _, (Tany
| Tmixed
| Tarraykind
_ | Toption
_
3116 | Tprim
_ | Tfun
_ | Ttuple
_
3117 | Tabstract
((AKenum
_ | AKdependent
_ | AKnewtype
_), _)
3118 | Tanon
(_, _) | Tobject
| Tshape
_ as ty
3120 Errors.expected_class ~suffix
:(", but got "^
Typing_print.error ty) p;
3122 in env, resolve_ety ty
3124 and call_construct
p env class_ params el uel
cid =
3125 let cstr = Env.get_construct
env class_ in
3126 let mode = Env.get_mode
env in
3127 Typing_hooks.dispatch_constructor_hook
class_ env p;
3128 match (fst
cstr) with
3131 (mode = FileInfo.Mstrict
|| mode = FileInfo.Mpartial
) &&
3132 class_.tc_members_fully_known
3133 then Errors.constructor_no_args
p;
3134 fst
(List.map_env
env el expr
)
3135 | Some
{ ce_visibility
= vis; ce_type
= m
; _ } ->
3136 TVis.check_obj_access
p env (Reason.to_pos
(fst m
), vis);
3137 let cid = if cid = CIparent
then CIstatic
else cid in
3138 let env, cid_ty
= static_class_id
p env cid in
3140 type_expansions
= [];
3142 substs
= Subst.make
class_.tc_tparams
params;
3143 from_class
= Some
cid;
3145 let env, m
= Phase.localize ~
ety_env env m
in
3146 fst
(call
p env m el uel
)
3148 and check_arity ?
(check_min
=true) pos pos_def
(arity:int) exp_arity
=
3149 let exp_min = (Typing_defs.arity_min exp_arity
) in
3150 if check_min
&& arity < exp_min then
3151 Errors.typing_too_few_args
pos pos_def
;
3152 match exp_arity
with
3153 | Fstandard
(_, exp_max
) ->
3154 if (arity > exp_max
)
3155 then Errors.typing_too_many_args
pos pos_def
;
3156 | Fvariadic
_ | Fellipsis
_ -> ()
3158 and check_deprecated
p { ft_pos
; ft_deprecated
; _ } =
3159 match ft_deprecated
with
3160 | Some s
-> Errors.deprecated_use
p ft_pos s
3163 (* The variadic capture argument is an array listing the passed
3164 * variable arguments for the purposes of the function body; callsites
3165 * should not unify with it *)
3166 and variadic_param
env ft =
3167 match ft.ft_arity
with
3168 | Fvariadic
(_, p_ty
) -> env, Some p_ty
3169 | Fellipsis
_ | Fstandard
_ -> env, None
3171 and call
pos env fty el uel
=
3172 let env, ty = call_
pos env fty el uel
in
3173 (* We need to solve the constraints after every single function call.
3174 * The type-checker is control-flow sensitive, the same value could
3175 * have different type depending on the branch that we are in.
3176 * When this is the case, a call could violate one of the constraints
3178 let env = fold_fun_list
env env.Env.todo
in
3181 (* Enforces that e is unpackable. If e is a tuple, appends its unpacked types
3182 * into the e_tyl returned.
3184 and unpack_expr
env e_tyl e
=
3185 let env, ety
= expr
env e
in
3188 (* Todo: Check that tuples are allowed - that is, disallow a tuple
3189 * unpacking after an array unpacking.
3191 let unpacked_e_tyl = List.map
tyl (fun ty -> e
, ty) in
3192 env, e_tyl
@ unpacked_e_tyl, true
3195 let unpack_r = Reason.Runpack_param
pos in
3196 let container_ty = (unpack_r, Tclass
((pos, SN.Collections.cContainer
),
3197 [unpack_r, Tany
])) in
3198 let env = Type.sub_type
pos Reason.URparam
env container_ty ety
in
3202 (* Unpacks uel. If tuples are found, unpacked types are appended to the
3205 and unpack_exprl
env e_tyl uel
=
3206 List.fold_left uel ~init
:(env, e_tyl
, false)
3207 ~
f: begin fun (env, e_tyl
, unpacked_tuple
) e
->
3208 let env, e_tyl
, is_tuple
= unpack_expr
env e_tyl e
in
3209 (env, e_tyl
, is_tuple
|| unpacked_tuple
)
3212 and call_
pos env fty el uel
=
3213 let env, efty
= Env.expand_type
env fty in
3215 | _, (Tany
| Tunresolved
[]) ->
3216 let el = el @ uel
in
3217 let env, _ = List.map_env
env el begin fun env elt
->
3218 let env, arg_ty
= expr
env elt
in
3219 let arg_ty = check_valid_rvalue
pos env arg_ty in
3222 Typing_hooks.dispatch_fun_call_hooks
[] (List.map
(el @ uel
) fst
) env;
3223 env, (Reason.Rnone
, Tany
)
3224 | r, Tunresolved
tyl ->
3225 let env, retl
= List.map_env
env tyl begin fun env ty ->
3226 call
pos env ty el uel
3228 TUtils.in_var
env (r, Tunresolved retl
)
3230 (* Typing of format string functions. It is dependent on the arguments (el)
3231 * so it cannot be done earlier.
3233 let env, ft = Typing_exts.retype_magic_func
env ft el in
3234 check_deprecated
pos ft;
3235 let pos_def = Reason.to_pos r2
in
3236 let env, var_param
= variadic_param
env ft in
3237 let env, e_tyl
= List.map_env
env el begin fun env e
->
3238 let env, ty = expr
env e
in
3241 let env, e_tyl
, unpacked_tuple
= unpack_exprl
env e_tyl uel
in
3242 let arity = if unpacked_tuple
3243 then List.length e_tyl
3244 (* Each array unpacked corresponds with at least 1 param. *)
3245 else List.length
el + List.length uel
in
3246 (* If we unpacked an array, we don't check arity exactly. Since each
3247 * unpacked array consumes 1 or many parameters, it is nonsensical to say
3248 * that not enough args were passed in (so we don't do the min check).
3250 let () = check_arity ~check_min
:(uel
= [] || unpacked_tuple
)
3251 pos pos_def arity ft.ft_arity
in
3252 let todos = ref [] in
3253 let env = wfold_left_default (call_param
todos) (env, var_param
)
3254 ft.ft_params e_tyl
in
3255 let env = fold_fun_list
env !todos in
3256 Typing_hooks.dispatch_fun_call_hooks
3257 ft.ft_params
(List.map
(el @ uel
) fst
) env;
3259 | r2
, Tanon
(arity, id
) when uel
= [] ->
3260 let env, tyl = List.map_env
env el expr
in
3261 let anon = Env.get_anonymous
env id
in
3262 let fpos = Reason.to_pos r2
in
3265 Errors.anonymous_recursive_call
pos;
3266 env, (Reason.Rnone
, Tany
)
3268 let () = check_arity
pos fpos (List.length
tyl) arity in
3269 let tyl = List.map
tyl (fun x -> None
, x) in
3271 | _, Tarraykind
_ when not
(Env.is_strict
env) ->
3272 (* Relaxing call_user_func to work with an array in partial mode *)
3273 env, (Reason.Rnone
, Tany
)
3276 env, (Reason.Rnone
, Tany
)
3279 and call_param
todos env (name, x) ((pos, _ as e
), arg_ty) =
3282 | Some
name -> Typing_suggest.save_param
name env x arg_ty
3284 let arg_ty = check_valid_rvalue
pos env arg_ty in
3286 (* When checking params the type 'x' may be expression dependent. Since
3287 * we store the expression id in the local env for Lvar, we want to apply
3290 let dep_ty = match snd e
with
3291 | Lvar
_ -> ExprDepTy.make
env (CIexpr e
) arg_ty
3293 (* We solve for Tanon types after all the other params because we want to
3294 * typecheck the lambda bodies with as much type information as possible. For
3295 * example, in array_map(fn, x), we might be able to use the type of x to
3296 * infer the type of fn, but if we call sub_type on fn first, we end up
3297 * typechecking its body without the benefit of knowing its full type. If
3298 * fn is typehinted but not x, we could use fn to infer the type of x, but
3299 * in practice the reverse situation is more likely. This rearrangement is
3300 * particularly useful since higher-order functions usually put fn before x.
3304 todos := (fun env ->
3305 Type.sub_type
pos Reason.URparam
env x arg_ty) :: !todos;
3307 | _, (Tany
| Tmixed
| Tarraykind
_ | Toption
_ | Tprim
_
3308 | Tvar
_ | Tfun
_ | Tabstract
(_, _) | Tclass
(_, _) | Ttuple
_
3309 | Tunresolved
_ | Tobject
| Tshape
_) ->
3310 Type.sub_type
pos Reason.URparam
env x dep_ty
3313 Errors.bad_call
p (Typing_print.error ty)
3315 and unop
p env uop
ty =
3318 Async.enforce_not_awaitable
env p ty;
3319 (* !$x (logical not) works with any type, so we just return Tbool *)
3320 env, (Reason.Rlogic_ret
p, Tprim Tbool
)
3322 (* ~$x (bitwise not) only works with int *)
3323 Type.unify
p Reason.URnone
env (Reason.Rarith
p, Tprim Tint
) ty
3330 (* math operators work with int or floats, so we call sub_type *)
3331 let env = Type.sub_type
p Reason.URnone
env (Reason.Rarith
p, Tprim Tnum
) ty in
3334 (* We basically just ignore references in non-strict files *)
3335 if Env.is_strict
env then
3336 Errors.reference_expr
p;
3339 and binop in_cond
p env bop
p1 ty1 p2 ty2 =
3340 let expand_num_type env p ty =
3341 let env, ty = TUtils.fold_unresolved
env ty in
3342 let env = Type.sub_type
p Reason.URnone
env (Reason.Rarith
p, Tprim Tnum
) ty in
3343 let env, ety
= Env.expand_type
env ty in
3347 let env, ty1 = TUtils.fold_unresolved
env ty1 in
3348 let env, ty2 = TUtils.fold_unresolved
env ty2 in
3349 let env, ety1
= Env.expand_type
env ty1 in
3350 let env, ety2
= Env.expand_type
env ty2 in
3351 (match ety1
, ety2
with
3352 (* For array<V1>+array<V2> and array<K1,V1>+array<K2,V2>, allow
3353 * the addition to produce a supertype. (We could also handle
3354 * when they have mismatching annotations, but we get better error
3355 * messages if we just let those get unified in the next case. *)
3356 | (_, Tarraykind
(AKmap
_ as ak
)), (_, Tarraykind
(AKmap
_))
3357 | (_, Tarraykind
(AKvec
_ as ak
)), (_, Tarraykind
(AKvec
_)) ->
3358 let env, a_sup
= Env.fresh_unresolved_type
env in
3359 let env, b_sup
= Env.fresh_unresolved_type
env in
3360 let res_ty = Reason.Rarray_plus_ret
p, Tarraykind
(
3362 | AKvec
_ -> AKvec a_sup
3363 | AKmap
_ -> AKmap
(a_sup
, b_sup
)
3366 let env = Type.sub_type
p1 Reason.URnone
env res_ty ety1
in
3367 let env = Type.sub_type
p2 Reason.URnone
env res_ty ety2
in
3369 | (_, Tarraykind
_), (_, Tarraykind
(AKshape
_)) ->
3370 let env, ty2 = Typing_arrays.downcast_aktypes
env ty2 in
3371 binop in_cond
p env bop
p1 ty1 p2 ty2
3372 | (_, Tarraykind
(AKshape
_)), (_, Tarraykind
_) ->
3373 let env, ty1 = Typing_arrays.downcast_aktypes
env ty1 in
3374 binop in_cond
p env bop
p1 ty1 p2 ty2
3375 | (_, Tarraykind
_), (_, Tarraykind
_)
3376 | (_, Tany
), (_, Tarraykind
_)
3377 | (_, Tarraykind
_), (_, Tany
) ->
3378 let env, ty = Type.unify
p Reason.URnone
env ty1 ty2 in
3380 | (_, (Tany
| Tmixed
| Tarraykind
_ | Toption
_
3381 | Tprim
_ | Tvar
_ | Tfun
_ | Tabstract
(_, _) | Tclass
(_, _)
3382 | Ttuple
_ | Tanon
(_, _) | Tunresolved
_ | Tobject
| Tshape
_
3384 ), _ -> binop in_cond
p env Ast.Minus
p1 ty1 p2 ty2
3386 | Ast.Minus
| Ast.Star
->
3387 let env, ty1 = TUtils.fold_unresolved
env ty1 in
3388 let env, ty2 = TUtils.fold_unresolved
env ty2 in
3389 let env = Type.sub_type
p1 Reason.URnone
env
3390 (Reason.Rarith
p1, Tprim Tnum
) ty1 in
3391 let env = Type.sub_type
p2 Reason.URnone
env
3392 (Reason.Rarith
p2, Tprim Tnum
) ty2 in
3393 let env, ety1
= Env.expand_type
env ty1 in
3394 let env, ety2
= Env.expand_type
env ty2 in
3395 (match ety1
, ety2
with
3396 | (r, Tprim Tfloat
), _ | _, (r, Tprim Tfloat
) ->
3397 (* if either side is a float then float: 1.0 - 1 -> float *)
3398 env, (r, Tprim Tfloat
)
3399 | (r, Tprim Tnum
), _ | _, (r, Tprim Tnum
) ->
3400 (* if either side is a num, then num: (3 / x) - 1 -> num *)
3401 env, (r, Tprim Tnum
)
3402 | (_, Tprim Tint
), (_, Tprim Tint
) ->
3403 (* Both sides are integers, then integer: 1 - 1 -> int *)
3404 env, (Reason.Rarith_ret
p, Tprim Tint
)
3405 | (_, (Tany
| Tmixed
| Tarraykind
_ | Toption
_
3406 | Tprim
_ | Tvar
_ | Tfun
_ | Tabstract
(_, _) | Tclass
(_, _)
3407 | Ttuple
_ | Tanon
(_, _) | Tunresolved
_ | Tobject
| Tshape
_
3410 (* Either side is unknown, unknown *)
3411 (* TODO um, what? This seems very wrong, particularly where "newtype
3413 * This also causes issues with primitive constraints on generics.
3414 * See test/typecheck/generic_primitive_invariant.php as an example *)
3417 let env, ety1
= expand_num_type env p1 ty1 in
3418 let env, ety2
= expand_num_type env p2 ty2 in
3419 (match ety1
, ety2
with
3420 | (r, Tprim Tfloat
), _ | _, (r, Tprim Tfloat
) -> env, (r, Tprim Tfloat
)
3421 | (_, (Tany
| Tmixed
| Tarraykind
_ | Toption
_
3422 | Tprim
_ | Tvar
_ | Tfun
_ | Tabstract
(_, _) | Tclass
(_, _)
3423 | Ttuple
_ | Tanon
(_, _) | Tunresolved
_ | Tobject
| Tshape
_
3425 ), _ -> env, (Reason.Rret_div
p, Tprim Tnum
)
3428 let env, ety1
= expand_num_type env p1 ty1 in
3429 let env, ety2
= expand_num_type env p2 ty2 in
3430 (match ety1
, ety2
with
3431 | (r, Tprim Tfloat
), _ | _, (r, Tprim Tfloat
) -> env, (r, Tprim Tfloat
)
3432 | (_, (Tany
| Tmixed
| Tarraykind
_ | Toption
_
3433 | Tprim
_ | Tvar
_ | Tfun
_ | Tabstract
(_, _) | Tclass
(_, _)
3434 | Ttuple
_ | Tanon
(_, _) | Tunresolved
_ | Tobject
| Tshape
_
3436 ), _ -> env, (Reason.Rarith_ret
p, Tprim Tnum
)
3439 let env = Type.sub_type
p Reason.URnone
env (Reason.Rarith
p1, Tprim Tint
) ty1 in
3440 let env = Type.sub_type
p Reason.URnone
env (Reason.Rarith
p1, Tprim Tint
) ty2 in
3441 env, (Reason.Rarith_ret
p, Tprim Tint
)
3443 let env, ty1 = TUtils.fold_unresolved
env ty1 in
3444 let env, ty2 = TUtils.fold_unresolved
env ty2 in
3445 let env, ety1
= Env.expand_type
env ty1 in
3446 let env, ety2
= Env.expand_type
env ty2 in
3447 (match ety1
, ety2
with
3448 | (_, Tprim Tbool
), _ | _, (_, Tprim Tbool
) ->
3449 let env, _ = Type.unify
p Reason.URnone
env ty1 (Reason.Rlogic_ret
p1, Tprim Tbool
) in
3450 let env, _ = Type.unify
p Reason.URnone
env ty2 (Reason.Rlogic_ret
p1, Tprim Tbool
) in
3451 env, (Reason.Rlogic_ret
p, Tprim Tbool
)
3452 | (_, (Tany
| Tmixed
| Tarraykind
_ | Toption
_
3453 | Tprim
_ | Tvar
_ | Tfun
_ | Tabstract
(_, _) | Tclass
(_, _)
3454 | Ttuple
_ | Tanon
(_, _) | Tunresolved
_ | Tobject
| Tshape
_
3457 let env, _ = Type.unify
p Reason.URnone
env ty1 (Reason.Rarith
p1, Tprim Tint
) in
3458 let env, _ = Type.unify
p Reason.URnone
env ty2 (Reason.Rarith
p1, Tprim Tint
) in
3459 env, (Reason.Rarith_ret
p, Tprim Tint
)
3461 | Ast.Eqeq
| Ast.Diff
->
3462 env, (Reason.Rcomp
p, Tprim Tbool
)
3463 | Ast.EQeqeq
| Ast.Diff2
->
3465 then Typing_equality_check.assert_nontrivial
p bop
env ty1 ty2;
3466 env, (Reason.Rcomp
p, Tprim Tbool
)
3467 | Ast.Lt
| Ast.Lte
| Ast.Gt
| Ast.Gte
->
3468 let ty_num = (Reason.Rcomp
p, Tprim
Nast.Tnum
) in
3469 let ty_string = (Reason.Rcomp
p, Tprim
Nast.Tstring
) in
3471 (Reason.Rcomp
p, Tclass
((p, SN.Classes.cDateTime
), [])) in
3473 SubType.is_sub_type
env ty ty1 && SubType.is_sub_type
env ty ty2 in
3474 if both_sub ty_num || both_sub ty_string || both_sub ty_datetime
3475 then env, (Reason.Rcomp
p, Tprim Tbool
)
3477 (* TODO this is questionable; PHP's semantics for conversions with "<"
3478 * are pretty crazy and we may want to just disallow this? *)
3479 let env, _ = Type.unify
p Reason.URnone
env ty1 ty2 in
3480 env, (Reason.Rcomp
p, Tprim Tbool
)
3482 let env = SubType.sub_string
p1 env ty1 in
3483 let env = SubType.sub_string
p2 env ty2 in
3484 env, (Reason.Rconcat_ret
p, Tprim Tstring
)
3487 env, (Reason.Rlogic_ret
p, Tprim Tbool
)
3488 | Ast.Amp
| Ast.Bar
| Ast.Ltlt
| Ast.Gtgt
->
3489 let env = Type.sub_type
p Reason.URnone
env (Reason.Rbitwise
p1, Tprim Tint
) ty1 in
3490 let env = Type.sub_type
p Reason.URnone
env (Reason.Rbitwise
p2, Tprim Tint
) ty2 in
3491 env, (Reason.Rbitwise_ret
p, Tprim Tint
)
3495 and non_null
env ty =
3496 let env, ty = Env.expand_type
env ty in
3499 let env, ty = Env.expand_type
env ty in
3500 (* When "??T" appears in the typing environment due to implicit
3501 * typing, the recursion here ensures that it's treated as
3502 * isomorphic to "?T"; that is, all nulls are created equal.
3505 | r, Tunresolved
tyl ->
3506 let env, tyl = List.map_env
env tyl non_null
in
3507 (* We need to flatten the unresolved types, otherwise we could
3508 * end up with "Tunresolved[Tunresolved _]" which is not supposed
3511 let tyl = List.fold_right
tyl ~
f:begin fun ty tyl ->
3513 | _, Tunresolved l
-> l
@ tyl
3516 env, (r, Tunresolved
tyl)
3518 | r, Tabstract
(ak
, tyopt
) ->
3519 begin match TUtils.get_as_constraints
env ak tyopt
with
3520 | Some
ty -> let env, ty = non_null
env ty in
3521 env, (r, Tabstract
(ak
, Some
ty))
3524 | _, (Tany
| Tmixed
| Tarraykind
_ | Tprim
_ | Tvar
_
3525 | Tclass
(_, _) | Ttuple
_ | Tanon
(_, _) | Tfun
_
3526 | Tobject
| Tshape
_) ->
3529 and condition_var_non_null
env = function
3531 | _, Dollardollar
(_, x) ->
3532 let env, x_ty
= Env.get_local
env x in
3533 let env, x_ty
= Env.expand_type
env x_ty
in
3534 let env, x_ty
= non_null
env x_ty
in
3535 Env.set_local
env x x_ty
3536 | p, Class_get
(cname
, (_, member_name
)) as e
->
3537 let env, ty = expr
env e
in
3538 let env, local = Env.FakeMembers.make_static
p env cname member_name
in
3539 let env = Env.set_local
env local ty in
3540 let local = p, Lvar
(p, local) in
3541 condition_var_non_null
env local
3542 | p, Obj_get
((_, This
| _, Lvar
_ as obj
),
3543 (_, Id
(_, member_name
)),
3545 let env, ty = expr
env e
in
3546 let env, local = Env.FakeMembers.make
p env obj member_name
in
3547 let env = Env.set_local
env local ty in
3548 let local = p, Lvar
(p, local) in
3549 condition_var_non_null
env local
3552 and condition_isset
env = function
3553 | _, Array_get
(x, _) -> condition_isset
env x
3554 | v
-> condition_var_non_null
env v
3557 * Build an environment for the true or false branch of
3558 * conditional statements.
3560 and condition
env tparamet
=
3561 let expr = raw_expr ~in_cond
:true in function
3562 | _, Expr_list
[] -> env
3563 | _, Expr_list
[x] ->
3564 let env, _ = expr env x in
3565 condition
env tparamet
x
3566 | r, Expr_list
(x::xs
) ->
3567 let env, _ = expr env x in
3568 condition
env tparamet
(r, Expr_list xs
)
3569 | _, Call
(Cnormal
, (_, Id
(_, func
)), [param], [])
3570 when SN.PseudoFunctions.isset
= func
&& tparamet
&&
3571 not
(Env.is_strict
env) ->
3572 condition_isset
env param
3573 | _, Call
(Cnormal
, (_, Id
(_, func
)), [e
], [])
3574 when not tparamet
&& SN.StdlibFunctions.is_null
= func
->
3575 condition_var_non_null
env e
3576 | r, Binop
((Ast.Eqeq
| Ast.EQeqeq
as bop
),
3578 | r, Binop
((Ast.Eqeq
| Ast.EQeqeq
as bop
),
3579 e
, (_, Null
)) when not tparamet
->
3580 let env, x_ty
= expr env e
in
3581 let env, x_ty
= Env.expand_type
env x_ty
in
3583 if bop
== Ast.Eqeq
then check_null_wtf
env r x_ty
else env in
3584 condition_var_non_null
env e
3585 | (p, (Lvar
_ | Obj_get
_ | Class_get
_) as e
) ->
3586 let env, ty = expr env e
in
3587 let env, ety
= Env.expand_type
env ty in
3589 | _, Tarraykind
(AKany
| AKempty
)
3590 | _, Tprim Tbool
-> env
3591 | _, (Tany
| Tmixed
| Tarraykind
_ | Toption
_
3592 | Tprim
_ | Tvar
_ | Tfun
_ | Tabstract
(_, _) | Tclass
(_, _)
3593 | Ttuple
_ | Tanon
(_, _) | Tunresolved
_ | Tobject
| Tshape
_
3595 condition
env (not tparamet
) (p, Binop
(Ast.Eqeq
, e
, (p, Null
))))
3596 | r, Binop
(Ast.Eq None
, var
, e
) when tparamet
->
3597 let env, e_ty
= expr env e
in
3598 let env, e_ty
= Env.expand_type
env e_ty
in
3599 let env = check_null_wtf
env r e_ty
in
3600 condition_var_non_null
env var
3601 | p1, Binop
(Ast.Eq None
, (_, (Lvar
_ | Obj_get
_) as lv
), (p2, _)) ->
3602 let env, _ = expr env (p1, Binop
(Ast.Eq None
, lv
, (p2, Null
))) in
3603 condition
env tparamet lv
3604 | p, Binop
((Ast.Diff
| Ast.Diff2
as op
), e1
, e2) ->
3605 let op = if op = Ast.Diff
then Ast.Eqeq
else Ast.EQeqeq
in
3606 condition
env (not tparamet
) (p, Binop
(op, e1
, e2))
3607 | _, Binop
(Ast.AMpamp
, e1
, e2) when tparamet
->
3608 let env = condition
env true e1
in
3609 let env = condition
env true e2 in
3611 | _, Binop
(Ast.BArbar
, e1
, e2) when not tparamet
->
3612 let env = condition
env false e1
in
3613 let env = condition
env false e2 in
3615 | _, Call
(Cnormal
, (_, Id
(_, f)), [lv
], [])
3616 when tparamet
&& f = SN.StdlibFunctions.is_array
->
3618 | _, Call
(Cnormal
, (_, Id
(_, f)), [lv
], [])
3619 when tparamet
&& f = SN.StdlibFunctions.is_int
->
3621 | _, Call
(Cnormal
, (_, Id
(_, f)), [lv
], [])
3622 when tparamet
&& f = SN.StdlibFunctions.is_bool
->
3623 is_type
env lv Tbool
3624 | _, Call
(Cnormal
, (_, Id
(_, f)), [lv
], [])
3625 when tparamet
&& f = SN.StdlibFunctions.is_float
->
3626 is_type
env lv Tfloat
3627 | _, Call
(Cnormal
, (_, Id
(_, f)), [lv
], [])
3628 when tparamet
&& f = SN.StdlibFunctions.is_string
->
3629 is_type
env lv Tstring
3630 | _, Call
(Cnormal
, (_, Id
(_, f)), [lv
], [])
3631 when tparamet
&& f = SN.StdlibFunctions.is_resource
->
3632 is_type
env lv Tresource
3633 | _, Unop
(Ast.Unot
, e
) ->
3634 condition
env (not tparamet
) e
3635 | p, InstanceOf
(ivar
, cid) when tparamet
&& is_instance_var ivar
->
3636 let env, x_ty
= expr env ivar
in
3637 let env, x_ty
= Env.expand_type
env x_ty
in
3638 let env, (ivar_pos
, x) = get_instance_var
env ivar
in
3639 let env = Env.set_local
env x x_ty
in
3640 (* XXX the position p here is not really correct... it's the position
3641 * of the instanceof expression, not the class id. But we don't store
3642 * position data for the latter. *)
3643 let env, obj_ty = static_class_id
p env cid in
3644 let rec resolve_obj env obj_ty =
3645 (* Expand so that we don't modify x *)
3646 let env, obj_ty = Env.expand_type
env obj_ty in
3648 | _, Tabstract
(AKgeneric
_, _) ->
3650 | _, Tabstract
(AKdependent
(`this
, []), Some
(_, Tclass
_)) ->
3652 (* Technically instanceof static is not strong enough to prove
3653 * that a type is exactly the same as the late bound type.
3654 * For now we allow this lie to exist. To solve
3655 * this we either need to create a new type that means
3656 * subtype of static or provide a way of specifying exactly
3657 * the late bound type i.e. $x::class === static::class
3659 if cid = CIstatic
then
3660 ExprDepTy.make
env CIstatic
obj_ty
3664 | _, Tabstract
((AKdependent
_ | AKnewtype
_), Some
ty) ->
3666 | _, Tclass
((_, cid as _c
), _) ->
3667 let class_ = Env.get_class
env cid in
3669 | None
-> env, (Reason.Rwitness ivar_pos
, Tobject
)
3671 if SubType.is_sub_type
env obj_ty x_ty
3673 (* If the right side of the `instanceof` object is
3674 * a super type of what we already knew. In this case,
3675 * since we already have a more specialized object, we
3676 * don't touch the original object. Check out the unit
3677 * test srecko.php if this is unclear.
3679 * Note that if x_ty is Tany, no amount of subtype
3680 * checking will be able to specify it
3681 * further. This is arguably desirable to maintain
3682 * the invariant that removing annotations gets rid
3683 * of typing errors in partial mode (See also
3688 | r, Tunresolved
tyl ->
3689 let env, tyl = List.map_env
env tyl resolve_obj in
3690 env, (r, Tunresolved
tyl)
3691 | _, (Tany
| Tmixed
| Tarraykind
_ | Tprim
_ | Tvar
_ | Tfun
_
3692 | Tabstract
((AKenum
_ | AKnewtype
_ | AKdependent
_), _)
3693 | Ttuple
_ | Tanon
(_, _) | Toption
_ | Tobject
| Tshape
_) ->
3694 env, (Reason.Rwitness ivar_pos
, Tobject
)
3696 let env, x_ty
= resolve_obj env obj_ty in
3697 Env.set_local
env x x_ty
3698 | _, Binop
((Ast.Eqeq
| Ast.EQeqeq
), e
, (_, Null
))
3699 | _, Binop
((Ast.Eqeq
| Ast.EQeqeq
), (_, Null
), e
) ->
3700 let env, _ = expr env e
in
3703 let env, _ = expr env e
in
3706 and is_instance_var
= function
3707 | _, (Lvar
_ | This
) -> true
3708 | _, Obj_get
((_, This
), (_, Id
_), _) -> true
3709 | _, Obj_get
((_, Lvar
_), (_, Id
_), _) -> true
3710 | _, Class_get
(_, _) -> true
3713 and get_instance_var
env = function
3714 | p, Class_get
(cname
, (_, member_name
)) ->
3715 let env, local = Env.FakeMembers.make_static
p env cname member_name
in
3717 | p, Obj_get
((_, This
| _, Lvar
_ as obj
), (_, Id
(_, member_name
)), _) ->
3718 let env, local = Env.FakeMembers.make
p env obj member_name
in
3720 | _, Lvar
(p, x) -> env, (p, x)
3721 | p, This
-> env, (p, this
)
3722 | _ -> failwith
"Should only be called when is_instance_var is true"
3724 and check_null_wtf
env p ty =
3725 if not
(Env.is_strict
env) then env else
3726 let env, ty = TUtils.fold_unresolved
env ty in
3727 let env, ety
= Env.expand_type
env ty in
3730 let env, ty = Env.expand_type
env ty in
3734 Errors.sketchy_null_check
p
3736 Errors.sketchy_null_check_primitive
p
3737 | _, (Tarraykind
_ | Toption
_ | Tvar
_ | Tfun
_
3738 | Tabstract
(_, _) | Tclass
(_, _) | Ttuple
_ | Tanon
(_, _)
3739 | Tunresolved
_ | Tobject
| Tshape
_ ) -> ());
3741 | _, (Tany
| Tmixed
| Tarraykind
_ | Tprim
_ | Tvar
_
3742 | Tfun
_ | Tabstract
(_, _) | Tclass
(_, _) | Ttuple
_ | Tanon
(_, _)
3743 | Tunresolved
_ | Tobject
| Tshape
_ ) -> env
3745 and is_type
env e tprim
=
3747 | p, Class_get
(cname
, (_, member_name
)) ->
3748 let env, local = Env.FakeMembers.make_static
p env cname member_name
in
3749 Env.set_local
env local (Reason.Rwitness
p, Tprim tprim
)
3750 | p, Obj_get
((_, This
| _, Lvar
_ as obj
), (_, Id
(_, member_name
)), _) ->
3751 let env, local = Env.FakeMembers.make
p env obj member_name
in
3752 Env.set_local
env local (Reason.Rwitness
p, Tprim tprim
)
3754 Env.set_local
env x (Reason.Rwitness
p, Tprim tprim
)
3757 and is_array
env = function
3758 | p, Class_get
(cname
, (_, member_name
)) ->
3759 let env, local = Env.FakeMembers.make_static
p env cname member_name
in
3760 Env.set_local
env local (Reason.Rwitness
p, Tarraykind AKany
)
3761 | p, Obj_get
((_, This
| _, Lvar
_ as obj
), (_, Id
(_, member_name
)), _) ->
3762 let env, local = Env.FakeMembers.make
p env obj member_name
in
3763 Env.set_local
env local (Reason.Rwitness
p, Tarraykind AKany
)
3765 Env.set_local
env x (Reason.Rwitness
p, Tarraykind AKany
)
3768 and string2
env idl
=
3769 List.fold_left idl ~init
:env ~
f:begin fun env x ->
3770 let env, ty = expr env x in
3772 let env = SubType.sub_string
p env ty in
3776 (* If the current class inherits from classes that take type arguments, we need
3777 * to check that the arguments provided are consistent with the constraints on
3778 * the type parameters. *)
3779 and check_implements_tparaml
(env: Env.env) ht
=
3780 let _r, (p, c), paraml = TUtils.unwrap_class_type ht
in
3781 let class_ = Env.get_class_dep
env c in
3784 (* The class lives in PHP land *)
3787 let size1 = List.length
class_.tc_tparams
in
3788 let size2 = List.length
paraml in
3789 if size1 <> size2 then Errors.class_arity
p class_.tc_pos
c size1;
3790 let subst = Inst.make_subst
class_.tc_tparams
paraml in
3791 iter2_shortest
begin fun (_, (p, _), cstrl
) ty ->
3792 List.iter cstrl
begin fun (ck
, cstr) ->
3793 (* Constraint might contain uses of generic type parameters *)
3794 let cstr = Inst.instantiate
subst cstr in
3796 | Ast.Constraint_as
->
3797 ignore
(Type.sub_type_decl
p Reason.URnone
env cstr ty)
3798 | Ast.Constraint_super
->
3799 ignore
(Type.sub_type_decl
p Reason.URnone
env ty cstr)
3801 end class_.tc_tparams
paraml
3803 (* In order to type-check a class, we need to know what "parent"
3804 * refers to. Sometimes people write "parent::", when that happens,
3805 * we need to know the type of parent.
3807 and class_def_parent
env class_def class_type
=
3808 match class_def
.c_extends
with
3809 | (_, Happly
((_, x), _) as parent_ty
) :: _ ->
3810 let parent_type = Env.get_class_dep
env x in
3811 (match parent_type with
3812 | Some
parent_type -> check_parent class_def class_type
parent_type
3814 let parent_ty = Decl_hint.hint
env.Env.decl_env
parent_ty in
3815 env, Some
x, parent_ty
3816 (* The only case where we have more than one parent class is when
3817 * dealing with interfaces and interfaces cannot use parent.
3820 | _ -> env, None
, (Reason.Rnone
, Tany
)
3822 and check_parent class_def class_type
parent_type =
3823 let position = fst class_def
.c_name
in
3824 (* Are all the parents in Hack? Do we know all their methods?
3825 * If so, let's check that the abstract methods have been implemented.
3827 if class_type
.tc_members_fully_known
3828 then check_parent_abstract
position parent_type class_type
;
3829 if parent_type.tc_final
3830 then Errors.extend_final
position parent_type.tc_pos
parent_type.tc_name
3833 and check_parent_abstract
position parent_type class_type
=
3834 let is_final = class_type
.tc_final
in
3835 if parent_type.tc_kind
= Ast.Cabstract
&&
3836 (class_type
.tc_kind
<> Ast.Cabstract
|| is_final)
3838 check_extend_abstract_meth ~
is_final position class_type
.tc_methods
;
3839 check_extend_abstract_meth ~
is_final position class_type
.tc_smethods
;
3840 check_extend_abstract_const ~
is_final position class_type
.tc_consts
;
3841 check_extend_abstract_typeconst
3842 ~
is_final position class_type
.tc_typeconsts
;
3845 and class_def tcopt
c =
3846 let filename = Pos.filename (fst
c.Nast.c_name
) in
3847 let dep = Dep.Class
(snd
c.c_name
) in
3848 let env = Env.empty tcopt
filename (Some
dep) in
3849 let c = TNBody.class_meth_bodies tcopt
c in
3850 if not
!auto_complete
then begin
3851 NastCheck.class_ env c;
3852 NastInitCheck.class_ env c;
3854 let tc = Env.get_class
env (snd
c.c_name
) in
3857 (* This can happen if there was an error during the declaration
3861 Typing_requirements.check_class
env tc;
3864 (* Given a class definition construct a type consisting of the
3865 * class instantiated at its generic parameters.
3866 * TODO AKENN: remove the need for embedded constraints here. *)
3867 and get_self_from_c
env c =
3868 let tparams = List.map
(fst
c.c_tparams
) begin fun (_, (p, s
), cstr) ->
3869 let cstr = List.map
cstr
3870 begin fun (ck
, h
) -> (ck
, Decl_hint.hint
env.Env.decl_env h
) end in
3871 Reason.Rwitness
p, Tgeneric
(s
, cstr)
3873 let ret = Reason.Rwitness
(fst
c.c_name
), Tapply
(c.c_name
, tparams) in
3876 and class_def_
env c tc =
3877 Typing_hooks.dispatch_enter_class_def_hook
c tc;
3878 let env = Env.set_mode
env c.c_mode
in
3879 let pc, _ = c.c_name
in
3881 (c.c_extends
@ c.c_implements
@ c.c_uses
)
3882 (Decl_hint.hint
env.Env.decl_env
) in
3883 TI.check_tparams_instantiable
env (fst
c.c_tparams
);
3884 let env = Phase.localize_generic_parameters_with_bounds
env (fst
c.c_tparams
)
3885 ~
ety_env:(Phase.env_with_self
env) in
3886 Typing_variance.class_ (Env.get_options
env) (snd
c.c_name
) tc impl;
3887 List.iter
impl (check_implements_tparaml
env);
3889 (* Set up self identifier and type *)
3890 let env = Env.set_self_id
env (snd
c.c_name
) in
3891 let self = get_self_from_c
env c in
3892 (* For enums, localize makes self:: into an abstract type, which we don't
3894 let env, self = match c.c_kind
with
3895 | Ast.Cenum
-> env, (fst
self, Tclass
(c.c_name
, []))
3896 | Ast.Cinterface
| Ast.Cabstract
| Ast.Ctrait
3897 | Ast.Cnormal
-> Phase.localize_with_self
env self in
3898 let env = Env.set_self
env self in
3900 let env, parent_id
, parent = class_def_parent
env c tc in
3901 let is_final = tc.tc_final
in
3902 if (tc.tc_kind
= Ast.Cnormal
|| is_final) && tc.tc_members_fully_known
3904 check_extend_abstract_meth ~
is_final pc tc.tc_methods
;
3905 check_extend_abstract_meth ~
is_final pc tc.tc_smethods
;
3906 check_extend_abstract_const ~
is_final pc tc.tc_consts
;
3907 check_extend_abstract_typeconst ~
is_final pc tc.tc_typeconsts
;
3909 let env = Env.set_parent
env parent in
3910 let env = match parent_id
with
3912 | Some parent_id
-> Env.set_parent_id
env parent_id
in
3913 if tc.tc_final
then begin
3915 | Ast.Cinterface
-> Errors.interface_final
(fst
c.c_name
)
3916 | Ast.Cabstract
-> ()
3917 | Ast.Ctrait
-> Errors.trait_final
(fst
c.c_name
)
3919 Errors.internal_error
pc "The parser should not parse final on enums"
3922 List.iter
impl (class_implements_type
env c);
3923 List.iter
c.c_vars
(class_var_def
env ~is_static
:false c);
3924 List.iter
c.c_methods
(method_def
env);
3925 List.iter
c.c_typeconsts
(typeconst_def
env);
3926 let const_types = List.map
c.c_consts
(class_const_def
env) in
3927 let env = Typing_enum.enum_class_check
env tc c.c_consts
const_types in
3928 class_constr_def
env c;
3929 let env = Env.set_static
env in
3930 List.iter
c.c_static_vars
(class_var_def
env ~is_static
:true c);
3931 List.iter
c.c_static_methods
(method_def
env);
3932 Typing_hooks.dispatch_exit_class_def_hook
c tc
3934 and check_extend_abstract_meth ~
is_final p smap
=
3935 SMap.iter
begin fun x ce
->
3936 match ce
.ce_type
with
3937 | r, Tfun
{ ft_abstract
= true; _ } ->
3938 Errors.implement_abstract ~
is_final p (Reason.to_pos
r) "method" x
3939 | _, (Tany
| Tmixed
| Tarray
(_, _) | Tgeneric
(_,_) | Toption
_ | Tprim
_
3940 | Tfun
_ | Tapply
(_, _) | Ttuple
_ | Tshape
_ | Taccess
(_, _)
3945 (* Type constants must be bound to a concrete type for non-abstract classes.
3947 and check_extend_abstract_typeconst ~
is_final p smap
=
3948 SMap.iter
begin fun x tc ->
3949 if tc.ttc_type
= None
then
3950 Errors.implement_abstract ~
is_final p (fst
tc.ttc_name
) "type constant" x
3953 and check_extend_abstract_const ~
is_final p smap
=
3954 SMap.iter
begin fun x cc
->
3955 match cc
.cc_type
with
3956 | r, Tgeneric
_ when not cc
.cc_synthesized
->
3957 Errors.implement_abstract ~
is_final p (Reason.to_pos
r) "constant" x
3958 | _, (Tany
| Tmixed
| Tarray
(_, _) | Toption
_ | Tprim
_ | Tfun
_
3959 | Tapply
(_, _) | Ttuple
_ | Tshape
_ | Taccess
(_, _) | Tthis
3963 and typeconst_def
env {
3964 c_tconst_name
= (pos, _);
3965 c_tconst_constraint
;
3968 let env, cstr = opt
Phase.hint_locl
env c_tconst_constraint
in
3969 let env, ty = opt
Phase.hint_locl
env c_tconst_type
in
3971 Option.map2
cstr ty ~
f:(Type.sub_type
pos Reason.URtypeconst_cstr
env)
3974 and class_const_def
env (h
, id
, e
) =
3977 | None
-> env, Env.fresh_type
()
3979 Phase.hint_locl
env h
3983 let env, ty'
= expr env e
in
3984 ignore
(Type.sub_type
(fst id
) Reason.URhint
env ty ty'
);
3988 and class_constr_def
env c =
3989 match c.c_constructor
with
3994 and class_implements_type
env c1 ctype2
=
3996 List.map_env
env (fst c1
.c_tparams
) begin fun env (_, (p, s
), param) ->
3997 let param = List.map
param begin fun (ck
, h
) ->
3998 let ty = Decl_hint.hint
env.Env.decl_env h
in
4000 env, (Reason.Rwitness
p, Tgeneric
(s
, param))
4002 let r = Reason.Rwitness
(fst c1
.c_name
) in
4003 let ctype1 = r, Tapply
(c1
.c_name
, params) in
4004 Typing_extends.check_implements
env ctype2
ctype1;
4007 and class_var_def
env ~is_static
c cv
=
4009 match cv
.cv_expr
with
4010 | None
-> env, Env.fresh_type
()
4011 | Some e
-> expr env e
in
4012 match cv
.cv_type
with
4013 | None
when Env.is_strict
env ->
4014 Errors.add_a_typehint
(fst cv
.cv_id
)
4016 let pos, name = cv
.cv_id
in
4017 let name = if is_static
then "$"^
name else name in
4018 let var_type = Reason.Rwitness
pos, Tany
in
4019 (match cv
.cv_expr
with
4021 Typing_suggest.uninitialized_member
(snd
c.c_name
) name env var_type ty;
4024 Typing_suggest.save_member
name env var_type ty;
4027 | Some
(p, _ as cty
) ->
4029 (* If this is an XHP attribute and we're in strict mode,
4030 relax to partial mode to allow the use of the "array"
4031 annotation without specifying type parameters. Until
4032 recently HHVM did not allow "array" with type parameters
4033 in XHP attribute declarations, so this is a temporary
4034 hack to support existing code for now. *)
4035 (* Task #5815945: Get rid of this Hack *)
4036 if cv
.cv_is_xhp
&& (Env.is_strict
env)
4037 then Env.set_mode
env FileInfo.Mpartial
4039 let cty = TI.instantiable_hint
env cty in
4040 let env, cty = Phase.localize_with_self
env cty in
4041 let _ = Type.sub_type
p Reason.URhint
env cty ty in
4044 and method_def
env m
=
4045 (* reset the expression dependent display ids for each method body *)
4046 Reason.expr_display_id_map
:= IMap.empty
;
4047 Typing_hooks.dispatch_enter_method_def_hook m
;
4048 let env = { env with Env.lenv = Env.empty_local
} in
4049 let env = Phase.localize_generic_parameters_with_bounds
env m
.m_tparams
4050 ~
ety_env:({ (Phase.env_with_self
env) with from_class
= Some CIstatic
; }) in
4051 TI.check_tparams_instantiable
env m
.m_tparams
;
4052 let env = Env.set_local
env this
(Env.get_self
env) in
4053 let env, ret = match m
.m_ret
with
4054 | None
-> env, (Reason.Rwitness
(fst m
.m_name
), Tany
)
4056 let ret = TI.instantiable_hint
env ret in
4057 (* If a 'this' type appears it needs to be compatiable with the
4061 { (Phase.env_with_self
env) with
4062 from_class
= Some CIstatic
} in
4063 Phase.localize ~
ety_env env ret in
4064 let m_params = match m
.m_variadic
with
4065 | FVvariadicArg
param -> param :: m
.m_params
4068 TI.check_params_instantiable
env m_params;
4069 let env, params = List.map_env
env m_params make_param_local_ty in
4070 if Env.is_strict
env then begin
4071 List.iter2_exn ~
f:(check_param
env) m_params params;
4073 if Attributes.mem
SN.UserAttributes.uaMemoize m
.m_user_attributes
then
4074 List.iter2_exn ~
f:(check_memoizable env) m_params params;
4075 let env = List.fold2_exn ~
f:bind_param ~init
:env params m_params in
4076 let nb = Nast.assert_named_body m
.m_body
in
4077 let env = fun_ ~abstract
:m
.m_abstract
env ret (fst m
.m_name
) nb m
.m_fun_kind
in
4079 List.fold_left
(Env.get_todo
env) ~
f:(fun env f -> f env) ~init
:env in
4081 | None
when Env.is_strict
env && snd m
.m_name
<> SN.Members.__destruct
->
4082 (* if we are in strict mode, the only case where we don't want to enforce
4083 * a return type is when the method is a destructor
4085 suggest_return env (fst m
.m_name
) ret
4088 Typing_hooks.dispatch_exit_method_def_hook m
4090 and typedef_def typedef
=
4091 let tid = (snd typedef
.t_name
) in
4092 let filename = Pos.filename (fst typedef
.t_kind
) in
4093 let dep = Typing_deps.Dep.Class
tid in
4095 Typing_env.empty
TypecheckerOptions.permissive
filename (Some
dep) in
4096 (* Mode for typedefs themselves doesn't really matter right now, but
4097 * they can expand hints, so make it loose so that the typedef doesn't
4098 * fail. (The hint will get re-checked with the proper mode anyways.)
4099 * Ideally the typedef would carry the right mode with it, but it's a
4100 * slightly larger change than I want to deal with right now. *)
4101 let env = Typing_env.set_mode
env FileInfo.Mdecl
in
4102 let env = Phase.localize_generic_parameters_with_bounds
env typedef
.t_tparams
4103 ~
ety_env:(Phase.env_with_self
env) in
4104 NastCheck.typedef
env typedef
;
4108 t_constraint
= tcstr
;
4110 t_user_attributes
= _;
4114 let ty = TI.instantiable_hint
env hint
in
4115 let env, ty = Phase.localize_with_self
env ty in
4116 begin match tcstr
with
4118 let cstr = TI.instantiable_hint
env tcstr
in
4119 let env, cstr = Phase.localize_with_self
env cstr in
4120 ignore
@@ Typing_ops.sub_type t_pos
Reason.URnewtype_cstr
env cstr ty
4124 | pos, Hshape fdm
->
4125 ignore
(check_shape_keys_validity
env pos (ShapeMap.keys fdm
))
4128 (* Calls the method of a class, but allows the f callback to override the
4129 * return value type *)
4130 and overload_function
p env class_id method_id
el uel
f =
4131 let env, ty = static_class_id
p env class_id
in
4133 class_get ~
is_method:true ~is_const
:false env ty method_id class_id
in
4134 (* call the function as declared to validate arity and input types,
4135 but ignore the result and overwrite with custom one *)
4136 let (env, res
), has_error
= Errors.try_with_error
4137 (fun () -> call
p env fty el uel
, false)
4138 (fun () -> (env, (Reason.Rwitness
p, Tany
)), true) in
4139 (* if there are errors already stop here - going forward would
4140 * report them twice *)
4141 if has_error
then env, res
4142 else f env fty res
el
4144 and update_array_type
p env e1
e2 valkind =
4145 let access_type = Typing_arrays.static_array_access
env e2 in
4147 Typing_arrays.update_array_type
p access_type in
4149 | `lvalue
| `lvalue_subexpr
->
4151 raw_expr ~
valkind:`lvalue_subexpr ~in_cond
:false env e1
in
4152 let env, ty1 = type_mapper env ty1 in
4154 | (_, Lvar
(_, x)) ->
4155 (* type_mapper has updated the type in ty1 typevars, but we
4156 need to update the local variable type too *)
4157 set_valid_rvalue
p env x ty1