Enforce output & access to static properties via coeffects
[hiphop-php.git] / hphp / hack / src / typing / typing.ml
blob428e74991aabb0dced1acf508e1d628d8e705b20
1 (*
2 * Copyright (c) 2015, Facebook, Inc.
3 * All rights reserved.
5 * This source code is licensed under the MIT license found in the
6 * LICENSE file in the "hack" directory of this source tree.
8 *)
10 (* This module implements the typing.
12 * Given an Nast.program, it infers the type of all the local
13 * variables, and checks that all the types are correct (aka
14 * consistent) *)
15 open Hh_prelude
16 open Common
17 open Aast
18 open Tast
19 open Typing_defs
20 open Typing_env_types
21 open Utils
22 open Typing_helpers
23 module TFTerm = Typing_func_terminality
24 module TUtils = Typing_utils
25 module Reason = Typing_reason
26 module Type = Typing_ops
27 module Env = Typing_env
28 module Inf = Typing_inference_env
29 module LEnv = Typing_lenv
30 module Async = Typing_async
31 module SubType = Typing_subtype
32 module Union = Typing_union
33 module Inter = Typing_intersection
34 module SN = Naming_special_names
35 module TVis = Typing_visibility
36 module Phase = Typing_phase
37 module TOG = Typing_object_get
38 module Subst = Decl_subst
39 module ExprDepTy = Typing_dependent_type.ExprDepTy
40 module TCO = TypecheckerOptions
41 module EnvFromDef = Typing_env_from_def
42 module C = Typing_continuations
43 module CMap = C.Map
44 module Try = Typing_try
45 module TR = Typing_reactivity
46 module FL = FeatureLogging
47 module MakeType = Typing_make_type
48 module Cls = Decl_provider.Class
49 module Partial = Partial_provider
50 module Fake = Typing_fake_members
51 module ExpectedTy = Typing_helpers.ExpectedTy
53 (*****************************************************************************)
54 (* Debugging *)
55 (*****************************************************************************)
57 (* A guess as to the last position we were typechecking, for use in debugging,
58 * such as figuring out what a runaway hh_server thread is doing. Updated
59 * only best-effort -- it's an approximation to point debugging in the right
60 * direction, nothing more. *)
61 let debug_last_pos = ref Pos.none
63 let debug_print_last_pos _ =
64 Hh_logger.info
65 "Last typecheck pos: %s"
66 (Pos.string (Pos.to_absolute !debug_last_pos))
68 (*****************************************************************************)
69 (* Helpers *)
70 (*****************************************************************************)
72 let err_witness env p = TUtils.terr env (Reason.Rwitness p)
74 (* Set all the types in an expression to the given type. *)
75 let with_type ty env (e : Nast.expr) : Tast.expr =
76 let visitor =
77 object
78 inherit [_] Aast.map
80 method on_'ex _ p = (p, ty)
82 method on_'fb _ _ = ()
84 method on_'en _ _ = env
86 method on_'hi _ _ = ty
87 end
89 visitor#on_expr () e
91 let expr_error env (r : Reason.t) (e : Nast.expr) =
92 let ty = TUtils.terr env r in
93 (env, with_type ty Tast.dummy_saved_env e, ty)
95 let expr_any env p e =
96 let ty = Typing_utils.mk_tany env p in
97 (env, with_type ty Tast.dummy_saved_env e, ty)
99 let unbound_name env (pos, name) e =
100 let strictish = Partial.should_check_error (Env.get_mode env) 4107 in
101 match Env.get_mode env with
102 | FileInfo.Mstrict ->
103 Errors.unbound_name_typing pos name;
104 expr_error env (Reason.Rwitness pos) e
105 | FileInfo.Mpartial when strictish ->
106 Errors.unbound_name_typing pos name;
107 expr_error env (Reason.Rwitness pos) e
108 | FileInfo.Mdecl
109 | FileInfo.Mpartial ->
110 expr_any env pos e
112 (* Is this type Traversable<vty> or Container<vty> for some vty? *)
113 let get_value_collection_inst ty =
114 match get_node ty with
115 | Tclass ((_, c), _, [vty])
116 when String.equal c SN.Collections.cTraversable
117 || String.equal c SN.Collections.cContainer ->
118 Some vty
119 (* If we're expecting a mixed or a nonnull then we can just assume
120 * that the element type is mixed *)
121 | Tnonnull -> Some (MakeType.mixed Reason.Rnone)
122 | Tany _ -> Some ty
123 | _ -> None
125 (* Is this type KeyedTraversable<kty,vty>
126 * or KeyedContainer<kty,vty>
127 * for some kty, vty?
129 let get_key_value_collection_inst p ty =
130 match get_node ty with
131 | Tclass ((_, c), _, [kty; vty])
132 when String.equal c SN.Collections.cKeyedTraversable
133 || String.equal c SN.Collections.cKeyedContainer ->
134 Some (kty, vty)
135 (* If we're expecting a mixed or a nonnull then we can just assume
136 * that the key type is arraykey and the value type is mixed *)
137 | Tnonnull ->
138 let arraykey = MakeType.arraykey (Reason.Rkey_value_collection_key p) in
139 let mixed = MakeType.mixed Reason.Rnone in
140 Some (arraykey, mixed)
141 | Tany _ -> Some (ty, ty)
142 | _ -> None
144 (* Is this type varray<vty> or a supertype for some vty? *)
145 let get_varray_inst ty =
146 match get_node ty with
147 (* It's varray<vty> *)
148 | Tvarray vty -> Some vty
149 | _ -> get_value_collection_inst ty
151 (* Is this type one of the value collection types with element type vty? *)
152 let get_vc_inst vc_kind ty =
153 match get_node ty with
154 | Tclass ((_, c), _, [vty]) when String.equal c (Nast.vc_kind_to_name vc_kind)
156 Some vty
157 | _ -> get_value_collection_inst ty
159 (* Is this type one of the three key-value collection types
160 * e.g. dict<kty,vty> or a supertype for some kty and vty? *)
161 let get_kvc_inst p kvc_kind ty =
162 match get_node ty with
163 | Tclass ((_, c), _, [kty; vty])
164 when String.equal c (Nast.kvc_kind_to_name kvc_kind) ->
165 Some (kty, vty)
166 | _ -> get_key_value_collection_inst p ty
168 (* Is this type darray<kty, vty> or a supertype for some kty and vty? *)
169 let get_darray_inst p ty =
170 match get_node ty with
171 (* It's darray<kty, vty> *)
172 | Tdarray (kty, vty) -> Some (kty, vty)
173 | _ -> get_key_value_collection_inst p ty
175 (* Check whether this is a function type that (a) either returns a disposable
176 * or (b) has the <<__ReturnDisposable>> attribute
178 let is_return_disposable_fun_type env ty =
179 let (_env, ty) = Env.expand_type env ty in
180 match get_node ty with
181 | Tfun ft ->
182 get_ft_return_disposable ft
183 || Option.is_some
184 (Typing_disposable.is_disposable_type env ft.ft_ret.et_type)
185 | _ -> false
187 (* Turn an environment into a local_id_map suitable to be embedded
188 * into an AssertEnv statement
190 let annot_map env =
191 match Env.next_cont_opt env with
192 | Some { Typing_per_cont_env.local_types; _ } ->
193 Some (Local_id.Map.map (fun (ty, pos, _expr_id) -> (pos, ty)) local_types)
194 | None -> None
196 (* Similar to annot_map above, but filters the map to only contain
197 * information about locals in lset
199 let refinement_annot_map env lset =
200 match annot_map env with
201 | Some map ->
202 let map =
203 Local_id.Map.filter (fun lid _ -> Local_id.Set.mem lid lset) map
205 if Local_id.Map.is_empty map then
206 None
207 else
208 Some map
209 | None -> None
211 let assert_env_blk ~pos ~at annotation_kind env_map_opt blk =
212 let mk_assert map = (pos, Aast.AssertEnv (annotation_kind, map)) in
213 let annot_blk = Option.to_list (Option.map ~f:mk_assert env_map_opt) in
214 match at with
215 | `Start -> annot_blk @ blk
216 | `End -> blk @ annot_blk
218 let assert_env_stmt ~pos ~at annotation_kind env_map_opt stmt =
219 let mk_assert map = (pos, Aast.AssertEnv (annotation_kind, map)) in
220 match env_map_opt with
221 | Some env_map ->
222 let blk =
223 match at with
224 | `Start -> [mk_assert env_map; (pos, stmt)]
225 | `End -> [(pos, stmt); mk_assert env_map]
227 Aast.Block blk
228 | None -> stmt
230 let set_tcopt_unstable_features env { fa_user_attributes; _ } =
231 match
232 Naming_attributes.find
233 SN.UserAttributes.uaEnableUnstableFeatures
234 fa_user_attributes
235 with
236 | None -> env
237 | Some { ua_name = _; ua_params } ->
238 let ( = ) = String.equal in
239 List.fold ua_params ~init:env ~f:(fun env feature ->
240 match snd feature with
241 | Aast.String s when s = SN.UnstableFeatures.coeffects_provisional ->
242 Env.map_tcopt ~f:TypecheckerOptions.set_coeffects env
243 | _ -> env)
245 (* Given a localized parameter type and parameter information, infer
246 * a type for the parameter default expression (if present) and check that
247 * it is a subtype of the parameter type (if present). If no parameter type
248 * is specified, then union with Tany. (So it's as though we did a conditional
249 * assignment of the default expression to the parameter).
250 * Set the type of the parameter in the locals environment *)
251 let rec bind_param env (ty1, param) =
252 let (env, param_te, ty1) =
253 match param.param_expr with
254 | None -> (env, None, ty1)
255 | Some e ->
256 let decl_hint =
257 Option.map
258 ~f:(Decl_hint.hint env.decl_env)
259 (hint_of_type_hint param.param_type_hint)
261 let enforced =
262 match decl_hint with
263 | None -> false
264 | Some ty -> Typing_enforceability.is_enforceable env ty
266 let ty1_enforced = { et_type = ty1; et_enforced = enforced } in
267 let expected =
268 ExpectedTy.make_and_allow_coercion
269 param.param_pos
270 Reason.URparam
271 ty1_enforced
273 let (env, te, ty2) = expr ~expected env e in
274 Typing_sequencing.sequence_check_expr e;
275 let (env, ty1) =
277 Option.is_none (hint_of_type_hint param.param_type_hint)
278 && (not @@ TCO.global_inference (Env.get_tcopt env))
279 (* ty1 will be Tany iff we have no type hint and we are not in
280 * 'infer missing mode'. When it ty1 is Tany we just union it with
281 * the type of the default expression *)
282 then
283 Union.union env ty1 ty2
284 (* Otherwise we have an explicit type, and the default expression type
285 * must be a subtype *)
286 else
287 let env =
288 Typing_coercion.coerce_type
289 param.param_pos
290 Reason.URhint
293 ty1_enforced
294 Errors.parameter_default_value_wrong_type
296 (env, ty1)
298 (env, Some te, ty1)
300 let (env, user_attributes) =
301 List.map_env env param.param_user_attributes user_attribute
303 let tparam =
305 Aast.param_annotation = Tast.make_expr_annotation param.param_pos ty1;
306 Aast.param_type_hint = (ty1, hint_of_type_hint param.param_type_hint);
307 Aast.param_is_variadic = param.param_is_variadic;
308 Aast.param_pos = param.param_pos;
309 Aast.param_name = param.param_name;
310 Aast.param_expr = param_te;
311 Aast.param_callconv = param.param_callconv;
312 Aast.param_user_attributes = user_attributes;
313 Aast.param_visibility = param.param_visibility;
316 let mode = get_param_mode param.param_callconv in
317 let id = Local_id.make_unscoped param.param_name in
318 let env = Env.set_local env id ty1 param.param_pos in
319 let env = Env.set_param env id (ty1, param.param_pos, mode) in
320 let env =
321 if has_accept_disposable_attribute param then
322 Env.set_using_var env id
323 else
326 let env =
327 match get_param_mutability param with
328 | Some Param_borrowed_mutable ->
329 Env.add_mutable_var
332 (param.param_pos, Typing_mutability_env.Borrowed)
333 | Some Param_owned_mutable ->
334 Env.add_mutable_var env id (param.param_pos, Typing_mutability_env.Mutable)
335 | Some Param_maybe_mutable ->
336 Env.add_mutable_var
339 (param.param_pos, Typing_mutability_env.MaybeMutable)
340 | None ->
341 Env.add_mutable_var
344 (param.param_pos, Typing_mutability_env.Immutable)
346 (env, tparam)
348 and check_inout_return ret_pos env =
349 let params = Local_id.Map.elements (Env.get_params env) in
350 List.fold params ~init:env ~f:(fun env (id, (ty, param_pos, mode)) ->
351 match mode with
352 | FPinout ->
353 (* Whenever the function exits normally, we require that each local
354 * corresponding to an inout parameter be compatible with the original
355 * type for the parameter (under subtyping rules). *)
356 let (local_ty, local_pos) = Env.get_local_pos env id in
357 let (env, ety) = Env.expand_type env local_ty in
358 let pos =
359 if not (Pos.equal Pos.none local_pos) then
360 local_pos
361 else if not (Pos.equal Pos.none ret_pos) then
362 ret_pos
363 else
364 param_pos
366 let param_ty = mk (Reason.Rinout_param (get_pos ty), get_node ty) in
367 Type.sub_type
369 Reason.URassign_inout
372 param_ty
373 Errors.inout_return_type_mismatch
374 | _ -> env)
376 (*****************************************************************************)
377 (* function used to type closures, functions and methods *)
378 (*****************************************************************************)
379 and fun_ ?(abstract = false) ?(disable = false) env return pos named_body f_kind
381 Env.with_env env (fun env ->
382 debug_last_pos := pos;
383 let env = Env.set_return env return in
384 let (env, tb) =
385 if disable then
386 let () =
387 Errors.internal_error
389 ( "Type inference for this function has been disabled by the "
390 ^ SN.UserAttributes.uaDisableTypecheckerInternal
391 ^ " attribute" )
393 block env []
394 else
395 block env named_body.fb_ast
397 Typing_sequencing.sequence_check_block named_body.fb_ast;
398 let { Typing_env_return_info.return_type = ret; _ } =
399 Env.get_return env
401 let env =
403 (not @@ LEnv.has_next env)
404 || abstract
405 || Nast.named_body_is_unsafe named_body
406 then
408 else
409 fun_implicit_return env pos ret.et_type f_kind
411 debug_last_pos := Pos.none;
412 (env, tb))
414 and fun_implicit_return env pos ret = function
415 | Ast_defs.FGenerator
416 | Ast_defs.FAsyncGenerator ->
418 | Ast_defs.FSync ->
419 (* A function without a terminal block has an implicit return; the
420 * "void" type *)
421 let env = check_inout_return Pos.none env in
422 let r = Reason.Rno_return pos in
423 let rty = MakeType.void r in
424 Typing_return.implicit_return env pos ~expected:ret ~actual:rty
425 | Ast_defs.FAsync ->
426 (* An async function without a terminal block has an implicit return;
427 * the Awaitable<void> type *)
428 let r = Reason.Rno_return_async pos in
429 let rty = MakeType.awaitable r (MakeType.void r) in
430 Typing_return.implicit_return env pos ~expected:ret ~actual:rty
432 and block env stl =
433 Typing_env.with_origin env Decl_counters.Body @@ fun env ->
434 List.map_env env stl ~f:stmt
436 (* Set a local; must not be already assigned if it is a using variable *)
437 and set_local ?(is_using_clause = false) env (pos, x) ty =
438 if Env.is_using_var env x then
439 if is_using_clause then
440 Errors.duplicate_using_var pos
441 else
442 Errors.illegal_disposable pos "assigned";
443 let env = Env.set_local env x ty pos in
444 if is_using_clause then
445 Env.set_using_var env x
446 else
449 (* Check an individual component in the expression `e` in the
450 * `using (e) { ... }` statement.
451 * This consists of either
452 * a simple assignment `$x = e`, in which `$x` is the using variable, or
453 * an arbitrary expression `e`, in which case a temporary is the using
454 * variable, inaccessible in the source.
455 * Return the typed expression and its type, and any variables that must
456 * be designated as "using variables" for avoiding escapes.
458 and check_using_expr has_await env ((pos, content) as using_clause) =
459 match content with
460 (* Simple assignment to local of form `$lvar = e` *)
461 | Binop (Ast_defs.Eq None, (lvar_pos, Lvar lvar), e) ->
462 let (env, te, ty) = expr ~is_using_clause:true env e in
463 let env =
464 Typing_disposable.enforce_is_disposable_type env has_await (fst e) ty
466 let env = set_local ~is_using_clause:true env lvar ty in
467 (* We are assigning a new value to the local variable, so we need to
468 * generate a new expression id
470 let env = Env.set_local_expr_id env (snd lvar) (Ident.tmp ()) in
471 ( env,
472 ( Tast.make_typed_expr
475 (Aast.Binop
476 ( Ast_defs.Eq None,
477 Tast.make_typed_expr lvar_pos ty (Aast.Lvar lvar),
478 te )),
479 [snd lvar] ) )
480 (* Arbitrary expression. This will be assigned to a temporary *)
481 | _ ->
482 let (env, typed_using_clause, ty) =
483 expr ~is_using_clause:true env using_clause
485 let env =
486 Typing_disposable.enforce_is_disposable_type env has_await pos ty
488 (env, (typed_using_clause, []))
490 (* Check the using clause e in
491 * `using (e) { ... }` statement (`has_await = false`) or
492 * `await using (e) { ... }` statement (`has_await = true`).
493 * `using_clauses` is a list of expressions.
494 * Return the typed expression, and any variables that must
495 * be designated as "using variables" for avoiding escapes.
497 and check_using_clause env has_await using_clauses =
498 let (env, pairs) =
499 List.map_env env using_clauses (check_using_expr has_await)
501 let (typed_using_clauses, vars) = List.unzip pairs in
502 (env, typed_using_clauses, List.concat vars)
504 (* Require a new construct with disposable *)
505 and enforce_return_disposable _env e =
506 match e with
507 | (_, New _) -> ()
508 | (_, Call _) -> ()
509 | (_, Await (_, Call _)) -> ()
510 | (p, _) -> Errors.invalid_return_disposable p
512 (* Wrappers around the function with the same name in Typing_lenv, which only
513 * performs the move/save and merge operation if we are in a try block or in a
514 * function with return type 'noreturn'.
515 * This enables significant perf improvement, because this is called at every
516 * function of method call, when most calls are outside of a try block. *)
517 and move_and_merge_next_in_catch env =
518 if env.in_try || TFTerm.is_noreturn env then
519 LEnv.move_and_merge_next_in_cont env C.Catch
520 else
521 LEnv.drop_cont env C.Next
523 and save_and_merge_next_in_catch env =
524 if env.in_try || TFTerm.is_noreturn env then
525 LEnv.save_and_merge_next_in_cont env C.Catch
526 else
529 and might_throw env = save_and_merge_next_in_catch env
531 and stmt env (pos, st) =
532 let (env, st) = stmt_ env pos st in
533 Typing_debug.log_env_if_too_big pos env;
534 (env, (pos, st))
536 and stmt_ env pos st =
537 (* Type check a loop. f env = (env, result) checks the body of the loop.
538 * We iterate over the loop until the "next" continuation environment is
539 * stable. alias_depth is supposed to be an upper bound on this; but in
540 * certain cases this fails (e.g. a generic type grows unboundedly). TODO:
541 * fix this.
543 let infer_loop env f =
544 let in_loop_outer = env.in_loop in
545 let alias_depth =
546 if in_loop_outer then
548 else
549 Typing_alias.get_depth (pos, st)
551 let env = { env with in_loop = true } in
552 let rec loop env n =
553 (* Remember the old environment *)
554 let old_next_entry = Env.next_cont_opt env in
555 let (env, result) = f env in
556 let new_next_entry = Env.next_cont_opt env in
557 (* Finish if we reach the bound, or if the environments match *)
559 Int.equal n alias_depth
560 || Typing_per_cont_ops.is_sub_opt_entry
561 Typing_subtype.is_sub_type
563 new_next_entry
564 old_next_entry
565 then
566 let env = { env with in_loop = in_loop_outer } in
567 (env, result)
568 else
569 loop env (n + 1)
571 loop env 1
573 let env = Env.open_tyvars env pos in
574 (fun (env, tb) ->
575 (Typing_solver.close_tyvars_and_solve env Errors.unify_error, tb))
577 match st with
578 | Fallthrough ->
579 let env =
580 if env.in_case then
581 LEnv.move_and_merge_next_in_cont env C.Fallthrough
582 else
585 (env, Aast.Fallthrough)
586 | Goto (_, label) ->
587 let env = LEnv.move_and_merge_next_in_cont env (C.Goto label) in
588 (env, Aast.Noop)
589 | GotoLabel (_, label) ->
590 let env = LEnv.update_next_from_conts env [C.Next; C.Goto label] in
591 (env, Aast.Noop)
592 | Noop -> (env, Aast.Noop)
593 | AssertEnv _ -> (env, Aast.Noop)
594 | Expr e ->
595 let (env, te, _) = expr env e in
596 let env =
597 if TFTerm.typed_expression_exits te then
598 LEnv.move_and_merge_next_in_cont env C.Exit
599 else
602 (env, Aast.Expr te)
603 | If (e, b1, b2) ->
604 let assert_refinement_env =
605 assert_env_blk ~pos ~at:`Start Aast.Refinement
607 let (env, te, _) = expr env e in
608 let (env, tb1, tb2) =
609 branch
611 (fun env ->
612 let (env, lset) = condition env true te in
613 let refinement_map = refinement_annot_map env lset in
614 let (env, b1) = block env b1 in
615 let b1 = assert_refinement_env refinement_map b1 in
616 (env, b1))
617 (fun env ->
618 let (env, lset) = condition env false te in
619 let refinement_map = refinement_annot_map env lset in
620 let (env, b2) = block env b2 in
621 let b2 = assert_refinement_env refinement_map b2 in
622 (env, b2))
624 (* TODO TAST: annotate with joined types *)
625 (env, Aast.If (te, tb1, tb2))
626 | Return None ->
627 let env = check_inout_return pos env in
628 let rty = MakeType.void (Reason.Rwitness pos) in
629 let { Typing_env_return_info.return_type = expected_return; _ } =
630 Env.get_return env
632 let expected_return =
633 Typing_return.strip_awaitable (Env.get_fn_kind env) env expected_return
635 let env =
636 match Env.get_fn_kind env with
637 | Ast_defs.FGenerator
638 | Ast_defs.FAsyncGenerator ->
640 | _ ->
641 Typing_return.implicit_return
644 ~expected:expected_return.et_type
645 ~actual:rty
647 let env = LEnv.move_and_merge_next_in_cont env C.Exit in
648 (env, Aast.Return None)
649 | Return (Some e) ->
650 let env = check_inout_return pos env in
651 let expr_pos = fst e in
652 let Typing_env_return_info.
654 return_type;
655 return_disposable;
656 return_mutable;
657 return_explicit;
658 return_void_to_rx;
660 Env.get_return env
662 let return_type =
663 Typing_return.strip_awaitable (Env.get_fn_kind env) env return_type
665 let expected =
666 if return_explicit then
667 Some
668 (ExpectedTy.make_and_allow_coercion
669 expr_pos
670 Reason.URreturn
671 return_type)
672 else
673 None
675 if return_disposable then enforce_return_disposable env e;
676 let (env, te, rty) =
677 expr ~is_using_clause:return_disposable ?expected env e
679 let env =
680 if not (equal_reactivity (env_reactivity env) Nonreactive) then
681 Typing_mutability.handle_value_in_return
682 ~function_returns_mutable:return_mutable
683 ~function_returns_void_for_rx:return_void_to_rx
685 env.function_pos
687 else
690 let return_type =
692 return_type with
693 et_type = TR.strip_condition_type_in_return env return_type.et_type;
696 (* This is a unify_error rather than a return_type_mismatch because the return
697 * statement is the problem, not the return type itself. *)
698 let env =
699 Typing_coercion.coerce_type
700 expr_pos
701 Reason.URreturn
704 return_type
705 Errors.unify_error
707 let env = LEnv.move_and_merge_next_in_cont env C.Exit in
708 (env, Aast.Return (Some te))
709 | Do (b, e) ->
710 (* NOTE: leaks scope as currently implemented; this matches
711 the behavior in naming (cf. `do_stmt` in naming/naming.ml).
713 let (env, (tb, te)) =
714 LEnv.stash_and_do env [C.Continue; C.Break; C.Do] (fun env ->
715 let env = LEnv.save_and_merge_next_in_cont env C.Do in
716 let (env, _) = block env b in
717 (* saving the locals in continue here even if there is no continue
718 * statement because they must be merged at the end of the loop, in
719 * case there is no iteration *)
720 let env = LEnv.save_and_merge_next_in_cont env C.Continue in
721 let (env, tb) =
722 infer_loop env (fun env ->
723 let env =
724 LEnv.update_next_from_conts env [C.Continue; C.Next]
726 (* The following is necessary in case there is an assignment in the
727 * expression *)
728 let (env, te, _) = expr env e in
729 let (env, _lset) = condition env true te in
730 let env = LEnv.update_next_from_conts env [C.Do; C.Next] in
731 let (env, tb) = block env b in
732 (env, tb))
734 let env = LEnv.update_next_from_conts env [C.Continue; C.Next] in
735 let (env, te, _) = expr env e in
736 let (env, _lset) = condition env false te in
737 let env = LEnv.update_next_from_conts env [C.Break; C.Next] in
738 (env, (tb, te)))
740 (env, Aast.Do (tb, te))
741 | While (e, b) ->
742 let (env, (te, tb, refinement_map)) =
743 LEnv.stash_and_do env [C.Continue; C.Break] (fun env ->
744 let env = LEnv.save_and_merge_next_in_cont env C.Continue in
745 let (env, tb) =
746 infer_loop env (fun env ->
747 let env =
748 LEnv.update_next_from_conts env [C.Continue; C.Next]
750 let join_map = annot_map env in
751 (* The following is necessary in case there is an assignment in the
752 * expression *)
753 let (env, te, _) = expr env e in
754 let (env, lset) = condition env true te in
755 let refinement_map = refinement_annot_map env lset in
756 (* TODO TAST: avoid repeated generation of block *)
757 let (env, tb) = block env b in
759 (* Annotate loop body with join and refined environments *)
760 let assert_env_blk = assert_env_blk ~pos ~at:`Start in
761 let tb = assert_env_blk Aast.Refinement refinement_map tb in
762 let tb = assert_env_blk Aast.Join join_map tb in
764 (env, tb))
766 let env = LEnv.update_next_from_conts env [C.Continue; C.Next] in
767 let (env, te, _) = expr env e in
768 let (env, lset) = condition env false te in
769 let refinement_map_at_exit = refinement_annot_map env lset in
770 let env = LEnv.update_next_from_conts env [C.Break; C.Next] in
771 (env, (te, tb, refinement_map_at_exit)))
773 let while_st = Aast.While (te, tb) in
774 (* Export the refined environment after the exit condition holds *)
775 let while_st =
776 assert_env_stmt ~pos ~at:`End Aast.Refinement refinement_map while_st
778 (env, while_st)
779 | Using
781 us_has_await = has_await;
782 us_exprs = (loc, using_clause);
783 us_block = using_block;
784 us_is_block_scoped;
785 } ->
786 let (env, typed_using_clause, using_vars) =
787 check_using_clause env has_await using_clause
789 let (env, typed_using_block) = block env using_block in
790 (* Remove any using variables from the environment, as they should not
791 * be in scope outside the block *)
792 let env = List.fold_left using_vars ~init:env ~f:Env.unset_local in
793 ( env,
794 Aast.Using
795 Aast.
797 us_has_await = has_await;
798 us_exprs = (loc, typed_using_clause);
799 us_block = typed_using_block;
800 us_is_block_scoped;
802 | For (e1, e2, e3, b) ->
803 let e2 =
804 match e2 with
805 | Some e2 -> e2
806 | None -> (Pos.none, True)
808 let (env, (te1, te2, te3, tb, refinement_map)) =
809 LEnv.stash_and_do env [C.Continue; C.Break] (fun env ->
810 (* For loops leak their initalizer, but nothing that's defined in the
811 body
813 let (env, te1, _) = exprs env e1 in
814 (* initializer *)
815 let env = LEnv.save_and_merge_next_in_cont env C.Continue in
816 let (env, (tb, te3)) =
817 infer_loop env (fun env ->
818 (* The following is necessary in case there is an assignment in the
819 * expression *)
820 let (env, te2, _) = expr env e2 in
821 let (env, lset) = condition env true te2 in
822 let refinement_map = refinement_annot_map env lset in
823 let (env, tb) = block env b in
824 let env =
825 LEnv.update_next_from_conts env [C.Continue; C.Next]
827 let join_map = annot_map env in
828 let (env, te3, _) = exprs env e3 in
830 (* Export the join and refinement environments *)
831 let assert_env_blk = assert_env_blk ~pos ~at:`Start in
832 let tb = assert_env_blk Aast.Refinement refinement_map tb in
833 let tb = assert_env_blk Aast.Join join_map tb in
835 (env, (tb, te3)))
837 let env = LEnv.update_next_from_conts env [C.Continue; C.Next] in
838 let (env, te2, _) = expr env e2 in
839 let (env, lset) = condition env false te2 in
840 let refinement_map_at_exit = refinement_annot_map env lset in
841 let env = LEnv.update_next_from_conts env [C.Break; C.Next] in
842 (env, (te1, te2, te3, tb, refinement_map_at_exit)))
844 let for_st = Aast.For (te1, Some te2, te3, tb) in
845 let for_st =
846 assert_env_stmt ~pos ~at:`End Aast.Refinement refinement_map for_st
848 (env, for_st)
849 | Switch (((pos, _) as e), cl) ->
850 let (env, te, ty) = expr env e in
851 (* NB: A 'continue' inside a 'switch' block is equivalent to a 'break'.
852 * See the note in
853 * http://php.net/manual/en/control-structures.continue.php *)
854 let (env, (te, tcl)) =
855 LEnv.stash_and_do env [C.Continue; C.Break] (fun env ->
856 let parent_locals = LEnv.get_all_locals env in
857 let case_list env = case_list parent_locals ty env pos cl in
858 let (env, tcl) = Env.in_case env case_list in
859 let env =
860 LEnv.update_next_from_conts env [C.Continue; C.Break; C.Next]
862 (env, (te, tcl)))
864 (env, Aast.Switch (te, tcl))
865 | Foreach (e1, e2, b) ->
866 (* It's safe to do foreach over a disposable, as no leaking is possible *)
867 let (env, te1, ty1) = expr ~accept_using_var:true env e1 in
868 let (env, (te1, te2, tb)) =
869 LEnv.stash_and_do env [C.Continue; C.Break] (fun env ->
870 let env = LEnv.save_and_merge_next_in_cont env C.Continue in
871 let (env, tk, tv) = as_expr env ty1 (fst e1) e2 in
872 let (env, (te2, tb)) =
873 infer_loop env (fun env ->
874 let env =
875 LEnv.update_next_from_conts env [C.Continue; C.Next]
877 let join_map = annot_map env in
878 let (env, te2) = bind_as_expr env (fst e1) tk tv e2 in
879 let (env, tb) = block env b in
880 (* Export the join environment *)
881 let tb = assert_env_blk ~pos ~at:`Start Aast.Join join_map tb in
882 (env, (te2, tb)))
884 let env =
885 LEnv.update_next_from_conts env [C.Continue; C.Break; C.Next]
887 (env, (te1, te2, tb)))
889 (env, Aast.Foreach (te1, te2, tb))
890 | Try (tb, cl, fb) ->
891 let (env, ttb, tcl, tfb) = try_catch env tb cl fb in
892 (env, Aast.Try (ttb, tcl, tfb))
893 | Awaitall (el, b) ->
894 let env = might_throw env in
895 let (env, el) =
896 List.fold_left el ~init:(env, []) ~f:(fun (env, tel) (e1, e2) ->
897 let (env, te2, ty2) = expr env e2 in
898 let (env, ty2) =
899 Async.overload_extract_from_awaitable env (fst e2) ty2
901 match e1 with
902 | Some e1 ->
903 let (env, _, _) = assign (fst e1) env (fst e1, Lvar e1) ty2 in
904 (env, (Some e1, te2) :: tel)
905 | None -> (env, (None, te2) :: tel))
907 let (env, b) = block env b in
908 (env, Aast.Awaitall (el, b))
909 | Throw e ->
910 let p = fst e in
911 let (env, te, ty) = expr env e in
912 let env = coerce_to_throwable p env ty in
913 let env = move_and_merge_next_in_catch env in
914 (env, Aast.Throw te)
915 | Continue ->
916 let env = LEnv.move_and_merge_next_in_cont env C.Continue in
917 (env, Aast.Continue)
918 | Break ->
919 let env = LEnv.move_and_merge_next_in_cont env C.Break in
920 (env, Aast.Break)
921 | Block _
922 | Markup _ ->
923 failwith
924 "Unexpected nodes in AST. These nodes should have been removed in naming."
926 and branch :
927 type res. env -> (env -> env * res) -> (env -> env * res) -> env * res * res
929 fun env branch1 branch2 ->
930 let parent_lenv = env.lenv in
931 let (env, tbr1) = branch1 env in
932 let lenv1 = env.lenv in
933 let env = { env with lenv = parent_lenv } in
934 let (env, tbr2) = branch2 env in
935 let lenv2 = env.lenv in
936 let env = LEnv.union_lenvs env parent_lenv lenv1 lenv2 in
937 (env, tbr1, tbr2)
939 and finally_cont fb env ctx =
940 (* The only locals in scope are the ones from the current continuation *)
941 let env = Env.env_with_locals env @@ CMap.singleton C.Next ctx in
942 let (env, _tfb) = block env fb in
943 (env, LEnv.get_all_locals env)
945 and finally env fb =
946 match fb with
947 | [] ->
948 let env = LEnv.update_next_from_conts env [C.Next; C.Finally] in
949 (env, [])
950 | _ ->
951 let parent_locals = LEnv.get_all_locals env in
952 (* First typecheck the finally block against all continuations merged
953 * together.
954 * During this phase, record errors found in the finally block, but discard
955 * the resulting environment. *)
956 let all_conts = Env.all_continuations env in
957 let env = LEnv.update_next_from_conts env all_conts in
958 let (env, tfb) = block env fb in
959 let env = LEnv.restore_conts_from env parent_locals all_conts in
960 (* Second, typecheck the finally block once against each continuation. This
961 * helps be more clever about what each continuation will be after the
962 * finally block.
963 * We don't want to record errors during this phase, because certain types
964 * of errors will fire wrongly. For example, if $x is nullable in some
965 * continuations but not in others, then we must use `?->` on $x, but an
966 * error will fire when typechecking the finally block againts continuations
967 * where $x is non-null. *)
968 let finally_cont env _key = finally_cont fb env in
969 let (env, locals_map) =
970 Errors.ignore_ (fun () -> CMap.map_env finally_cont env parent_locals)
972 let union env _key = LEnv.union_contextopts env in
973 let (env, locals) = Try.finally_merge union env locals_map all_conts in
974 (Env.env_with_locals env locals, tfb)
976 and try_catch env tb cl fb =
977 let parent_locals = LEnv.get_all_locals env in
978 let env =
979 LEnv.drop_conts env [C.Break; C.Continue; C.Exit; C.Catch; C.Finally]
981 let (env, (ttb, tcb)) =
982 Env.in_try env (fun env ->
983 let (env, ttb) = block env tb in
984 let env = LEnv.move_and_merge_next_in_cont env C.Finally in
985 let catchctx = LEnv.get_cont_option env C.Catch in
986 let (env, lenvtcblist) = List.map_env env ~f:(catch catchctx) cl in
987 let (lenvl, tcb) = List.unzip lenvtcblist in
988 let env = LEnv.union_lenv_list env env.lenv lenvl in
989 let env = LEnv.move_and_merge_next_in_cont env C.Finally in
990 (env, (ttb, tcb)))
992 let (env, tfb) = finally env fb in
993 let env = LEnv.update_next_from_conts env [C.Finally] in
994 let env = LEnv.drop_cont env C.Finally in
995 let env =
996 LEnv.restore_and_merge_conts_from
998 parent_locals
999 [C.Break; C.Continue; C.Exit; C.Catch; C.Finally]
1001 (env, ttb, tcb, tfb)
1003 and case_list parent_locals ty env switch_pos cl =
1004 let initialize_next_cont env =
1005 let env = LEnv.restore_conts_from env parent_locals [C.Next] in
1006 let env = LEnv.update_next_from_conts env [C.Next; C.Fallthrough] in
1007 LEnv.drop_cont env C.Fallthrough
1009 let check_fallthrough env switch_pos case_pos block rest_of_list ~is_default =
1010 if not @@ List.is_empty block then
1011 match rest_of_list with
1012 | []
1013 | [Default (_, [])] ->
1015 | _ ->
1016 begin
1017 match LEnv.get_cont_option env C.Next with
1018 | Some _ ->
1019 if is_default then
1020 Errors.default_fallthrough switch_pos
1021 else
1022 Errors.case_fallthrough switch_pos case_pos
1023 | None -> ()
1025 (* match *)
1026 (* match *)
1027 else
1030 let make_exhaustive_equivalent_case_list env cl =
1031 let has_default =
1032 List.exists cl ~f:(function
1033 | Default _ -> true
1034 | _ -> false)
1036 let (env, ty) =
1037 (* If it hasn't got a default clause then we need to solve type variables
1038 * in order to check for an enum *)
1039 if has_default then
1040 Env.expand_type env ty
1041 else
1042 Typing_solver.expand_type_and_solve
1044 ~description_of_expected:"a value"
1045 switch_pos
1047 Errors.unify_error
1049 let is_enum =
1050 let top_type =
1051 MakeType.class_type
1052 Reason.Rnone
1053 SN.Classes.cHH_BuiltinEnum
1054 [MakeType.mixed Reason.Rnone]
1056 Typing_subtype.is_sub_type_for_coercion env ty top_type
1058 (* If there is no default case and this is not a switch on enum (since
1059 * exhaustiveness is garanteed elsewhere on enums),
1060 * then add a default case for control flow correctness
1062 if has_default || is_enum then
1063 (env, cl, false)
1064 else
1065 (env, cl @ [Default (Pos.none, [])], true)
1067 let rec case_list env = function
1068 | [] -> (env, [])
1069 | Default (pos, b) :: rl ->
1070 let env = initialize_next_cont env in
1071 let (env, tb) = block env b in
1072 check_fallthrough env switch_pos pos b rl ~is_default:true;
1073 let (env, tcl) = case_list env rl in
1074 (env, Aast.Default (pos, tb) :: tcl)
1075 | Case (((pos, _) as e), b) :: rl ->
1076 let env = initialize_next_cont env in
1077 let (env, te, _) = expr env e in
1078 let (env, tb) = block env b in
1079 check_fallthrough env switch_pos pos b rl ~is_default:false;
1080 let (env, tcl) = case_list env rl in
1081 (env, Aast.Case (te, tb) :: tcl)
1083 let (env, cl, added_empty_default) =
1084 make_exhaustive_equivalent_case_list env cl
1086 let (env, tcl) = case_list env cl in
1087 let tcl =
1088 if added_empty_default then
1089 List.take tcl (List.length tcl - 1)
1090 else
1093 (env, tcl)
1095 and catch catchctx env (sid, exn_lvar, b) =
1096 let env = LEnv.replace_cont env C.Next catchctx in
1097 let cid = CI sid in
1098 let ety_p = fst sid in
1099 let (env, _, _, _) = instantiable_cid ety_p env cid [] in
1100 let (env, _tal, _te, ety) =
1101 static_class_id ~check_constraints:false ety_p env [] cid
1103 let env = coerce_to_throwable ety_p env ety in
1104 let env = set_local env exn_lvar ety in
1105 let (env, tb) = block env b in
1106 (env, (env.lenv, (sid, exn_lvar, tb)))
1108 and as_expr env ty1 pe e =
1109 let env = Env.open_tyvars env pe in
1110 (fun (env, expected_ty, tk, tv) ->
1111 let rec distribute_union env ty =
1112 let (env, ty) = Env.expand_type env ty in
1113 match get_node ty with
1114 | Tunion tyl -> List.fold tyl ~init:env ~f:distribute_union
1115 | _ ->
1116 if SubType.is_sub_type_for_union env ty (MakeType.dynamic Reason.Rnone)
1117 then
1118 let env = SubType.sub_type env ty tk Errors.unify_error in
1119 let env = SubType.sub_type env ty tv Errors.unify_error in
1121 else
1122 let ur = Reason.URforeach in
1123 Type.sub_type pe ur env ty expected_ty Errors.unify_error
1125 let env = distribute_union env ty1 in
1126 let env = Env.set_tyvar_variance env expected_ty in
1127 (Typing_solver.close_tyvars_and_solve env Errors.unify_error, tk, tv))
1129 let (env, tv) = Env.fresh_type env pe in
1130 match e with
1131 | As_v _ ->
1132 let tk = MakeType.mixed Reason.Rnone in
1133 (env, MakeType.traversable (Reason.Rforeach pe) tv, tk, tv)
1134 | As_kv _ ->
1135 let (env, tk) = Env.fresh_type env pe in
1136 (env, MakeType.keyed_traversable (Reason.Rforeach pe) tk tv, tk, tv)
1137 | Await_as_v _ ->
1138 let tk = MakeType.mixed Reason.Rnone in
1139 (env, MakeType.async_iterator (Reason.Rasyncforeach pe) tv, tk, tv)
1140 | Await_as_kv _ ->
1141 let (env, tk) = Env.fresh_type env pe in
1142 (env, MakeType.async_keyed_iterator (Reason.Rasyncforeach pe) tk tv, tk, tv)
1144 and bind_as_expr env p ty1 ty2 aexpr =
1145 let check_reassigned_mutable env te =
1146 if Env.env_local_reactive env then
1147 Typing_mutability.handle_assignment_mutability env te None
1148 else
1151 match aexpr with
1152 | As_v ev ->
1153 let (env, te, _) = assign p env ev ty2 in
1154 let env = check_reassigned_mutable env te in
1155 (env, Aast.As_v te)
1156 | Await_as_v (p, ev) ->
1157 let (env, te, _) = assign p env ev ty2 in
1158 let env = check_reassigned_mutable env te in
1159 (env, Aast.Await_as_v (p, te))
1160 | As_kv ((p, Lvar ((_, k) as id)), ev) ->
1161 let env = set_valid_rvalue p env k ty1 in
1162 let (env, te, _) = assign p env ev ty2 in
1163 let tk = Tast.make_typed_expr p ty1 (Aast.Lvar id) in
1164 let env = check_reassigned_mutable env tk in
1165 let env = check_reassigned_mutable env te in
1166 (env, Aast.As_kv (tk, te))
1167 | Await_as_kv (p, (p1, Lvar ((_, k) as id)), ev) ->
1168 let env = set_valid_rvalue p env k ty1 in
1169 let (env, te, _) = assign p env ev ty2 in
1170 let tk = Tast.make_typed_expr p1 ty1 (Aast.Lvar id) in
1171 let env = check_reassigned_mutable env tk in
1172 let env = check_reassigned_mutable env te in
1173 (env, Aast.Await_as_kv (p, tk, te))
1174 | _ ->
1175 (* TODO Probably impossible, should check that *)
1176 assert false
1178 and expr
1179 ?(expected : ExpectedTy.t option)
1180 ?(accept_using_var = false)
1181 ?(is_using_clause = false)
1182 ?(valkind = `other)
1183 ?(check_defined = true)
1184 ?in_await
1186 ((p, _) as e) =
1188 begin
1189 match expected with
1190 | None -> ()
1191 | Some ExpectedTy.{ reason = r; ty = { et_type = ty; _ }; _ } ->
1192 Typing_log.(
1193 log_with_level env "typing" 1 (fun () ->
1194 log_types
1198 Log_head
1199 ( "Typing.expr " ^ Typing_reason.string_of_ureason r,
1200 [Log_type ("expected_ty", ty)] );
1202 end;
1203 raw_expr
1204 ~accept_using_var
1205 ~is_using_clause
1206 ~valkind
1207 ~check_defined
1208 ?in_await
1209 ?expected
1212 with Inf.InconsistentTypeVarState _ as e ->
1213 (* we don't want to catch unwanted exceptions here, eg Timeouts *)
1214 Errors.exception_occurred p (Exception.wrap e);
1215 make_result env p Aast.Any @@ err_witness env p
1217 and raw_expr
1218 ?(accept_using_var = false)
1219 ?(is_using_clause = false)
1220 ?(expected : ExpectedTy.t option)
1221 ?lhs_of_null_coalesce
1222 ?(valkind = `other)
1223 ?(check_defined = true)
1224 ?in_await
1227 debug_last_pos := fst e;
1228 expr_
1229 ~accept_using_var
1230 ~is_using_clause
1231 ?expected
1232 ?lhs_of_null_coalesce
1233 ?in_await
1234 ~valkind
1235 ~check_defined
1239 and lvalue env e =
1240 let valkind = `lvalue in
1241 expr_ ~valkind ~check_defined:false env e
1243 and lvalues env el =
1244 match el with
1245 | [] -> (env, [], [])
1246 | e :: el ->
1247 let (env, te, ty) = lvalue env e in
1248 let (env, tel, tyl) = lvalues env el in
1249 (env, te :: tel, ty :: tyl)
1251 and is_pseudo_function s =
1252 String.equal s SN.PseudoFunctions.hh_show
1253 || String.equal s SN.PseudoFunctions.hh_show_env
1254 || String.equal s SN.PseudoFunctions.hh_log_level
1255 || String.equal s SN.PseudoFunctions.hh_force_solve
1256 || String.equal s SN.PseudoFunctions.hh_loop_forever
1258 and loop_forever env =
1259 (* forever = up to 10 minutes, to avoid accidentally stuck processes *)
1260 for i = 1 to 600 do
1261 (* Look up things in shared memory occasionally to have a chance to be
1262 * interrupted *)
1263 match Env.get_class env "FOR_TEST_ONLY" with
1264 | None -> Unix.sleep 1
1265 | _ -> assert false
1266 done;
1267 Utils.assert_false_log_backtrace
1268 (Some "hh_loop_forever was looping for more than 10 minutes")
1270 (* $x ?? 0 is handled similarly to $x ?: 0, except that the latter will also
1271 * look for sketchy null checks in the condition. *)
1272 (* TODO TAST: type refinement should be made explicit in the typed AST *)
1273 and eif env ~(expected : ExpectedTy.t option) ?in_await p c e1 e2 =
1274 let condition = condition ~lhs_of_null_coalesce:false in
1275 let (env, tc, tyc) = raw_expr ~lhs_of_null_coalesce:false env c in
1276 let parent_lenv = env.lenv in
1277 let (env, _lset) = condition env true tc in
1278 let (env, te1, ty1) =
1279 match e1 with
1280 | None ->
1281 let (env, ty) = Typing_solver.non_null env p tyc in
1282 (env, None, ty)
1283 | Some e1 ->
1284 let (env, te1, ty1) = expr ?expected ?in_await env e1 in
1285 (env, Some te1, ty1)
1287 let lenv1 = env.lenv in
1288 let env = { env with lenv = parent_lenv } in
1289 let (env, _lset) = condition env false tc in
1290 let (env, te2, ty2) = expr ?expected ?in_await env e2 in
1291 let lenv2 = env.lenv in
1292 let env = LEnv.union_lenvs env parent_lenv lenv1 lenv2 in
1293 let (env, ty) = Union.union env ty1 ty2 in
1294 make_result env p (Aast.Eif (tc, te1, te2)) ty
1296 and is_parameter env x = Local_id.Map.mem x (Env.get_params env)
1298 and check_escaping_var env (pos, x) =
1299 if Env.is_using_var env x then
1300 if Local_id.equal x this then
1301 Errors.escaping_this pos
1302 else if is_parameter env x then
1303 Errors.escaping_disposable_parameter pos
1304 else
1305 Errors.escaping_disposable pos
1306 else
1309 and exprs
1310 ?(accept_using_var = false)
1311 ?(expected : ExpectedTy.t option)
1312 ?(valkind = `other)
1313 ?(check_defined = true)
1315 el =
1316 match el with
1317 | [] -> (env, [], [])
1318 | e :: el ->
1319 let (env, te, ty) =
1320 expr ~accept_using_var ?expected ~valkind ~check_defined env e
1322 let (env, tel, tyl) =
1323 exprs ~accept_using_var ?expected ~valkind ~check_defined env el
1325 (env, te :: tel, ty :: tyl)
1327 and exprs_expected (pos, ur, expected_tyl) env el =
1328 match (el, expected_tyl) with
1329 | ([], _) -> (env, [], [])
1330 | (e :: el, expected_ty :: expected_tyl) ->
1331 let expected = ExpectedTy.make pos ur expected_ty in
1332 let (env, te, ty) = expr ~expected env e in
1333 let (env, tel, tyl) = exprs_expected (pos, ur, expected_tyl) env el in
1334 (env, te :: tel, ty :: tyl)
1335 | (el, []) -> exprs env el
1337 and make_result env p te ty =
1338 (* Set the variance of any type variables that were generated according
1339 * to how they appear in the expression type *)
1340 let env = Env.set_tyvar_variance env ty in
1341 (env, Tast.make_typed_expr p ty te, ty)
1343 and localize_targ env ta =
1344 let pos = fst ta in
1345 let (env, targ) = Phase.localize_targ ~check_well_kinded:true env ta in
1346 (env, targ, ExpectedTy.make pos Reason.URhint (fst targ))
1348 and set_function_pointer ty =
1349 match get_node ty with
1350 | Tfun ft ->
1351 let ft = set_ft_is_function_pointer ft true in
1352 mk (get_reason ty, Tfun ft)
1353 | _ -> ty
1355 and expr_
1356 ?(expected : ExpectedTy.t option)
1357 ?(accept_using_var = false)
1358 ?(is_using_clause = false)
1359 ?lhs_of_null_coalesce
1360 ?in_await
1361 ~(valkind : [> `lvalue | `lvalue_subexpr | `other ])
1362 ~check_defined
1364 ((p, e) as outer) =
1365 let env = Env.open_tyvars env p in
1366 (fun (env, te, ty) ->
1367 let env = Typing_solver.close_tyvars_and_solve env Errors.unify_error in
1368 (env, te, ty))
1370 let expr = expr ~check_defined in
1371 let exprs = exprs ~check_defined in
1372 let raw_expr = raw_expr ~check_defined in
1374 * Given a list of types, computes their supertype. If any of the types are
1375 * unknown (e.g., comes from PHP), the supertype will be Typing_utils.tany env.
1377 let compute_supertype
1378 ~(expected : ExpectedTy.t option) ~reason ~use_pos r env tys =
1379 let (env, supertype) =
1380 match expected with
1381 | None -> Env.fresh_type_reason env r
1382 | Some ExpectedTy.{ ty = { et_type = ty; _ }; _ } -> (env, ty)
1384 match get_node supertype with
1385 (* No need to check individual subtypes if expected type is mixed or any! *)
1386 | Tany _ -> (env, supertype)
1387 | _ ->
1388 let subtype_value env ty =
1389 Type.sub_type use_pos reason env ty supertype Errors.unify_error
1391 let env = List.fold_left tys ~init:env ~f:subtype_value in
1393 List.exists tys (fun ty ->
1394 equal_locl_ty_ (get_node ty) (Typing_utils.tany env))
1395 then
1396 (* If one of the values comes from PHP land, we have to be conservative
1397 * and consider that we don't know what the type of the values are. *)
1398 (env, Typing_utils.mk_tany env p)
1399 else
1400 (env, supertype)
1403 * Given a 'a list and a method to extract an expr and its ty from a 'a, this
1404 * function extracts a list of exprs from the list, and computes the supertype
1405 * of all of the expressions' tys.
1407 let compute_exprs_and_supertype
1408 ~(expected : ExpectedTy.t option)
1409 ?(reason = Reason.URarray_value)
1410 ~use_pos
1414 extract_expr_and_ty =
1415 let (env, exprs_and_tys) =
1416 List.map_env env l (extract_expr_and_ty ~expected)
1418 let (exprs, tys) = List.unzip exprs_and_tys in
1419 let (env, supertype) =
1420 compute_supertype ~expected ~reason ~use_pos r env tys
1422 (env, exprs, supertype)
1424 let forget_fake_members env p callexpr =
1425 (* Some functions are well known to not change the types of members, e.g.
1426 * `is_null`.
1427 * There are a lot of usages like
1428 * if (!is_null($x->a) && !is_null($x->a->b))
1429 * where the second is_null call invalidates the first condition.
1430 * This function is a bit best effort. Add stuff here when you want
1431 * To avoid adding too many undue HH_FIXMEs. *)
1432 match callexpr with
1433 | (_, Id (_, func))
1434 when String.equal func SN.StdlibFunctions.is_null
1435 || String.equal func SN.PseudoFunctions.isset ->
1437 | _ -> Env.forget_members env Reason.(Blame (p, BScall))
1439 let check_call
1440 ~is_using_clause
1441 ~(expected : ExpectedTy.t option)
1442 ?in_await
1446 explicit_targs
1448 unpacked_element =
1449 let (env, te, result) =
1450 dispatch_call
1451 ~is_using_clause
1452 ~expected
1453 ?in_await
1457 explicit_targs
1459 unpacked_element
1461 let env = forget_fake_members env p e in
1462 (env, te, result)
1464 match e with
1465 | Import _
1466 | Collection _
1467 | BracedExpr _ ->
1468 failwith "AST should not contain these nodes"
1469 | Omitted ->
1470 let ty = Typing_utils.mk_tany env p in
1471 make_result env p Aast.Omitted ty
1472 | Any -> expr_error env (Reason.Rwitness p) outer
1473 | Varray (th, el)
1474 | ValCollection (_, th, el) ->
1475 let (get_expected_kind, name, subtype_val, make_expr, make_ty) =
1476 match e with
1477 | ValCollection (kind, _, _) ->
1478 let class_name = Nast.vc_kind_to_name kind in
1479 let subtype_val =
1480 match kind with
1481 | Set
1482 | ImmSet
1483 | Keyset ->
1484 arraykey_value p class_name
1485 | Vector
1486 | ImmVector
1487 | Vec
1488 | Pair_ ->
1489 array_value
1491 ( get_vc_inst kind,
1492 class_name,
1493 subtype_val,
1494 (fun th elements -> Aast.ValCollection (kind, th, elements)),
1495 fun value_ty ->
1496 MakeType.class_type (Reason.Rwitness p) class_name [value_ty] )
1497 | Varray _ ->
1498 ( get_varray_inst,
1499 "varray",
1500 array_value,
1501 (fun th elements -> Aast.Varray (th, elements)),
1502 (fun value_ty -> MakeType.varray (Reason.Rwitness p) value_ty) )
1503 | _ ->
1504 (* The parent match makes this case impossible *)
1505 failwith "impossible match case"
1507 (* Use expected type to determine expected element type *)
1508 let (env, elem_expected, th) =
1509 match th with
1510 | Some (_, tv) ->
1511 let (env, tv, tv_expected) = localize_targ env tv in
1512 (env, Some tv_expected, Some tv)
1513 | _ ->
1514 begin
1515 match expand_expected_and_get_node env expected with
1516 | (env, Some (pos, ur, ety, _)) ->
1517 begin
1518 match get_expected_kind ety with
1519 | Some vty -> (env, Some (ExpectedTy.make pos ur vty), None)
1520 | None -> (env, None, None)
1522 | _ -> (env, None, None)
1525 let (env, tel, elem_ty) =
1526 compute_exprs_and_supertype
1527 ~expected:elem_expected
1528 ~use_pos:p
1529 ~reason:Reason.URvector
1530 (Reason.Rtype_variable_generics (p, "T", strip_ns name))
1533 subtype_val
1535 make_result env p (make_expr th tel) (make_ty elem_ty)
1536 | Darray (th, l)
1537 | KeyValCollection (_, th, l) ->
1538 let (get_expected_kind, name, make_expr, make_ty) =
1539 match e with
1540 | KeyValCollection (kind, _, _) ->
1541 let class_name = Nast.kvc_kind_to_name kind in
1542 ( get_kvc_inst p kind,
1543 class_name,
1544 (fun th pairs -> Aast.KeyValCollection (kind, th, pairs)),
1545 (fun k v -> MakeType.class_type (Reason.Rwitness p) class_name [k; v])
1547 | Darray _ ->
1548 ( get_darray_inst p,
1549 "darray",
1550 (fun th pairs -> Aast.Darray (th, pairs)),
1551 (fun k v -> MakeType.darray (Reason.Rwitness p) k v) )
1552 | _ ->
1553 (* The parent match makes this case impossible *)
1554 failwith "impossible match case"
1556 (* Use expected type to determine expected key and value types *)
1557 let (env, kexpected, vexpected, th) =
1558 match th with
1559 | Some ((_, tk), (_, tv)) ->
1560 let (env, tk, tk_expected) = localize_targ env tk in
1561 let (env, tv, tv_expected) = localize_targ env tv in
1562 (env, Some tk_expected, Some tv_expected, Some (tk, tv))
1563 | _ ->
1564 (* no explicit typehint, fallback to supplied expect *)
1565 begin
1566 match expand_expected_and_get_node env expected with
1567 | (env, Some (pos, reason, ety, _)) ->
1568 begin
1569 match get_expected_kind ety with
1570 | Some (kty, vty) ->
1571 let k_expected = ExpectedTy.make pos reason kty in
1572 let v_expected = ExpectedTy.make pos reason vty in
1573 (env, Some k_expected, Some v_expected, None)
1574 | None -> (env, None, None, None)
1576 | _ -> (env, None, None, None)
1579 let (kl, vl) = List.unzip l in
1580 let (env, tkl, k) =
1581 compute_exprs_and_supertype
1582 ~expected:kexpected
1583 ~use_pos:p
1584 ~reason:Reason.URkey
1585 (Reason.Rtype_variable_generics (p, "Tk", strip_ns name))
1588 (arraykey_value p name)
1590 let (env, tvl, v) =
1591 compute_exprs_and_supertype
1592 ~expected:vexpected
1593 ~use_pos:p
1594 ~reason:Reason.URvalue
1595 (Reason.Rtype_variable_generics (p, "Tv", strip_ns name))
1598 array_value
1600 let pairs = List.zip_exn tkl tvl in
1601 make_result env p (make_expr th pairs) (make_ty k v)
1602 | Clone e ->
1603 let (env, te, ty) = expr env e in
1604 (* Clone only works on objects; anything else fatals at runtime *)
1605 let tobj = mk (Reason.Rwitness p, Tobject) in
1606 let env = Type.sub_type p Reason.URclone env ty tobj Errors.unify_error in
1607 make_result env p (Aast.Clone te) ty
1608 | This ->
1609 if Option.is_none (Env.get_self_ty env) then Errors.this_var_outside_class p;
1610 if not accept_using_var then check_escaping_var env (p, this);
1611 let ty = Env.get_local env this in
1612 let r = Reason.Rwitness p in
1613 let ty = mk (r, TUtils.this_of (mk (r, get_node ty))) in
1614 make_result env p Aast.This ty
1615 | Assert (AE_assert e) ->
1616 let (env, te, _) = expr env e in
1617 let env = LEnv.save_and_merge_next_in_cont env C.Exit in
1618 let (env, _lset) = condition env true te in
1619 make_result
1622 (Aast.Assert (Aast.AE_assert te))
1623 (MakeType.void (Reason.Rwitness p))
1624 | True -> make_result env p Aast.True (MakeType.bool (Reason.Rwitness p))
1625 | False -> make_result env p Aast.False (MakeType.bool (Reason.Rwitness p))
1626 (* TODO TAST: consider checking that the integer is in range. Right now
1627 * it's possible for HHVM to fail on well-typed Hack code
1629 | Int s -> make_result env p (Aast.Int s) (MakeType.int (Reason.Rwitness p))
1630 | Float s ->
1631 make_result env p (Aast.Float s) (MakeType.float (Reason.Rwitness p))
1632 (* TODO TAST: consider introducing a "null" type, and defining ?t to
1633 * be null | t
1635 | Null -> make_result env p Aast.Null (MakeType.null (Reason.Rwitness p))
1636 | String s ->
1637 make_result env p (Aast.String s) (MakeType.string (Reason.Rwitness p))
1638 | String2 idl ->
1639 let (env, tel) = string2 env idl in
1640 make_result env p (Aast.String2 tel) (MakeType.string (Reason.Rwitness p))
1641 | PrefixedString (n, e) ->
1642 if String.( <> ) n "re" then (
1643 Errors.experimental_feature
1645 "String prefixes other than `re` are not yet supported.";
1646 expr_error env Reason.Rnone outer
1647 ) else
1648 let (env, te, ty) = expr env e in
1649 let pe = fst e in
1650 let env = Typing_substring.sub_string pe env ty in
1651 (match snd e with
1652 | String _ ->
1653 begin
1655 make_result
1658 (Aast.PrefixedString (n, te))
1659 (Typing_regex.type_pattern e)
1660 with
1661 | Pcre.Error (Pcre.BadPattern (s, i)) ->
1662 let s = s ^ " [" ^ string_of_int i ^ "]" in
1663 Errors.bad_regex_pattern pe s;
1664 expr_error env (Reason.Rregex pe) e
1665 | Typing_regex.Empty_regex_pattern ->
1666 Errors.bad_regex_pattern pe "This pattern is empty";
1667 expr_error env (Reason.Rregex pe) e
1668 | Typing_regex.Missing_delimiter ->
1669 Errors.bad_regex_pattern pe "Missing delimiter(s)";
1670 expr_error env (Reason.Rregex pe) e
1671 | Typing_regex.Invalid_global_option ->
1672 Errors.bad_regex_pattern pe "Invalid global option(s)";
1673 expr_error env (Reason.Rregex pe) e
1675 | String2 _ ->
1676 Errors.re_prefixed_non_string pe "Strings with embedded expressions";
1677 expr_error env (Reason.Rregex pe) e
1678 | _ ->
1679 Errors.re_prefixed_non_string pe "Non-strings";
1680 expr_error env (Reason.Rregex pe) e)
1681 | Fun_id x ->
1682 let (env, fty, _tal) = fun_type_of_id env x [] [] in
1683 make_result env p (Aast.Fun_id x) fty
1684 | Id ((cst_pos, cst_name) as id) ->
1685 (match Env.get_gconst env cst_name with
1686 | None when Partial.should_check_error (Env.get_mode env) 4106 ->
1687 Errors.unbound_global cst_pos;
1688 let ty = err_witness env cst_pos in
1689 make_result env cst_pos (Aast.Id id) ty
1690 | None -> make_result env p (Aast.Id id) (Typing_utils.mk_tany env cst_pos)
1691 | Some ty ->
1692 let (env, ty) = Phase.localize_with_self ~pos:p env ty in
1693 make_result env p (Aast.Id id) ty)
1694 | Method_id (instance, meth) ->
1695 (* Method_id is used when creating a "method pointer" using the magic
1696 * inst_meth function.
1698 * Typing this is pretty simple, we just need to check that instance->meth
1699 * is public+not static and then return its type.
1701 let (env, te, ty1) = expr env instance in
1702 let (env, (result, _tal)) =
1703 TOG.obj_get_
1704 ~inst_meth:true
1705 ~obj_pos:p
1706 ~is_method:true
1707 ~nullsafe:None
1708 ~coerce_from_ty:None
1709 ~is_nonnull:false
1710 ~explicit_targs:[]
1713 (CIexpr instance)
1714 meth
1715 (fun x -> x)
1716 Errors.unify_error
1718 let (env, result) =
1719 Env.FakeMembers.check_instance_invalid env instance (snd meth) result
1721 make_result env p (Aast.Method_id (te, meth)) result
1722 | Method_caller (((pos, class_name) as pos_cname), meth_name) ->
1723 (* meth_caller('X', 'foo') desugars to:
1724 * $x ==> $x->foo()
1726 let class_ = Env.get_class env class_name in
1727 (match class_ with
1728 | None -> unbound_name env pos_cname outer
1729 | Some class_ ->
1730 (* Create a class type for the given object instantiated with unresolved
1731 * types for its type parameters.
1733 let () =
1734 if Ast_defs.is_c_trait (Cls.kind class_) then
1735 Errors.meth_caller_trait pos class_name
1737 let (env, tvarl) =
1738 List.map_env env (Cls.tparams class_) (fun env _ ->
1739 Env.fresh_type env p)
1741 let params =
1742 List.map (Cls.tparams class_) (fun { tp_name = (p, n); _ } ->
1743 (* TODO(T69551141) handle type arguments for Tgeneric *)
1744 MakeType.generic (Reason.Rwitness p) n)
1746 let obj_type = MakeType.apply (Reason.Rwitness p) pos_cname params in
1747 let ety_env =
1749 (Phase.env_with_self env) with
1750 substs = TUtils.make_locl_subst_for_class_tparams class_ tvarl;
1753 let (env, local_obj_ty) = Phase.localize ~ety_env env obj_type in
1754 let (env, (fty, _tal)) =
1755 TOG.obj_get
1756 ~obj_pos:pos
1757 ~is_method:true
1758 ~nullsafe:None
1759 ~coerce_from_ty:None
1760 ~explicit_targs:[]
1762 local_obj_ty
1763 (CI (pos, class_name))
1764 meth_name
1765 Errors.unify_error
1767 let (env, fty) = Env.expand_type env fty in
1768 (match deref fty with
1769 | (reason, Tfun ftype) ->
1770 (* We are creating a fake closure:
1771 * function(Class $x, arg_types_of(Class::meth_name))
1772 : return_type_of(Class::meth_name)
1774 let ety_env =
1776 ety_env with
1777 substs = TUtils.make_locl_subst_for_class_tparams class_ tvarl;
1780 let env =
1781 Phase.check_tparams_constraints
1782 ~use_pos:p
1783 ~ety_env
1785 (Cls.tparams class_)
1787 let (env, local_obj_ty) = Phase.localize ~ety_env env obj_type in
1788 let local_obj_fp = TUtils.default_fun_param local_obj_ty in
1789 let fty = { ftype with ft_params = local_obj_fp :: ftype.ft_params } in
1790 let caller =
1792 ft_arity = fty.ft_arity;
1793 ft_tparams = fty.ft_tparams;
1794 ft_where_constraints = fty.ft_where_constraints;
1795 ft_params = fty.ft_params;
1796 ft_implicit_params = fty.ft_implicit_params;
1797 ft_ret = fty.ft_ret;
1798 (* propagate 'is_coroutine' from the method being called*)
1799 ft_flags = fty.ft_flags;
1800 ft_reactive = fty.ft_reactive;
1801 ft_ifc_decl = fty.ft_ifc_decl;
1804 make_result
1807 (Aast.Method_caller (pos_cname, meth_name))
1808 (mk (reason, Tfun caller))
1809 | _ ->
1810 (* This can happen if the method lives in PHP *)
1811 make_result
1814 (Aast.Method_caller (pos_cname, meth_name))
1815 (Typing_utils.mk_tany env pos)))
1816 | FunctionPointer (FP_class_const ((cpos, cid), meth), targs) ->
1817 let (env, _, ce, cty) =
1818 static_class_id ~check_constraints:true cpos env [] cid
1820 let (env, (fpty, tal)) =
1821 class_get
1822 ~is_method:true
1823 ~is_const:false
1824 ~incl_tc:false (* What is this? *)
1825 ~coerce_from_ty:None (* What is this? *)
1826 ~explicit_targs:targs
1827 ~function_pointer:true
1830 meth
1833 let env = Env.set_tyvar_variance env fpty in
1834 let fpty = set_function_pointer fpty in
1835 make_result
1838 (Aast.FunctionPointer (FP_class_const (ce, meth), tal))
1839 fpty
1840 | Smethod_id ((pc, cid), meth) ->
1841 (* Smethod_id is used when creating a "method pointer" using the magic
1842 * class_meth function.
1844 * Typing this is pretty simple, we just need to check that c::meth is
1845 * public+static and then return its type.
1847 let (class_, classname) =
1848 match cid with
1849 | CIself
1850 | CIstatic ->
1851 (Env.get_self_class env, Env.get_self_id env)
1852 | CI (_, const) when String.equal const SN.PseudoConsts.g__CLASS__ ->
1853 (Env.get_self_class env, Env.get_self_id env)
1854 | CI (_, id) -> (Env.get_class env id, Some id)
1855 | _ -> (None, None)
1857 let classname = Option.value classname ~default:"" in
1858 (match class_ with
1859 | None ->
1860 (* The class given as a static string was not found. *)
1861 unbound_name env (pc, classname) outer
1862 | Some class_ ->
1863 let smethod = Env.get_static_member true env class_ (snd meth) in
1864 (match smethod with
1865 | None ->
1866 (* The static method wasn't found. *)
1867 TOG.smember_not_found
1868 (fst meth)
1869 ~is_const:false
1870 ~is_method:true
1871 class_
1872 (snd meth)
1873 Errors.unify_error;
1874 expr_error env Reason.Rnone outer
1875 | Some ({ ce_type = (lazy ty); ce_pos = (lazy ce_pos); _ } as ce) ->
1876 let () =
1877 if get_ce_abstract ce then
1878 match cid with
1879 | CIstatic -> ()
1880 | _ -> Errors.class_meth_abstract_call classname (snd meth) p ce_pos
1882 let ce_visibility = ce.ce_visibility in
1883 let ce_deprecated = ce.ce_deprecated in
1884 let (env, _tal, te, cid_ty) =
1885 static_class_id ~check_constraints:true pc env [] cid
1887 let (env, cid_ty) = Env.expand_type env cid_ty in
1888 let tyargs =
1889 match get_node cid_ty with
1890 | Tclass (_, _, tyargs) -> tyargs
1891 | _ -> []
1893 let ety_env =
1895 type_expansions = [];
1896 substs = TUtils.make_locl_subst_for_class_tparams class_ tyargs;
1897 this_ty = cid_ty;
1898 from_class = Some cid;
1899 quiet = true;
1900 on_error = Errors.unify_error_at p;
1903 (match deref ty with
1904 | (r, Tfun ft) ->
1905 let ft =
1906 Typing_enforceability.compute_enforced_and_pessimize_fun_type env ft
1908 let def_pos = ce_pos in
1909 let (env, tal) =
1910 Phase.localize_targs
1911 ~check_well_kinded:true
1912 ~is_method:true
1913 ~def_pos:ce_pos
1914 ~use_pos:p
1915 ~use_name:(strip_ns (snd meth))
1917 ft.ft_tparams
1920 let (env, ft) =
1921 Phase.(
1922 localize_ft
1923 ~instantiation:
1924 Phase.
1926 use_name = strip_ns (snd meth);
1927 use_pos = p;
1928 explicit_targs = tal;
1930 ~ety_env
1931 ~def_pos:ce_pos
1935 let ty = mk (r, Tfun ft) in
1936 let use_pos = fst meth in
1937 TVis.check_deprecated ~use_pos ~def_pos ce_deprecated;
1938 (match ce_visibility with
1939 | Vpublic -> make_result env p (Aast.Smethod_id (te, meth)) ty
1940 | Vprivate _ ->
1941 Errors.private_class_meth ~def_pos ~use_pos;
1942 expr_error env r outer
1943 | Vprotected _ ->
1944 Errors.protected_class_meth ~def_pos ~use_pos;
1945 expr_error env r outer)
1946 | (r, _) ->
1947 Errors.internal_error p "We have a method which isn't callable";
1948 expr_error env r outer)))
1949 | Lplaceholder p ->
1950 let r = Reason.Rplaceholder p in
1951 let ty = MakeType.void r in
1952 make_result env p (Aast.Lplaceholder p) ty
1953 | Dollardollar _ when phys_equal valkind `lvalue ->
1954 Errors.dollardollar_lvalue p;
1955 expr_error env (Reason.Rwitness p) outer
1956 | Dollardollar id ->
1957 let ty = Env.get_local_check_defined env id in
1958 let env = might_throw env in
1959 make_result env p (Aast.Dollardollar id) ty
1960 | Lvar ((_, x) as id) ->
1961 if not accept_using_var then check_escaping_var env id;
1962 let ty =
1963 if check_defined then
1964 Env.get_local_check_defined env id
1965 else
1966 Env.get_local env x
1968 make_result env p (Aast.Lvar id) ty
1969 | List el ->
1970 let (env, tel, tyl) =
1971 match valkind with
1972 | `lvalue
1973 | `lvalue_subexpr ->
1974 lvalues env el
1975 | `other ->
1976 let (env, expected) = expand_expected_and_get_node env expected in
1977 (match expected with
1978 | Some (pos, ur, _, Ttuple expected_tyl) ->
1979 exprs_expected (pos, ur, expected_tyl) env el
1980 | _ -> exprs env el)
1982 let ty = MakeType.tuple (Reason.Rwitness p) tyl in
1983 make_result env p (Aast.List tel) ty
1984 | Pair (th, e1, e2) ->
1985 let (env, expected1, expected2, th) =
1986 match th with
1987 | Some ((_, t1), (_, t2)) ->
1988 let (env, t1, t1_expected) = localize_targ env t1 in
1989 let (env, t2, t2_expected) = localize_targ env t2 in
1990 (env, Some t1_expected, Some t2_expected, Some (t1, t2))
1991 | None ->
1992 (* Use expected type to determine expected element types *)
1993 (match expand_expected_and_get_node env expected with
1994 | (env, Some (pos, reason, _ty, Tclass ((_, k), _, [ty1; ty2])))
1995 when String.equal k SN.Collections.cPair ->
1996 let ty1_expected = ExpectedTy.make pos reason ty1 in
1997 let ty2_expected = ExpectedTy.make pos reason ty2 in
1998 (env, Some ty1_expected, Some ty2_expected, None)
1999 | _ -> (env, None, None, None))
2001 let (env, te1, ty1) = expr ?expected:expected1 env e1 in
2002 let (env, te2, ty2) = expr ?expected:expected2 env e2 in
2003 let p1 = fst e1 in
2004 let p2 = fst e2 in
2005 let (env, ty1) =
2006 compute_supertype
2007 ~expected:expected1
2008 ~reason:Reason.URpair_value
2009 ~use_pos:p1
2010 (Reason.Rtype_variable_generics (p1, "T1", "Pair"))
2012 [ty1]
2014 let (env, ty2) =
2015 compute_supertype
2016 ~expected:expected2
2017 ~reason:Reason.URpair_value
2018 ~use_pos:p2
2019 (Reason.Rtype_variable_generics (p2, "T2", "Pair"))
2021 [ty2]
2023 let ty = MakeType.pair (Reason.Rwitness p) ty1 ty2 in
2024 make_result env p (Aast.Pair (th, te1, te2)) ty
2025 | Array_get (e, None) ->
2026 let (env, te, _) = update_array_type p env e valkind in
2027 let env = might_throw env in
2028 (* NAST check reports an error if [] is used for reading in an
2029 lvalue context. *)
2030 let ty = err_witness env p in
2031 make_result env p (Aast.Array_get (te, None)) ty
2032 | Array_get (e1, Some e2) ->
2033 let (env, te1, ty1) =
2034 update_array_type ?lhs_of_null_coalesce p env e1 valkind
2036 let (env, te2, ty2) = expr env e2 in
2037 let env = might_throw env in
2038 let is_lvalue = phys_equal valkind `lvalue in
2039 let (env, ty) =
2040 Typing_array_access.array_get
2041 ~array_pos:(fst e1)
2042 ~expr_pos:p
2043 ?lhs_of_null_coalesce
2044 is_lvalue
2050 make_result env p (Aast.Array_get (te1, Some te2)) ty
2051 | Call ((pos_id, Id ((_, s) as id)), [], el, None) when is_pseudo_function s
2053 let (env, tel, tys) = exprs ~accept_using_var:true env el in
2054 let env =
2055 if String.equal s SN.PseudoFunctions.hh_show then (
2056 List.iter tys (Typing_log.hh_show p env);
2058 ) else if String.equal s SN.PseudoFunctions.hh_show_env then (
2059 Typing_log.hh_show_env p env;
2061 ) else if String.equal s SN.PseudoFunctions.hh_log_level then
2062 match el with
2063 | [(_, String key_str); (_, Int level_str)] ->
2064 Env.set_log_level env key_str (int_of_string level_str)
2065 | _ -> env
2066 else if String.equal s SN.PseudoFunctions.hh_force_solve then
2067 Typing_solver.solve_all_unsolved_tyvars env Errors.unify_error
2068 else if String.equal s SN.PseudoFunctions.hh_loop_forever then (
2069 loop_forever env;
2071 ) else
2074 let ty = MakeType.void (Reason.Rwitness p) in
2075 make_result
2078 (Aast.Call
2079 ( Tast.make_typed_expr pos_id (TUtils.mk_tany env pos_id) (Aast.Id id),
2081 tel,
2082 None ))
2084 | Call (e, explicit_targs, el, unpacked_element) ->
2085 let env =
2086 match snd e with
2087 | Id (pos, f) when String.equal f SN.SpecialFunctions.echo ->
2088 Typing_local_ops.enforce_output pos env
2089 | _ -> env
2091 let env = might_throw env in
2092 let (env, te, ty) =
2093 check_call
2094 ~is_using_clause
2095 ~expected
2096 ?in_await
2100 explicit_targs
2102 unpacked_element
2104 (env, te, ty)
2105 | FunctionPointer (FP_id fid, targs) ->
2106 let (env, fty, targs) = fun_type_of_id env fid targs [] in
2107 let e = Aast.FunctionPointer (FP_id fid, targs) in
2108 let fty = set_function_pointer fty in
2109 make_result env p e fty
2110 | Binop (Ast_defs.QuestionQuestion, e1, e2) ->
2111 let (env, te1, ty1) = raw_expr ~lhs_of_null_coalesce:true env e1 in
2112 let (env, te2, ty2) = expr ?expected env e2 in
2113 let (env, ty1') = Env.fresh_type env (fst e1) in
2114 let env =
2115 SubType.sub_type
2118 (MakeType.nullable_locl Reason.Rnone ty1')
2119 Errors.unify_error
2121 (* Essentially mimic a call to
2122 * function coalesce<Tr, Ta as Tr, Tb as Tr>(?Ta, Tb): Tr
2123 * That way we let the constraint solver take care of the union logic.
2125 let (env, ty_result) = Env.fresh_type env (fst e2) in
2126 let env = SubType.sub_type env ty1' ty_result Errors.unify_error in
2127 let env = SubType.sub_type env ty2 ty_result Errors.unify_error in
2128 make_result
2131 (Aast.Binop (Ast_defs.QuestionQuestion, te1, te2))
2132 ty_result
2133 (* For example, e1 += e2. This is typed and translated as if
2134 * written e1 = e1 + e2.
2135 * TODO TAST: is this right? e1 will get evaluated more than once
2137 | Binop (Ast_defs.Eq (Some op), e1, e2) ->
2138 begin
2139 match (op, snd e1) with
2140 | (Ast_defs.QuestionQuestion, Class_get _) ->
2141 Errors.experimental_feature
2143 "null coalesce assignment operator with static properties";
2144 expr_error env Reason.Rnone outer
2145 | _ ->
2146 let e_fake =
2147 (p, Binop (Ast_defs.Eq None, e1, (p, Binop (op, e1, e2))))
2149 let (env, te_fake, ty) = raw_expr env e_fake in
2150 begin
2151 match snd te_fake with
2152 | Aast.Binop (_, te1, (_, Aast.Binop (_, _, te2))) ->
2153 let te = Aast.Binop (Ast_defs.Eq (Some op), te1, te2) in
2154 make_result env p te ty
2155 | _ -> assert false
2158 | Binop (Ast_defs.Eq None, e1, e2) ->
2159 let (env, te2, ty2) = raw_expr env e2 in
2160 let (env, te1, ty) = assign p env e1 ty2 in
2161 let env =
2162 if Env.env_local_reactive env then
2163 Typing_mutability.handle_assignment_mutability env te1 (Some (snd te2))
2164 else
2167 make_result env p (Aast.Binop (Ast_defs.Eq None, te1, te2)) ty
2168 | Binop (((Ast_defs.Ampamp | Ast_defs.Barbar) as bop), e1, e2) ->
2169 let c = Ast_defs.(equal_bop bop Ampamp) in
2170 let (env, te1, _) = expr env e1 in
2171 let lenv = env.lenv in
2172 let (env, _lset) = condition env c te1 in
2173 let (env, te2, _) = expr env e2 in
2174 let env = { env with lenv } in
2175 make_result
2178 (Aast.Binop (bop, te1, te2))
2179 (MakeType.bool (Reason.Rlogic_ret p))
2180 | Binop (bop, e1, e2) ->
2181 let (env, te1, ty1) = raw_expr env e1 in
2182 let (env, te2, ty2) = raw_expr env e2 in
2183 let env =
2184 match bop with
2185 (* TODO: This could be less conservative: we only need to account for
2186 * the possibility of exception if the operator is `/` or `/=`.
2188 | Ast_defs.Eqeqeq
2189 | Ast_defs.Diff2 ->
2191 | _ -> might_throw env
2193 let (env, te3, ty) =
2194 Typing_arithmetic.binop p env bop (fst e1) te1 ty1 (fst e2) te2 ty2
2196 (env, te3, ty)
2197 | Pipe (e0, e1, e2) ->
2198 (* If it weren't for local variable assignment or refinement the pipe
2199 * expression e1 |> e2 could be typed using this rule (E is environment with
2200 * types for locals):
2202 * E |- e1 : ty1 E[$$:ty1] |- e2 : ty2
2203 * --------------------------------------
2204 * E |- e1|>e2 : ty2
2206 * The possibility of e2 changing the types of locals in E means that E
2207 * can evolve, and so we need to restore $$ to its original state.
2209 let (env, te1, ty1) = expr env e1 in
2210 let dd_var = Local_id.make_unscoped SN.SpecialIdents.dollardollar in
2211 let dd_old_ty =
2212 if Env.is_local_defined env dd_var then
2213 Some (Env.get_local_pos env dd_var)
2214 else
2215 None
2217 let env = Env.set_local env dd_var ty1 Pos.none in
2218 let (env, te2, ty2) = expr env e2 in
2219 let env =
2220 match dd_old_ty with
2221 | None -> Env.unset_local env dd_var
2222 | Some (ty, pos) -> Env.set_local env dd_var ty pos
2224 make_result env p (Aast.Pipe (e0, te1, te2)) ty2
2225 | Unop (uop, e) ->
2226 let (env, te, ty) = raw_expr env e in
2227 let env = might_throw env in
2228 Typing_arithmetic.unop p env uop te ty
2229 | Eif (c, e1, e2) -> eif env ~expected ?in_await p c e1 e2
2230 | Class_const ((p, CI sid), pstr)
2231 when String.equal (snd pstr) "class" && Env.is_typedef env (snd sid) ->
2232 begin
2233 match Env.get_typedef env (snd sid) with
2234 | Some { td_tparams = tparaml; _ } ->
2235 (* Typedef type parameters cannot have constraints *)
2236 let params =
2237 List.map
2239 begin
2240 fun { tp_name = (p, x); _ } ->
2241 (* TODO(T69551141) handle type arguments for Tgeneric *)
2242 MakeType.generic (Reason.Rwitness p) x
2244 tparaml
2246 let tdef = mk (Reason.Rwitness p, Tapply (sid, params)) in
2247 let typename =
2248 mk (Reason.Rwitness p, Tapply ((p, SN.Classes.cTypename), [tdef]))
2250 let (env, tparams) =
2251 List.map_env env tparaml (fun env tp ->
2252 Env.fresh_type env (fst tp.tp_name))
2254 let ety_env =
2256 (Phase.env_with_self env) with
2257 substs = Subst.make_locl tparaml tparams;
2260 let env =
2261 Phase.check_tparams_constraints ~use_pos:p ~ety_env env tparaml
2263 let (env, ty) = Phase.localize ~ety_env env typename in
2264 make_result env p (Class_const (((p, ty), CI sid), pstr)) ty
2265 | None ->
2266 (* Should not expect None as we've checked whether the sid is a typedef *)
2267 expr_error env (Reason.Rwitness p) outer
2269 | Class_const (cid, mid) -> class_const env p (cid, mid)
2270 | Class_get ((cpos, cid), CGstring mid, in_parens)
2271 when Env.FakeMembers.is_valid_static env cid (snd mid) ->
2272 let (env, local) = Env.FakeMembers.make_static env cid (snd mid) p in
2273 let local = (p, Lvar (p, local)) in
2274 let (env, _, ty) = expr env local in
2275 let (env, _tal, te, _) =
2276 static_class_id ~check_constraints:false cpos env [] cid
2278 make_result env p (Aast.Class_get (te, Aast.CGstring mid, in_parens)) ty
2279 | Class_get ((cpos, cid), CGstring ((ppos, _) as mid), in_parens) ->
2280 let (env, _tal, te, cty) =
2281 static_class_id ~check_constraints:false cpos env [] cid
2283 let env = might_throw env in
2284 let (env, (ty, _tal)) =
2285 class_get
2286 ~is_method:false
2287 ~is_const:false
2288 ~coerce_from_ty:None
2294 let (env, ty) = Env.FakeMembers.check_static_invalid env cid (snd mid) ty in
2295 let env = Typing_local_ops.enforce_static_property_access ppos env in
2296 make_result env p (Aast.Class_get (te, Aast.CGstring mid, in_parens)) ty
2297 (* Fake member property access. For example:
2298 * if ($x->f !== null) { ...$x->f... }
2300 | Class_get (_, CGexpr _, _) ->
2301 failwith "AST should not have any CGexprs after naming"
2302 | Obj_get (e, (pid, Id (py, y)), nf, in_parens)
2303 when Env.FakeMembers.is_valid env e y ->
2304 let env = might_throw env in
2305 let (env, local) = Env.FakeMembers.make env e y p in
2306 let local = (p, Lvar (p, local)) in
2307 let (env, _, ty) = expr env local in
2308 let (env, t_lhs, _) = expr ~accept_using_var:true env e in
2309 let t_rhs = Tast.make_typed_expr pid ty (Aast.Id (py, y)) in
2310 make_result env p (Aast.Obj_get (t_lhs, t_rhs, nf, in_parens)) ty
2311 (* Statically-known instance property access e.g. $x->f *)
2312 | Obj_get (e1, (pm, Id m), nullflavor, in_parens) ->
2313 let nullsafe =
2314 match nullflavor with
2315 | OG_nullthrows -> None
2316 | OG_nullsafe -> Some p
2318 let (env, te1, ty1) = expr ~accept_using_var:true env e1 in
2319 let env = might_throw env in
2320 (* We typecheck Obj_get by checking whether it is a subtype of
2321 Thas_member(m, #1) where #1 is a fresh type variable. *)
2322 let (env, mem_ty) = Env.fresh_type env p in
2323 let r = Reason.Rwitness (fst e1) in
2324 let has_member_ty =
2325 MakeType.has_member
2327 ~name:m
2328 ~ty:mem_ty
2329 ~class_id:(CIexpr e1)
2330 ~explicit_targs:None
2332 let lty1 = LoclType ty1 in
2333 let (env, result_ty) =
2334 match nullsafe with
2335 | None ->
2336 let env =
2337 Type.sub_type_i
2338 (fst e1)
2339 Reason.URnone
2341 lty1
2342 has_member_ty
2343 Errors.unify_error
2345 (env, mem_ty)
2346 | Some _ ->
2347 (* In that case ty1 is a subtype of ?Thas_member(m, #1)
2348 and the result is ?#1 if ty1 is nullable. *)
2349 let r = Reason.Rnullsafe_op p in
2350 let null_ty = MakeType.null r in
2351 let (env, null_has_mem_ty) =
2352 Union.union_i env r has_member_ty null_ty
2354 let env =
2355 Type.sub_type_i
2356 (fst e1)
2357 Reason.URnone
2359 lty1
2360 null_has_mem_ty
2361 Errors.unify_error
2363 let (env, null_or_nothing_ty) = Inter.intersect env ~r null_ty ty1 in
2364 let (env, result_ty) = Union.union env null_or_nothing_ty mem_ty in
2365 (env, result_ty)
2367 let (env, result_ty) =
2368 Env.FakeMembers.check_instance_invalid env e1 (snd m) result_ty
2370 make_result
2373 (Aast.Obj_get
2374 ( te1,
2375 Tast.make_typed_expr pm result_ty (Aast.Id m),
2376 nullflavor,
2377 in_parens ))
2378 result_ty
2379 (* Dynamic instance property access e.g. $x->$f *)
2380 | Obj_get (e1, e2, nullflavor, in_parens) ->
2381 let (env, te1, ty1) = expr ~accept_using_var:true env e1 in
2382 let (env, te2, _) = expr env e2 in
2383 let ty =
2384 if TUtils.is_dynamic env ty1 then
2385 MakeType.dynamic (Reason.Rwitness p)
2386 else
2387 Typing_utils.mk_tany env p
2389 let ((pos, _), te2) = te2 in
2390 let env = might_throw env in
2391 let te2 = Tast.make_typed_expr pos ty te2 in
2392 make_result env p (Aast.Obj_get (te1, te2, nullflavor, in_parens)) ty
2393 | Yield_break ->
2394 make_result env p Aast.Yield_break (Typing_utils.mk_tany env p)
2395 | Yield af ->
2396 let (env, (taf, opt_key, value)) = array_field env af in
2397 let (env, send) = Env.fresh_type env p in
2398 let (env, key) =
2399 match (af, opt_key) with
2400 | (AFvalue (p, _), None) ->
2401 begin
2402 match Env.get_fn_kind env with
2403 | Ast_defs.FSync
2404 | Ast_defs.FAsync ->
2405 Errors.internal_error p "yield found in non-generator";
2406 (env, Typing_utils.mk_tany env p)
2407 | Ast_defs.FGenerator -> (env, MakeType.int (Reason.Rwitness p))
2408 | Ast_defs.FAsyncGenerator ->
2409 let (env, ty) = Env.fresh_type env p in
2410 (env, MakeType.nullable_locl (Reason.Ryield_asyncnull p) ty)
2412 | (_, Some x) -> (env, x)
2413 | (_, _) -> assert false
2415 let rty =
2416 match Env.get_fn_kind env with
2417 | Ast_defs.FGenerator ->
2418 MakeType.generator (Reason.Ryield_gen p) key value send
2419 | Ast_defs.FAsyncGenerator ->
2420 MakeType.async_generator (Reason.Ryield_asyncgen p) key value send
2421 | Ast_defs.FSync
2422 | Ast_defs.FAsync ->
2423 failwith "Parsing should never allow this"
2425 let Typing_env_return_info.{ return_type = expected_return; _ } =
2426 Env.get_return env
2428 let env =
2429 Typing_coercion.coerce_type
2431 Reason.URyield
2434 expected_return
2435 Errors.unify_error
2437 let env = Env.forget_members env Reason.(Blame (p, BScall)) in
2438 let env = LEnv.save_and_merge_next_in_cont env C.Exit in
2439 make_result
2442 (Aast.Yield taf)
2443 (MakeType.nullable_locl (Reason.Ryield_send p) send)
2444 | Await e ->
2445 let env = might_throw env in
2446 (* Await is permitted in a using clause e.g. using (await make_handle()) *)
2447 let (env, te, rty) =
2448 expr ~is_using_clause ~in_await:(Reason.Rwitness p) env e
2450 let (env, ty) = Async.overload_extract_from_awaitable env p rty in
2451 make_result env p (Aast.Await te) ty
2452 | Suspend e ->
2453 let (env, te, ty) =
2454 match e with
2455 | (_, Call (e, explicit_targs, el, unpacked_element)) ->
2456 let env = Env.open_tyvars env p in
2457 (fun (env, te, ty) ->
2458 (Typing_solver.close_tyvars_and_solve env Errors.unify_error, te, ty))
2459 @@ check_call
2460 ~is_using_clause
2461 ~expected
2465 explicit_targs
2467 unpacked_element
2468 | _ -> expr env e
2470 make_result env p (Aast.Suspend te) ty
2471 | New ((pos, c), explicit_targs, el, unpacked_element, p1) ->
2472 let env = might_throw env in
2473 let (env, tc, tal, tel, typed_unpack_element, ty, ctor_fty) =
2474 new_object
2475 ~expected
2476 ~is_using_clause
2477 ~check_parent:false
2478 ~check_not_abstract:true
2482 explicit_targs
2484 unpacked_element
2486 let env = Env.forget_members env Reason.(Blame (p, BScall)) in
2487 make_result
2490 (Aast.New (tc, tal, tel, typed_unpack_element, (p1, ctor_fty)))
2492 | Record ((pos, id), field_values) ->
2493 (match Decl_provider.get_record_def (Env.get_ctx env) id with
2494 | Some rd ->
2495 if rd.rdt_abstract then Errors.new_abstract_record (pos, id);
2497 let field_name (pos, expr_) =
2498 match expr_ with
2499 | Aast.String name -> Some (pos, name)
2500 | _ ->
2501 (* TODO T44306013: Ensure that other values for field names are banned. *)
2502 None
2504 let fields_declared = Typing_helpers.all_record_fields env rd in
2505 let fields_present =
2506 List.map field_values ~f:(fun (name, _value) -> field_name name)
2507 |> List.filter_opt
2509 (* Check for missing required fields. *)
2510 let fields_present_names =
2511 List.map ~f:snd fields_present |> SSet.of_list
2513 SMap.iter
2514 (fun field_name info ->
2515 let ((field_pos, _), req) = info in
2516 match req with
2517 | Typing_defs.ValueRequired
2518 when not (SSet.mem field_name fields_present_names) ->
2519 Errors.missing_record_field_name
2520 ~field_name
2521 ~new_pos:pos
2522 ~record_name:id
2523 ~field_decl_pos:field_pos
2524 | _ -> ())
2525 fields_declared;
2527 (* Check for unknown fields.*)
2528 List.iter fields_present ~f:(fun (pos, field_name) ->
2529 if not (SMap.mem field_name fields_declared) then
2530 Errors.unexpected_record_field_name
2531 ~field_name
2532 ~field_pos:pos
2533 ~record_name:id
2534 ~decl_pos:(fst rd.rdt_name))
2535 | None -> Errors.type_not_record id pos);
2537 expr_error env (Reason.Rwitness p) outer
2538 | Cast (hint, e) ->
2539 let (env, te, ty2) = expr ?in_await env e in
2540 let env = might_throw env in
2541 let env =
2543 TypecheckerOptions.experimental_feature_enabled
2544 (Env.get_tcopt env)
2545 TypecheckerOptions.experimental_forbid_nullable_cast
2546 && not (TUtils.is_mixed env ty2)
2547 then
2548 SubType.sub_type_or_fail
2551 (MakeType.nonnull (get_reason ty2))
2552 (fun () ->
2553 Errors.nullable_cast p (Typing_print.error env ty2) (get_pos ty2))
2554 else
2557 let (env, ty) = Phase.localize_hint_with_self env hint in
2558 make_result env p (Aast.Cast (hint, te)) ty
2559 | ExpressionTree et -> expression_tree env p et
2560 | Is (e, hint) ->
2561 Typing_kinding.Simple.check_well_kinded_hint env hint;
2562 let (env, te, _) = expr env e in
2563 make_result env p (Aast.Is (te, hint)) (MakeType.bool (Reason.Rwitness p))
2564 | As (e, hint, is_nullable) ->
2565 Typing_kinding.Simple.check_well_kinded_hint env hint;
2566 let refine_type env lpos lty rty =
2567 let reason = Reason.Ras lpos in
2568 let (env, rty) = Env.expand_type env rty in
2569 let (env, rty) = class_for_refinement env p reason lpos lty rty in
2570 Inter.intersect env reason lty rty
2572 let (env, te, expr_ty) = expr env e in
2573 let env = might_throw env in
2574 let ety_env =
2575 { (Phase.env_with_self env) with from_class = Some CIstatic }
2577 let (env, hint_ty) = Phase.localize_hint ~ety_env env hint in
2578 let (env, hint_ty) =
2579 if Typing_utils.is_dynamic env hint_ty then
2580 let env =
2581 if is_instance_var e then
2582 let (env, ivar) = get_instance_var env e in
2583 set_local env ivar hint_ty
2584 else
2587 (env, hint_ty)
2588 else if is_nullable then
2589 let (env, hint_ty) = refine_type env (fst e) expr_ty hint_ty in
2590 (env, MakeType.nullable_locl (Reason.Rwitness p) hint_ty)
2591 else if is_instance_var e then
2592 let (env, _, ivar_ty) = raw_expr env e in
2593 let (env, ((ivar_pos, _) as ivar)) = get_instance_var env e in
2594 let (env, hint_ty) = refine_type env ivar_pos ivar_ty hint_ty in
2595 let env = set_local env ivar hint_ty in
2596 (env, hint_ty)
2597 else
2598 refine_type env (fst e) expr_ty hint_ty
2600 make_result env p (Aast.As (te, hint, is_nullable)) hint_ty
2601 | Efun (f, idl)
2602 | Lfun (f, idl) ->
2603 let is_anon =
2604 match e with
2605 | Efun _ -> true
2606 | Lfun _ -> false
2607 | _ -> assert false
2609 (* Check type annotations on the lambda *)
2610 Typing_check_decls.fun_ env f;
2611 (* Check attributes on the lambda *)
2612 let env =
2613 attributes_check_def env SN.AttributeKinds.lambda f.f_user_attributes
2615 (* This is the function type as declared on the lambda itself.
2616 * If type hints are absent then use Tany instead. *)
2617 let declared_fe =
2618 Decl_nast.fun_decl_in_env env.decl_env ~is_lambda:true f
2620 let { fe_type; fe_pos; _ } = declared_fe in
2621 let (declared_pos, declared_ft) =
2622 match get_node fe_type with
2623 | Tfun ft -> (fe_pos, ft)
2624 | _ -> failwith "Not a function"
2626 let declared_ft =
2627 Typing_enforceability.compute_enforced_and_pessimize_fun_type
2629 declared_ft
2631 (* When creating a closure, the 'this' type will mean the late bound type
2632 * of the current enclosing class
2634 let ety_env =
2635 { (Phase.env_with_self env) with from_class = Some CIstatic }
2637 let (env, declared_ft) =
2638 Phase.(
2639 localize_ft
2640 ~instantiation:
2641 { use_name = "lambda"; use_pos = p; explicit_targs = [] }
2642 ~ety_env
2643 ~def_pos:declared_pos
2645 declared_ft)
2647 List.iter idl (check_escaping_var env);
2649 (* Ensure lambda arity is not ellipsis in strict mode *)
2650 begin
2651 match declared_ft.ft_arity with
2652 | Fvariadic { fp_name = None; _ }
2653 when Partial.should_check_error (Env.get_mode env) 4223 ->
2654 Errors.ellipsis_strict_mode ~require:`Param_name p
2655 | _ -> ()
2656 end;
2658 (* Is the return type declared? *)
2659 let is_explicit_ret = Option.is_some (hint_of_type_hint f.f_ret) in
2660 let reactivity =
2661 Decl_fun_utils.fun_reactivity_opt env.decl_env f.f_user_attributes
2662 |> Option.value
2663 ~default:(TR.strip_conditional_reactivity (env_reactivity env))
2665 let check_body_under_known_params env ?ret_ty ft : env * _ * locl_ty =
2666 let old_reactivity = env_reactivity env in
2667 let env = Env.set_env_reactive env reactivity in
2668 let ft = { ft with ft_reactive = reactivity } in
2669 let (env, (tefun, ty, ft)) = anon_make ?ret_ty env p f ft idl is_anon in
2670 let env = Env.set_env_reactive env old_reactivity in
2671 let inferred_ty =
2673 ( Reason.Rwitness p,
2674 Tfun
2676 ft with
2677 ft_ret =
2678 ( if is_explicit_ret then
2679 declared_ft.ft_ret
2680 else
2681 MakeType.unenforced ty );
2684 (env, tefun, inferred_ty)
2686 let (env, eexpected) = expand_expected_and_get_node env expected in
2687 (* TODO: move this into the expand_expected_and_get_node function and prune its callsites
2688 * Strip like type from function type hint *)
2689 let eexpected =
2690 match eexpected with
2691 | Some (pos, ur, _, Tunion [ty1; ty2]) when is_dynamic ty1 && is_fun ty2
2693 Some (pos, ur, ty2, get_node ty2)
2694 | _ -> eexpected
2696 begin
2697 match eexpected with
2698 | Some (_pos, _ur, ty, Tfun expected_ft) ->
2699 (* First check that arities match up *)
2700 check_lambda_arity p (get_pos ty) declared_ft expected_ft;
2701 (* Use declared types for parameters in preference to those determined
2702 * by the context: they might be more general. *)
2703 let rec replace_non_declared_types
2704 params declared_ft_params expected_ft_params =
2705 match (params, declared_ft_params, expected_ft_params) with
2706 | ( param :: params,
2707 declared_ft_param :: declared_ft_params,
2708 expected_ft_param :: expected_ft_params ) ->
2709 let rest =
2710 replace_non_declared_types
2711 params
2712 declared_ft_params
2713 expected_ft_params
2715 let resolved_ft_param =
2716 if Option.is_some (hint_of_type_hint param.param_type_hint) then
2717 declared_ft_param
2718 else
2719 { declared_ft_param with fp_type = expected_ft_param.fp_type }
2721 resolved_ft_param :: rest
2722 | (_ :: params, declared_ft_param :: declared_ft_params, []) ->
2723 let rest =
2724 replace_non_declared_types
2725 params
2726 declared_ft_params
2727 expected_ft_params
2729 declared_ft_param :: rest
2730 | (_, _, _) ->
2731 (* This means the expected_ft params list can have more parameters
2732 * than declared parameters in the lambda. For variadics, this is OK.
2734 expected_ft_params
2736 let replace_non_declared_arity variadic declared_arity expected_arity =
2737 match variadic with
2738 | FVvariadicArg { param_type_hint = (_, Some _); _ } -> declared_arity
2739 | FVvariadicArg _ ->
2740 begin
2741 match (declared_arity, expected_arity) with
2742 | (Fvariadic declared, Fvariadic expected) ->
2743 Fvariadic { declared with fp_type = expected.fp_type }
2744 | (_, _) -> declared_arity
2746 | _ -> declared_arity
2748 let expected_ft =
2750 expected_ft with
2751 ft_arity =
2752 replace_non_declared_arity
2753 f.f_variadic
2754 declared_ft.ft_arity
2755 expected_ft.ft_arity;
2756 ft_params =
2757 replace_non_declared_types
2758 f.f_params
2759 declared_ft.ft_params
2760 expected_ft.ft_params;
2761 ft_implicit_params = declared_ft.ft_implicit_params;
2764 (* Don't bother passing in `void` if there is no explicit return *)
2765 let ret_ty =
2766 match get_node expected_ft.ft_ret.et_type with
2767 | Tprim Tvoid when not is_explicit_ret -> None
2768 | _ -> Some expected_ft.ft_ret.et_type
2770 Typing_log.increment_feature_count env FL.Lambda.contextual_params;
2771 check_body_under_known_params env ?ret_ty expected_ft
2772 | _ ->
2773 let explicit_variadic_param_or_non_variadic =
2774 match f.f_variadic with
2775 | FVvariadicArg { param_type_hint; _ } ->
2776 Option.is_some (hint_of_type_hint param_type_hint)
2777 | FVellipsis _ -> false
2778 | _ -> true
2780 (* If all parameters are annotated with explicit types, then type-check
2781 * the body under those assumptions and pick up the result type *)
2782 let all_explicit_params =
2783 List.for_all f.f_params (fun param ->
2784 Option.is_some (hint_of_type_hint param.param_type_hint))
2786 if all_explicit_params && explicit_variadic_param_or_non_variadic then (
2787 Typing_log.increment_feature_count
2789 ( if List.is_empty f.f_params then
2790 FL.Lambda.no_params
2791 else
2792 FL.Lambda.explicit_params );
2793 check_body_under_known_params env declared_ft
2794 ) else (
2795 match expected with
2796 | Some ExpectedTy.{ ty = { et_type; _ }; _ } when is_any et_type ->
2797 (* If the expected type is Tany env then we're passing a lambda to
2798 * an untyped function and we just assume every parameter has type
2799 * Tany.
2800 * Note: we should be using 'nothing' to type the arguments. *)
2801 Typing_log.increment_feature_count env FL.Lambda.untyped_context;
2802 check_body_under_known_params env declared_ft
2803 | Some _ ->
2804 (* If the expected type is something concrete but not a function
2805 * then we should reject in strict mode. Check body anyway.
2806 * Note: we should be using 'nothing' to type the arguments. *)
2807 if Partial.should_check_error (Env.get_mode env) 4224 then
2808 Errors.untyped_lambda_strict_mode p;
2809 Typing_log.increment_feature_count
2811 FL.Lambda.non_function_typed_context;
2812 check_body_under_known_params env declared_ft
2813 | None ->
2814 (* If we're in partial mode then type-check definition anyway,
2815 * so treating parameters without type hints as "untyped"
2817 if not (Env.is_strict env) then (
2818 Typing_log.increment_feature_count
2820 FL.Lambda.non_strict_unknown_params;
2821 check_body_under_known_params env declared_ft
2822 ) else (
2823 Typing_log.increment_feature_count
2825 FL.Lambda.fresh_tyvar_params;
2827 (* Replace uses of Tany that originated from "untyped" parameters or return type
2828 * with fresh type variables *)
2829 let freshen_ftype env ft =
2830 let freshen_ty env pos et =
2831 match get_node et.et_type with
2832 | Tany _ ->
2833 let (env, ty) = Env.fresh_type env pos in
2834 (env, { et with et_type = ty })
2835 | Tclass (id, e, [ty])
2836 when String.equal (snd id) SN.Classes.cAwaitable
2837 && is_any ty ->
2838 let (env, t) = Env.fresh_type env pos in
2839 ( env,
2841 et with
2842 et_type = mk (get_reason et.et_type, Tclass (id, e, [t]));
2844 | _ -> (env, et)
2846 let freshen_untyped_param env ft_param =
2847 let (env, fp_type) =
2848 freshen_ty env ft_param.fp_pos ft_param.fp_type
2850 (env, { ft_param with fp_type })
2852 let (env, ft_params) =
2853 List.map_env env ft.ft_params freshen_untyped_param
2855 let (env, ft_ret) = freshen_ty env declared_pos ft.ft_ret in
2856 (env, { ft with ft_params; ft_ret })
2858 let (env, declared_ft) = freshen_ftype env declared_ft in
2859 let env =
2860 Env.set_tyvar_variance env (mk (Reason.Rnone, Tfun declared_ft))
2862 (* TODO(jjwu): the declared_ft here is set to public,
2863 but is actually inferred from the surrounding context
2864 (don't think this matters in practice, since we check lambdas separately) *)
2865 check_body_under_known_params
2867 ~ret_ty:declared_ft.ft_ret.et_type
2868 declared_ft
2872 | Xml (sid, attrl, el) ->
2873 let cid = CI sid in
2874 let (env, _tal, _te, classes) =
2875 class_id_for_new ~exact:Nonexact p env cid []
2877 let class_info =
2878 match classes with
2879 | [] -> None
2880 (* OK to ignore rest of list; class_info only used for errors, and
2881 * cid = CI sid cannot produce a union of classes anyhow *)
2882 | (_, class_info, _) :: _ -> Some class_info
2884 let (env, _te, obj) =
2885 expr env (fst sid, New ((fst sid, cid), [], [], None, fst sid))
2887 let (env, typed_attrs, attr_types) =
2888 xhp_attribute_exprs env class_info attrl
2890 let (env, tel) =
2891 List.map_env env el ~f:(fun env e ->
2892 let (env, te, _) = expr env e in
2893 (env, te))
2895 let txml = Aast.Xml (sid, typed_attrs, List.rev tel) in
2896 (match class_info with
2897 | None -> make_result env p txml (mk (Reason.Runknown_class p, Tobject))
2898 | Some class_info ->
2899 let env =
2900 List.fold_left
2901 attr_types
2903 begin
2904 fun env attr ->
2905 let (namepstr, valpty) = attr in
2906 let (valp, valty) = valpty in
2907 let (env, (declty, _tal)) =
2908 TOG.obj_get
2909 ~obj_pos:(fst sid)
2910 ~is_method:false
2911 ~nullsafe:None
2912 ~coerce_from_ty:None
2913 ~explicit_targs:[]
2917 namepstr
2918 Errors.unify_error
2920 let ureason = Reason.URxhp (Cls.name class_info, snd namepstr) in
2921 Typing_coercion.coerce_type
2922 valp
2923 ureason
2925 valty
2926 (MakeType.unenforced declty)
2927 Errors.xhp_attribute_does_not_match_hint
2929 ~init:env
2931 make_result env p txml obj)
2932 | Callconv (kind, e) ->
2933 let (env, te, ty) = expr env e in
2934 make_result env p (Aast.Callconv (kind, te)) ty
2935 | Shape fdm ->
2936 let (env, fdm_with_expected) =
2937 match expand_expected_and_get_node env expected with
2938 | (env, Some (pos, ur, _, Tshape (_, expected_fdm))) ->
2939 let fdme =
2940 List.map
2941 ~f:(fun (k, v) ->
2942 match ShapeMap.find_opt k expected_fdm with
2943 | None -> (k, (v, None))
2944 | Some sft -> (k, (v, Some (ExpectedTy.make pos ur sft.sft_ty))))
2947 (env, fdme)
2948 | _ -> (env, List.map ~f:(fun (k, v) -> (k, (v, None))) fdm)
2950 (* allow_inter adds a type-variable *)
2951 let (env, tfdm) =
2952 List.map_env
2953 ~f:(fun env (key, (e, expected)) ->
2954 let (env, te, ty) = expr ?expected env e in
2955 (env, (key, (te, ty))))
2957 fdm_with_expected
2959 let (env, fdm) =
2960 let convert_expr_and_type_to_shape_field_type env (key, (_, ty)) =
2961 (* An expression evaluation always corresponds to a shape_field_type
2962 with sft_optional = false. *)
2963 (env, (key, { sft_optional = false; sft_ty = ty }))
2965 List.map_env ~f:convert_expr_and_type_to_shape_field_type env tfdm
2967 let fdm =
2968 List.fold_left
2969 ~f:(fun acc (k, v) -> ShapeMap.add k v acc)
2970 ~init:ShapeMap.empty
2973 let env = check_shape_keys_validity env p (ShapeMap.keys fdm) in
2974 (* Fields are fully known, because this shape is constructed
2975 * using shape keyword and we know exactly what fields are set. *)
2976 make_result
2979 (Aast.Shape (List.map ~f:(fun (k, (te, _)) -> (k, te)) tfdm))
2980 (mk (Reason.Rwitness p, Tshape (Closed_shape, fdm)))
2981 | ET_Splice e -> et_splice env p e
2982 | EnumAtom s ->
2983 Errors.atom_as_expr p;
2984 make_result env p (Aast.EnumAtom s) (mk (Reason.Rwitness p, Terr))
2986 (* let ty = err_witness env cst_pos in *)
2987 and class_const ?(incl_tc = false) env p ((cpos, cid), mid) =
2988 let (env, _tal, ce, cty) =
2989 static_class_id ~check_constraints:true cpos env [] cid
2991 let (env, (const_ty, _tal)) =
2992 class_get
2993 ~is_method:false
2994 ~is_const:true
2995 ~incl_tc
2996 ~coerce_from_ty:None
3002 make_result env p (Aast.Class_const (ce, mid)) const_ty
3003 (*****************************************************************************)
3004 (* XHP attribute/body helpers. *)
3005 (*****************************************************************************)
3008 * Process a spread operator by computing the intersection of XHP attributes
3009 * between the spread expression and the XHP constructor onto which we're
3010 * spreading.
3012 and xhp_spread_attribute env c_onto valexpr =
3013 let (p, _) = valexpr in
3014 let (env, te, valty) = expr env valexpr in
3015 (* Build the typed attribute node *)
3016 let typed_attr = Aast.Xhp_spread te in
3017 let (env, attr_ptys) =
3018 match c_onto with
3019 | None -> (env, [])
3020 | Some class_info -> Typing_xhp.get_spread_attributes env p class_info valty
3022 (env, typed_attr, attr_ptys)
3025 * Simple XHP attributes (attr={expr} form) are simply interpreted as a member
3026 * variable prefixed with a colon, the types of which will be validated later
3028 and xhp_simple_attribute env id valexpr =
3029 let (p, _) = valexpr in
3030 let (env, te, valty) = expr env valexpr in
3031 (* This converts the attribute name to a member name. *)
3032 let name = ":" ^ snd id in
3033 let attr_pty = ((fst id, name), (p, valty)) in
3034 let typed_attr = Aast.Xhp_simple (id, te) in
3035 (env, typed_attr, [attr_pty])
3038 * Typecheck the attribute expressions - this just checks that the expressions are
3039 * valid, not that they match the declared type for the attribute and,
3040 * in case of spreads, makes sure they are XHP.
3042 and xhp_attribute_exprs env cid attrl =
3043 let handle_attr (env, typed_attrl, attr_ptyl) attr =
3044 let (env, typed_attr, attr_ptys) =
3045 match attr with
3046 | Xhp_simple (id, valexpr) -> xhp_simple_attribute env id valexpr
3047 | Xhp_spread valexpr -> xhp_spread_attribute env cid valexpr
3049 (env, typed_attr :: typed_attrl, attr_ptys @ attr_ptyl)
3051 let (env, typed_attrl, attr_ptyl) =
3052 List.fold_left ~f:handle_attr ~init:(env, [], []) attrl
3054 (env, List.rev typed_attrl, List.rev attr_ptyl)
3056 (*****************************************************************************)
3057 (* Anonymous functions. *)
3058 (*****************************************************************************)
3059 and anon_bind_param params (env, t_params) ty : env * Tast.fun_param list =
3060 match !params with
3061 | [] ->
3062 (* This code cannot be executed normally, because the arity is wrong
3063 * and it will error later. Bind as many parameters as we can and carry
3064 * on. *)
3065 (env, t_params)
3066 | param :: paraml ->
3067 params := paraml;
3068 (match hint_of_type_hint param.param_type_hint with
3069 | Some h ->
3070 let h = Decl_hint.hint env.decl_env h in
3071 (* When creating a closure, the 'this' type will mean the
3072 * late bound type of the current enclosing class
3074 let ety_env =
3075 { (Phase.env_with_self env) with from_class = Some CIstatic }
3077 let (env, h) = Phase.localize ~ety_env env h in
3078 let pos = get_pos h in
3079 let env =
3080 Typing_coercion.coerce_type
3082 Reason.URparam
3085 (MakeType.unenforced h)
3086 Errors.unify_error
3088 (* Closures are allowed to have explicit type-hints. When
3089 * that is the case we should check that the argument passed
3090 * is compatible with the type-hint.
3091 * The body of the function should be type-checked with the
3092 * hint and not the type of the argument passed.
3093 * Otherwise it leads to strange results where
3094 * foo(?string $x = null) is called with a string and fails to
3095 * type-check. If $x is a string instead of ?string, null is not
3096 * subtype of string ...
3098 let (env, t_param) = bind_param env (h, param) in
3099 (env, t_params @ [t_param])
3100 | None ->
3101 let ty =
3102 mk (Reason.Rlambda_param (param.param_pos, get_reason ty), get_node ty)
3104 let (env, t_param) = bind_param env (ty, param) in
3105 (env, t_params @ [t_param]))
3107 and anon_bind_variadic env vparam variadic_ty =
3108 let (env, ty, pos) =
3109 match hint_of_type_hint vparam.param_type_hint with
3110 | None ->
3111 (* if the hint is missing, use the type we expect *)
3112 (env, variadic_ty, get_pos variadic_ty)
3113 | Some hint ->
3114 let h = Decl_hint.hint env.decl_env hint in
3115 let ety_env =
3116 { (Phase.env_with_self env) with from_class = Some CIstatic }
3118 let (env, h) = Phase.localize ~ety_env env h in
3119 let pos = get_pos h in
3120 let env =
3121 Typing_coercion.coerce_type
3123 Reason.URparam
3125 variadic_ty
3126 (MakeType.unenforced h)
3127 Errors.unify_error
3129 (env, h, vparam.param_pos)
3131 let r = Reason.Rvar_param pos in
3132 let arr_values = mk (r, get_node ty) in
3133 let ty = MakeType.varray r arr_values in
3134 let (env, t_variadic) = bind_param env (ty, vparam) in
3135 (env, t_variadic)
3137 and anon_bind_opt_param env param : env =
3138 match param.param_expr with
3139 | None ->
3140 let ty = Typing_utils.mk_tany env param.param_pos in
3141 let (env, _) = bind_param env (ty, param) in
3143 | Some default ->
3144 let (env, _te, ty) = expr env default in
3145 Typing_sequencing.sequence_check_expr default;
3146 let (env, _) = bind_param env (ty, param) in
3149 and anon_check_param env param =
3150 match hint_of_type_hint param.param_type_hint with
3151 | None -> env
3152 | Some hty ->
3153 let (env, hty) = Phase.localize_hint_with_self env hty in
3154 let paramty = Env.get_local env (Local_id.make_unscoped param.param_name) in
3155 let hint_pos = get_pos hty in
3156 let env =
3157 Typing_coercion.coerce_type
3158 hint_pos
3159 Reason.URhint
3161 paramty
3162 (MakeType.unenforced hty)
3163 Errors.unify_error
3167 and stash_conts_for_anon env p is_anon captured f =
3168 let captured =
3169 if Env.is_local_defined env this then
3170 (Pos.none, this) :: captured
3171 else
3172 captured
3174 let init =
3175 Option.map (Env.next_cont_opt env) ~f:(fun next_cont ->
3176 let initial_locals =
3177 if is_anon then
3178 Env.get_locals env captured
3179 else
3180 next_cont.Typing_per_cont_env.local_types
3182 let initial_fakes =
3183 Fake.forget (Env.get_fake_members env) Reason.(Blame (p, BSlambda))
3185 let tpenv = Env.get_tpenv env in
3186 (initial_locals, initial_fakes, tpenv))
3188 Typing_lenv.stash_and_do env (Env.all_continuations env) (fun env ->
3189 let env =
3190 match init with
3191 | None -> env
3192 | Some (initial_locals, initial_fakes, tpenv) ->
3193 let env = Env.reinitialize_locals env in
3194 let env = Env.set_locals env initial_locals in
3195 let env = Env.set_fake_members env initial_fakes in
3196 let env = Env.env_with_tpenv env tpenv in
3199 f env)
3201 (* Make a type-checking function for an anonymous function. *)
3202 (* Here ret_ty should include Awaitable wrapper *)
3203 (* TODO: ?el is never set; so we need to fix variadic use of lambda *)
3204 and anon_make ?el ?ret_ty env lambda_pos f ft idl is_anon =
3205 let nb = Nast.assert_named_body f.f_body in
3206 Env.anon env.lenv env (fun env ->
3207 (* Extract capabilities from AAST and add them to the environment *)
3208 let (env, capability) =
3209 match (hint_of_type_hint f.f_cap, hint_of_type_hint f.f_unsafe_cap) with
3210 | (None, None) ->
3211 (* if the closure has no explicit coeffect annotations,
3212 do _not_ insert (unsafe) capabilities into the environment;
3213 instead, rely on the fact that a capability from an enclosing
3214 scope can simply be captured, which has the same semantics
3215 as redeclaring and shadowing with another same-typed capability.
3216 This avoid unnecessary overhead in the most common case, i.e.,
3217 when a closure does not need a different (usually smaller)
3218 set of capabilities. *)
3219 (env, Env.get_local env Typing_coeffects.local_capability_id)
3220 | (_, _) ->
3221 let (env, f_cap, f_unsafe_cap) =
3222 type_capability env f.f_cap f.f_unsafe_cap (fst f.f_name)
3224 Typing_coeffects.register_capabilities
3226 (type_of_type_hint f_cap)
3227 (type_of_type_hint f_unsafe_cap)
3229 let ft =
3230 { ft with ft_implicit_params = { capability = CapTy capability } }
3232 stash_conts_for_anon env lambda_pos is_anon idl (fun env ->
3233 let env = Env.clear_params env in
3234 let make_variadic_arg env varg tyl =
3235 let remaining_types =
3236 (* It's possible the variadic arg will capture the variadic
3237 * parameter of the supplied arity (if arity is Fvariadic)
3238 * and additional supplied params.
3240 * For example in cases such as:
3241 * lambda1 = (int $a, string...$c) ==> {};
3242 * lambda1(1, "hello", ...$y); (where $y is a variadic string)
3243 * lambda1(1, "hello", "world");
3244 * then ...$c will contain "hello" and everything in $y in the first
3245 * example, and "hello" and "world" in the second example.
3247 * To account for a mismatch in arity, we take the remaining supplied
3248 * parameters and return a list of all their types. We'll use this
3249 * to create a union type when creating the typed variadic arg.
3251 let remaining_params =
3252 List.drop ft.ft_params (List.length f.f_params)
3254 List.map ~f:(fun param -> param.fp_type.et_type) remaining_params
3256 let r = Reason.Rvar_param varg.param_pos in
3257 let union = Tunion (tyl @ remaining_types) in
3258 let (env, t_param) = anon_bind_variadic env varg (mk (r, union)) in
3259 (env, Aast.FVvariadicArg t_param)
3261 let (env, t_variadic) =
3262 match (f.f_variadic, ft.ft_arity) with
3263 | (FVvariadicArg arg, Fvariadic variadic) ->
3264 make_variadic_arg env arg [variadic.fp_type.et_type]
3265 | (FVvariadicArg arg, Fstandard) -> make_variadic_arg env arg []
3266 | (FVellipsis pos, _) -> (env, Aast.FVellipsis pos)
3267 | (_, _) -> (env, Aast.FVnonVariadic)
3269 let params = ref f.f_params in
3270 let (env, t_params) =
3271 List.fold_left
3272 ~f:(anon_bind_param params)
3273 ~init:(env, [])
3274 (List.map ft.ft_params (fun x -> x.fp_type.et_type))
3276 let env = List.fold_left ~f:anon_bind_opt_param ~init:env !params in
3277 let env = List.fold_left ~f:anon_check_param ~init:env f.f_params in
3278 let env =
3279 match el with
3280 | None ->
3281 (*iter2_shortest
3282 Unify.unify_param_modes
3283 ft.ft_params
3284 supplied_params; *)
3286 | Some x ->
3287 let var_param =
3288 match f.f_variadic with
3289 | FVellipsis pos ->
3290 let param =
3291 TUtils.default_fun_param
3292 ~pos
3293 (mk (Reason.Rvar_param pos, Typing_defs.make_tany ()))
3295 Some param
3296 | _ -> None
3298 let rec iter l1 l2 =
3299 match (l1, l2, var_param) with
3300 | (_, [], _) -> ()
3301 | ([], _, None) -> ()
3302 | ([], x2 :: rl2, Some def1) ->
3303 param_modes ~is_variadic:true def1 x2;
3304 iter [] rl2
3305 | (x1 :: rl1, x2 :: rl2, _) ->
3306 param_modes x1 x2;
3307 iter rl1 rl2
3309 iter ft.ft_params x;
3310 wfold_left2 inout_write_back env ft.ft_params x
3312 let env = Env.set_fn_kind env f.f_fun_kind in
3313 let decl_ty =
3314 Option.map
3315 ~f:(Decl_hint.hint env.decl_env)
3316 (hint_of_type_hint f.f_ret)
3318 let (env, hret) =
3319 match decl_ty with
3320 | None ->
3321 (* Do we have a contextual return type? *)
3322 begin
3323 match ret_ty with
3324 | None ->
3325 let (env, ret_ty) = Env.fresh_type env lambda_pos in
3326 (env, Typing_return.wrap_awaitable env lambda_pos ret_ty)
3327 | Some ret_ty ->
3328 (* We might need to force it to be Awaitable if it is a type variable *)
3329 Typing_return.force_awaitable env lambda_pos ret_ty
3331 | Some ret ->
3332 (* If a 'this' type appears it needs to be compatible with the
3333 * late static type
3335 let ety_env =
3336 { (Phase.env_with_self env) with from_class = Some CIstatic }
3338 Typing_return.make_return_type (Phase.localize ~ety_env) env ret
3340 let env =
3341 Env.set_return
3343 (Typing_return.make_info
3344 f.f_fun_kind
3347 ~is_explicit:(Option.is_some ret_ty)
3348 hret
3349 decl_ty)
3351 let local_tpenv = Env.get_tpenv env in
3352 (* Outer pipe variables aren't available in closures. Note that
3353 * locals are restored by Env.anon after processing the closure
3355 let env =
3356 Env.unset_local
3358 (Local_id.make_unscoped SN.SpecialIdents.dollardollar)
3360 let (env, tb) = block env nb.fb_ast in
3361 let implicit_return = LEnv.has_next env in
3362 let env =
3363 if (not implicit_return) || Nast.named_body_is_unsafe nb then
3365 else
3366 fun_implicit_return env lambda_pos hret f.f_fun_kind
3368 let (env, tparams) = List.map_env env f.f_tparams type_param in
3369 let (env, user_attributes) =
3370 List.map_env env f.f_user_attributes user_attribute
3372 let (env, f_cap, f_unsafe_cap) =
3373 type_capability env f.f_cap f.f_unsafe_cap (fst f.f_name)
3375 let tfun_ =
3377 Aast.f_annotation = Env.save local_tpenv env;
3378 Aast.f_span = f.f_span;
3379 Aast.f_mode = f.f_mode;
3380 Aast.f_ret = (hret, hint_of_type_hint f.f_ret);
3381 Aast.f_name = f.f_name;
3382 Aast.f_tparams = tparams;
3383 Aast.f_where_constraints = f.f_where_constraints;
3384 Aast.f_fun_kind = f.f_fun_kind;
3385 Aast.f_file_attributes = [];
3386 Aast.f_user_attributes = user_attributes;
3387 Aast.f_body = { Aast.fb_ast = tb; fb_annotation = () };
3388 Aast.f_cap;
3389 Aast.f_unsafe_cap;
3390 Aast.f_params = t_params;
3391 Aast.f_variadic = t_variadic;
3392 (* TODO TAST: Variadic efuns *)
3393 Aast.f_external = f.f_external;
3394 Aast.f_namespace = f.f_namespace;
3395 Aast.f_doc_comment = f.f_doc_comment;
3396 Aast.f_static = f.f_static;
3399 let ty = mk (Reason.Rwitness lambda_pos, Tfun ft) in
3400 let te =
3401 Tast.make_typed_expr
3402 lambda_pos
3404 ( if is_anon then
3405 Aast.Efun (tfun_, idl)
3406 else
3407 Aast.Lfun (tfun_, idl) )
3409 let env = Env.set_tyvar_variance env ty in
3410 (env, (te, hret, ft))
3411 (* stash_conts_for_anon *))
3412 (* Env.anon *))
3414 (*****************************************************************************)
3415 (* End of anonymous functions. *)
3416 (*****************************************************************************)
3418 (*****************************************************************************)
3419 (* Expression trees *)
3420 (*****************************************************************************)
3421 and expression_tree env p et =
3422 let (_, t_src_expr, _) = expr_any env p et.et_src_expr in
3423 let (env, t_desugared_expr, ty_desugared) = expr env et.et_desugared_expr in
3424 make_result
3427 (Aast.ExpressionTree
3429 et_hint = et.et_hint;
3430 et_src_expr = t_src_expr;
3431 et_desugared_expr = t_desugared_expr;
3433 ty_desugared
3435 and et_splice env p e =
3436 let (env, te, ty) = expr env e in
3437 let (env, ty_visitor) = Env.fresh_type env p in
3438 let (env, ty_res) = Env.fresh_type env p in
3439 let (env, ty_infer) = Env.fresh_type env p in
3440 let expr_tree_type =
3441 MakeType.expr_tree (Reason.Rsplice p) ty_visitor ty_res ty_infer
3443 let env = SubType.sub_type env ty expr_tree_type Errors.unify_error in
3444 make_result env p (Aast.ET_Splice te) ty_infer
3446 (*****************************************************************************)
3447 (* End expression trees *)
3448 (*****************************************************************************)
3450 (* Goes from Nast.f_cap to Tast.f_cap (same for unsafe_cap) *)
3451 and type_capability env cap unsafe_cap default_pos =
3452 let cap_hint_opt = hint_of_type_hint cap in
3453 let (env, cap_ty) =
3454 Option.value_map
3455 cap_hint_opt
3456 ~default:(MakeType.default_capability (Reason.Rhint default_pos))
3457 ~f:(Decl_hint.hint env.decl_env)
3458 |> Phase.localize_with_self env
3460 let unsafe_cap_hint_opt = hint_of_type_hint unsafe_cap in
3461 let (env, unsafe_cap_ty) =
3462 Option.value_map
3463 unsafe_cap_hint_opt (* default is no unsafe capabilities *)
3464 ~default:(env, MakeType.mixed (Reason.Rhint default_pos))
3465 ~f:(Phase.localize_hint_with_self env)
3467 (env, (cap_ty, cap_hint_opt), (unsafe_cap_ty, unsafe_cap_hint_opt))
3469 and requires_consistent_construct = function
3470 | CIstatic -> true
3471 | CIexpr _ -> true
3472 | CIparent -> false
3473 | CIself -> false
3474 | CI _ -> false
3476 (* Caller will be looking for a particular form of expected type
3477 * e.g. a function type (when checking lambdas) or tuple type (when checking
3478 * tuples). First expand the expected type and elide single union; also
3479 * strip nullables, so ?t becomes t, as context will always accept a t if a ?t
3480 * is expected.
3482 and expand_expected_and_get_node env (expected : ExpectedTy.t option) =
3483 match expected with
3484 | None -> (env, None)
3485 | Some ExpectedTy.{ pos = p; reason = ur; ty = { et_type = ty; _ }; _ } ->
3486 let (env, ty) = Env.expand_type env ty in
3487 (match get_node ty with
3488 | Tunion [ty] -> (env, Some (p, ur, ty, get_node ty))
3489 | Toption ty -> (env, Some (p, ur, ty, get_node ty))
3490 | _ -> (env, Some (p, ur, ty, get_node ty)))
3492 (** Do a subtype check of inferred type against expected type *)
3493 and check_expected_ty message env inferred_ty (expected : ExpectedTy.t option) =
3494 match expected with
3495 | None -> env
3496 | Some ExpectedTy.{ pos = p; reason = ur; ty } ->
3497 Typing_log.(
3498 log_with_level env "typing" 1 (fun () ->
3499 log_types
3503 Log_head
3504 ( Printf.sprintf
3505 "Typing.check_expected_ty %s enforced=%b"
3506 message
3507 ty.et_enforced,
3509 Log_type ("inferred_ty", inferred_ty);
3510 Log_type ("expected_ty", ty.et_type);
3511 ] );
3512 ]));
3513 Typing_coercion.coerce_type p ur env inferred_ty ty Errors.unify_error
3515 and new_object
3516 ~(expected : ExpectedTy.t option)
3517 ~check_parent
3518 ~check_not_abstract
3519 ~is_using_clause
3523 explicit_targs
3525 unpacked_element =
3526 (* Obtain class info from the cid expression. We get multiple
3527 * results with a CIexpr that has a union type, e.g. in
3529 $classname = (mycond()? classname<A>: classname<B>);
3530 new $classname();
3532 let (env, tal, tcid, classes) =
3533 instantiable_cid ~exact:Exact p env cid explicit_targs
3535 let allow_abstract_bound_generic =
3536 match tcid with
3537 | ((_, ty), Aast.CI (_, tn)) -> is_generic_equal_to tn ty
3538 | _ -> false
3540 let gather (env, _tel, _typed_unpack_element) (cname, class_info, c_ty) =
3542 check_not_abstract
3543 && Cls.abstract class_info
3544 && (not (requires_consistent_construct cid))
3545 && not allow_abstract_bound_generic
3546 then
3547 uninstantiable_error
3551 (Cls.pos class_info)
3552 (Cls.name class_info)
3554 c_ty;
3555 let (env, obj_ty_, params) =
3556 let (env, c_ty) = Env.expand_type env c_ty in
3557 match (cid, tal, get_class_type c_ty) with
3558 (* Explicit type arguments *)
3559 | (CI _, _ :: _, Some (_, _, tyl)) -> (env, get_node c_ty, tyl)
3560 | (_, _, class_type_opt) ->
3561 let (env, params) =
3562 List.map_env env (Cls.tparams class_info) (fun env tparam ->
3563 let (env, tvar) =
3564 Env.fresh_type_reason
3566 (Reason.Rtype_variable_generics
3567 (p, snd tparam.tp_name, strip_ns (snd cname)))
3569 Typing_log.log_new_tvar_for_new_object env p tvar cname tparam;
3570 (env, tvar))
3572 begin
3573 match class_type_opt with
3574 | Some (_, Exact, _) -> (env, Tclass (cname, Exact, params), params)
3575 | _ -> (env, Tclass (cname, Nonexact, params), params)
3579 (not check_parent)
3580 && (not is_using_clause)
3581 && Cls.is_disposable class_info
3582 then
3583 Errors.invalid_new_disposable p;
3584 let r_witness = Reason.Rwitness p in
3585 let obj_ty = mk (r_witness, obj_ty_) in
3586 let c_ty =
3587 match cid with
3588 | CIstatic
3589 | CIexpr _ ->
3590 mk (r_witness, get_node c_ty)
3591 | _ -> obj_ty
3593 let (env, new_ty) =
3594 let ((_, cid_ty), _) = tcid in
3595 let (env, cid_ty) = Env.expand_type env cid_ty in
3596 if is_generic cid_ty then
3597 (env, cid_ty)
3598 else if check_parent then
3599 (env, c_ty)
3600 else
3601 ExprDepTy.make env cid c_ty
3603 (* Set variance according to type of `new` expression now. Lambda arguments
3604 * to the constructor might depend on it, and `call_construct` only uses
3605 * `ctor_fty` to set the variance which has void return type *)
3606 let env = Env.set_tyvar_variance env new_ty in
3607 let (env, tel, typed_unpack_element, ctor_fty) =
3608 let env = check_expected_ty "New" env new_ty expected in
3609 call_construct p env class_info params el unpacked_element cid new_ty
3611 ( if equal_consistent_kind (snd (Cls.construct class_info)) Inconsistent then
3612 match cid with
3613 | CIstatic -> Errors.new_inconsistent_construct p cname `static
3614 | CIexpr _ -> Errors.new_inconsistent_construct p cname `classname
3615 | _ -> () );
3616 match cid with
3617 | CIparent ->
3618 let (env, ctor_fty) =
3619 match fst (Cls.construct class_info) with
3620 | Some ({ ce_type = (lazy ty); _ } as ce) ->
3621 let ety_env =
3623 type_expansions = [];
3624 substs =
3625 TUtils.make_locl_subst_for_class_tparams class_info params;
3626 this_ty = obj_ty;
3627 from_class = None;
3628 quiet = false;
3629 on_error = Errors.unify_error_at p;
3632 if get_ce_abstract ce then
3633 Errors.parent_abstract_call
3634 SN.Members.__construct
3636 (get_pos ctor_fty);
3637 let (env, ctor_fty) = Phase.localize ~ety_env env ty in
3638 (env, ctor_fty)
3639 | None -> (env, ctor_fty)
3641 ((env, tel, typed_unpack_element), (obj_ty, ctor_fty))
3642 | CIstatic
3643 | CI _
3644 | CIself ->
3645 ((env, tel, typed_unpack_element), (c_ty, ctor_fty))
3646 | CIexpr _ ->
3647 (* When constructing from a (classname) variable, the variable
3648 * dictates what the constructed object is going to be. This allows
3649 * for generic and dependent types to be correctly carried
3650 * through the 'new $foo()' iff the constructed obj_ty is a
3651 * supertype of the variable-dictated c_ty *)
3652 let env =
3653 Typing_ops.sub_type p Reason.URnone env c_ty obj_ty Errors.unify_error
3655 ((env, tel, typed_unpack_element), (c_ty, ctor_fty))
3657 let ((env, tel, typed_unpack_element), class_types_and_ctor_types) =
3658 List.fold_map classes ~init:(env, [], None) ~f:gather
3660 let (env, tel, typed_unpack_element, ty, ctor_fty) =
3661 match class_types_and_ctor_types with
3662 | [] ->
3663 let (env, tel, _) = exprs env el in
3664 let (env, typed_unpack_element, _) =
3665 match unpacked_element with
3666 | None -> (env, None, MakeType.nothing Reason.Rnone)
3667 | Some unpacked_element ->
3668 let (env, e, ty) = expr env unpacked_element in
3669 (env, Some e, ty)
3671 let r = Reason.Runknown_class p in
3672 (env, tel, typed_unpack_element, mk (r, Tobject), TUtils.terr env r)
3673 | [(ty, ctor_fty)] -> (env, tel, typed_unpack_element, ty, ctor_fty)
3674 | l ->
3675 let (tyl, ctyl) = List.unzip l in
3676 let r = Reason.Rwitness p in
3677 (env, tel, typed_unpack_element, mk (r, Tunion tyl), mk (r, Tunion ctyl))
3679 let (env, new_ty) =
3680 let ((_, cid_ty), _) = tcid in
3681 let (env, cid_ty) = Env.expand_type env cid_ty in
3682 if is_generic cid_ty then
3683 (env, cid_ty)
3684 else if check_parent then
3685 (env, ty)
3686 else
3687 ExprDepTy.make env cid ty
3689 (env, tcid, tal, tel, typed_unpack_element, new_ty, ctor_fty)
3691 and attributes_check_def env kind attrs =
3692 Typing_attributes.check_def env new_object kind attrs
3694 (** Get class infos for a class expression (e.g. `parent`, `self` or
3695 regular classnames) - which might resolve to a union or intersection
3696 of classes - and check they are instantiable.
3698 FIXME: we need to separate our instantiability into two parts. Currently,
3699 all this function is doing is checking if a given type is inhabited --
3700 that is, whether there are runtime values of type Aast. However,
3701 instantiability should be the stricter notion that T has a runtime
3702 constructor; that is, `new T()` should be valid. In particular, interfaces
3703 are inhabited, but not instantiable.
3704 To make this work with classname, we likely need to add something like
3705 concrete_classname<T>, where T cannot be an interface. *)
3706 and instantiable_cid ?(exact = Nonexact) p env cid explicit_targs =
3707 let (env, tal, te, classes) =
3708 class_id_for_new ~exact p env cid explicit_targs
3710 List.iter classes (fun ((pos, name), class_info, c_ty) ->
3712 Ast_defs.(equal_class_kind (Cls.kind class_info) Ctrait)
3713 || Ast_defs.(equal_class_kind (Cls.kind class_info) Cenum)
3714 then
3715 match cid with
3716 | CIexpr _
3717 | CI _ ->
3718 uninstantiable_error env p cid (Cls.pos class_info) name pos c_ty
3719 | CIstatic
3720 | CIparent
3721 | CIself ->
3723 else if
3724 Ast_defs.(equal_class_kind (Cls.kind class_info) Cabstract)
3725 && Cls.final class_info
3726 then
3727 uninstantiable_error env p cid (Cls.pos class_info) name pos c_ty
3728 else
3729 ());
3730 (env, tal, te, classes)
3732 and uninstantiable_error env reason_pos cid c_tc_pos c_name c_usage_pos c_ty =
3733 let reason_msgl =
3734 match cid with
3735 | CIexpr _ ->
3736 let ty_str = "This would be " ^ Typing_print.error env c_ty in
3737 [(reason_pos, ty_str)]
3738 | _ -> []
3740 Errors.uninstantiable_class c_usage_pos c_tc_pos c_name reason_msgl
3742 and coerce_to_throwable pos env exn_ty =
3743 let throwable_ty = MakeType.throwable (Reason.Rthrow pos) in
3744 Typing_coercion.coerce_type
3746 Reason.URthrow
3748 exn_ty
3749 { et_type = throwable_ty; et_enforced = false }
3750 Errors.unify_error
3752 and shape_field_pos = function
3753 | Ast_defs.SFlit_int (p, _)
3754 | Ast_defs.SFlit_str (p, _) ->
3756 | Ast_defs.SFclass_const ((cls_pos, _), (mem_pos, _)) ->
3757 Pos.btw cls_pos mem_pos
3759 and check_shape_keys_validity env pos keys =
3760 (* If the key is a class constant, get its class name and type. *)
3761 let get_field_info env key =
3762 let key_pos = shape_field_pos key in
3763 (* Empty strings or literals that start with numbers are not
3764 permitted as shape field names. *)
3765 match key with
3766 | Ast_defs.SFlit_int _ -> (env, key_pos, None)
3767 | Ast_defs.SFlit_str (_, key_name) ->
3768 if Int.equal 0 (String.length key_name) then
3769 Errors.invalid_shape_field_name_empty key_pos;
3770 (env, key_pos, None)
3771 | Ast_defs.SFclass_const (((p, cls) as x), y) ->
3772 let (env, _te, ty) = class_const env pos ((p, CI x), y) in
3773 let r = Reason.Rwitness key_pos in
3774 let env =
3775 Type.sub_type
3776 key_pos
3777 Reason.URnone
3780 (MakeType.arraykey r)
3781 (fun ?code:_ _ ->
3782 Errors.invalid_shape_field_type
3783 key_pos
3784 (get_pos ty)
3785 (Typing_print.error env ty)
3788 (env, key_pos, Some (cls, ty))
3790 let check_field witness_pos witness_info env key =
3791 let (env, key_pos, key_info) = get_field_info env key in
3792 match (witness_info, key_info) with
3793 | (Some _, None) ->
3794 Errors.invalid_shape_field_literal key_pos witness_pos;
3796 | (None, Some _) ->
3797 Errors.invalid_shape_field_const key_pos witness_pos;
3799 | (None, None) -> env
3800 | (Some (cls1, ty1), Some (cls2, ty2)) ->
3801 if String.( <> ) cls1 cls2 then
3802 Errors.shape_field_class_mismatch
3803 key_pos
3804 witness_pos
3805 (strip_ns cls2)
3806 (strip_ns cls1);
3809 ( Typing_solver.is_sub_type env ty1 ty2
3810 && Typing_solver.is_sub_type env ty2 ty1 )
3811 then
3812 Errors.shape_field_type_mismatch
3813 key_pos
3814 witness_pos
3815 (Typing_print.error env ty2)
3816 (Typing_print.error env ty1);
3819 (* Sort the keys by their positions since the error messages will make
3820 * more sense if we take the one that appears first as canonical and if
3821 * they are processed in source order. *)
3822 let cmp_keys x y = Pos.compare (shape_field_pos x) (shape_field_pos y) in
3823 let keys = List.sort ~compare:cmp_keys keys in
3824 match keys with
3825 | [] -> env
3826 | witness :: rest_keys ->
3827 let (env, pos, info) = get_field_info env witness in
3828 List.fold_left ~f:(check_field pos info) ~init:env rest_keys
3830 and set_valid_rvalue p env x ty =
3831 let env = set_local env (p, x) ty in
3832 (* We are assigning a new value to the local variable, so we need to
3833 * generate a new expression id
3835 Env.set_local_expr_id env x (Ident.tmp ())
3837 (* Produce an error if assignment is used in the scope of the |> operator, i.e.
3838 * if $$ is in scope *)
3839 and error_if_assign_in_pipe p env =
3840 let dd_var = Local_id.make_unscoped SN.SpecialIdents.dollardollar in
3841 let dd_defined = Env.is_local_defined env dd_var in
3842 if dd_defined then
3843 Errors.unimplemented_feature p "Assignment within pipe expressions"
3845 (* Deal with assignment of a value of type ty2 to lvalue e1 *)
3846 and assign p env e1 ty2 : _ * Tast.expr * Tast.ty =
3847 error_if_assign_in_pipe p env;
3848 assign_ p Reason.URassign env e1 ty2
3850 and is_hack_collection env ty =
3851 Typing_solver.is_sub_type
3854 (MakeType.const_collection Reason.Rnone (MakeType.mixed Reason.Rnone))
3856 and assign_ p ur env e1 ty2 =
3857 let env =
3858 match e1 with
3859 | (_, Lvar (_, x)) ->
3860 Env.forget_prefixed_members env x Reason.(Blame (p, BSassignment))
3861 (* If we ever extend fake members from $x->a to more complicated lvalues
3862 such as $x->a->b, we would need to call forget_prefixed_members on
3863 other lvalues as well. *)
3864 | (_, Obj_get (_, (_, Id (_, property)), _, _)) ->
3865 Env.forget_suffixed_members env property Reason.(Blame (p, BSassignment))
3866 | _ -> env
3868 match e1 with
3869 | (_, Lvar ((_, x) as id)) ->
3870 let env = set_valid_rvalue p env x ty2 in
3871 make_result env (fst e1) (Aast.Lvar id) ty2
3872 | (_, Lplaceholder id) ->
3873 let placeholder_ty = MakeType.void (Reason.Rplaceholder p) in
3874 make_result env (fst e1) (Aast.Lplaceholder id) placeholder_ty
3875 | (_, List el) ->
3876 let (env, tyl) =
3877 List.map_env env el ~f:(fun env _ -> Env.fresh_type env (get_pos ty2))
3879 let destructure_ty =
3880 MakeType.list_destructure (Reason.Rdestructure (fst e1)) tyl
3882 let lty2 = LoclType ty2 in
3883 let env = Type.sub_type_i p ur env lty2 destructure_ty Errors.unify_error in
3884 let env = Env.set_tyvar_variance_i env destructure_ty in
3885 let (env, reversed_tel) =
3886 List.fold2_exn el tyl ~init:(env, []) ~f:(fun (env, tel) lvalue ty2 ->
3887 let (env, te, _) = assign p env lvalue ty2 in
3888 (env, te :: tel))
3890 make_result env (fst e1) (Aast.List (List.rev reversed_tel)) ty2
3891 | ( pobj,
3892 Obj_get (obj, (pm, Id ((_, member_name) as m)), nullflavor, in_parens) )
3894 let lenv = env.lenv in
3895 let nullsafe =
3896 match nullflavor with
3897 | OG_nullthrows -> None
3898 | OG_nullsafe -> Some (Reason.Rnullsafe_op pobj)
3900 let (env, tobj, obj_ty) = expr ~accept_using_var:true env obj in
3901 let env = might_throw env in
3902 let (env, (result, _tal)) =
3903 TOG.obj_get
3904 ~obj_pos:(fst obj)
3905 ~is_method:false
3906 ~nullsafe
3907 ~coerce_from_ty:(Some (p, ur, ty2))
3908 ~explicit_targs:[]
3910 obj_ty
3911 (CIexpr e1)
3913 Errors.unify_error
3915 let te1 =
3916 Tast.make_typed_expr
3917 pobj
3918 result
3919 (Aast.Obj_get
3920 ( tobj,
3921 Tast.make_typed_expr pm result (Aast.Id m),
3922 nullflavor,
3923 in_parens ))
3925 let env = { env with lenv } in
3926 begin
3927 match obj with
3928 | (_, This) ->
3929 let (env, local) = Env.FakeMembers.make env obj member_name p in
3930 let env = set_valid_rvalue p env local ty2 in
3931 (env, te1, ty2)
3932 | (_, Lvar _) ->
3933 let (env, local) = Env.FakeMembers.make env obj member_name p in
3934 let env = set_valid_rvalue p env local ty2 in
3935 (env, te1, ty2)
3936 | _ -> (env, te1, ty2)
3938 | (_, Obj_get _) ->
3939 let lenv = env.lenv in
3940 let no_fakes = LEnv.env_with_empty_fakes env in
3941 let (env, te1, real_type) = lvalue no_fakes e1 in
3942 let (env, exp_real_type) = Env.expand_type env real_type in
3943 let env = { env with lenv } in
3944 let env =
3945 Typing_coercion.coerce_type
3950 (MakeType.unenforced exp_real_type)
3951 Errors.unify_error
3953 (env, te1, ty2)
3954 | (_, Class_get (_, CGexpr _, _)) ->
3955 failwith "AST should not have any CGexprs after naming"
3956 | (_, Class_get ((pos_classid, x), CGstring (pos_member, y), _)) ->
3957 let lenv = env.lenv in
3958 let no_fakes = LEnv.env_with_empty_fakes env in
3959 let (env, te1, _) = lvalue no_fakes e1 in
3960 let env = { env with lenv } in
3961 let (env, ety2) = Env.expand_type env ty2 in
3962 (* This defers the coercion check to class_get, which looks up the appropriate target type *)
3963 let (env, _tal, _, cty) =
3964 static_class_id ~check_constraints:false pos_classid env [] x
3966 let env = might_throw env in
3967 let (env, _) =
3968 class_get
3969 ~is_method:false
3970 ~is_const:false
3971 ~coerce_from_ty:(Some (p, ur, ety2))
3974 (pos_member, y)
3977 let (env, local) = Env.FakeMembers.make_static env x y p in
3978 let env = set_valid_rvalue p env local ty2 in
3979 (env, te1, ty2)
3980 | (pos, Array_get (e1, None)) ->
3981 let (env, te1, ty1) = update_array_type pos env e1 `lvalue in
3982 let (env, ty1') =
3983 Typing_array_access.assign_array_append
3984 ~array_pos:(fst e1)
3985 ~expr_pos:p
3991 let (env, te1) =
3992 if is_hack_collection env ty1 then
3993 (env, te1)
3994 else
3995 let (env, te1, _) = assign_ p ur env e1 ty1' in
3996 (env, te1)
3998 make_result env pos (Aast.Array_get (te1, None)) ty2
3999 | (pos, Array_get (e1, Some e)) ->
4000 let (env, te1, ty1) = update_array_type pos env e1 `lvalue in
4001 let (env, te, ty) = expr env e in
4002 let (env, ty1') =
4003 Typing_array_access.assign_array_get
4004 ~array_pos:(fst e1)
4005 ~expr_pos:p
4013 let (env, te1) =
4014 if is_hack_collection env ty1 then
4015 (env, te1)
4016 else
4017 let (env, te1, _) = assign_ p ur env e1 ty1' in
4018 (env, te1)
4020 (env, ((pos, ty2), Aast.Array_get (te1, Some te)), ty2)
4021 | _ -> assign_simple p ur env e1 ty2
4023 and assign_simple pos ur env e1 ty2 =
4024 let (env, te1, ty1) = lvalue env e1 in
4025 let env =
4026 Typing_coercion.coerce_type
4031 (MakeType.unenforced ty1)
4032 Errors.unify_error
4034 (env, te1, ty2)
4036 and array_field env = function
4037 | AFvalue ve ->
4038 let (env, tve, tv) = expr env ve in
4039 (env, (Aast.AFvalue tve, None, tv))
4040 | AFkvalue (ke, ve) ->
4041 let (env, tke, tk) = expr env ke in
4042 let (env, tve, tv) = expr env ve in
4043 (env, (Aast.AFkvalue (tke, tve), Some tk, tv))
4045 and array_value ~(expected : ExpectedTy.t option) env x =
4046 let (env, te, ty) = expr ?expected env x in
4047 (env, (te, ty))
4049 and arraykey_value
4050 p class_name ~(expected : ExpectedTy.t option) env ((pos, _) as x) =
4051 let (env, (te, ty)) = array_value ~expected env x in
4052 let ty_arraykey = MakeType.arraykey (Reason.Ridx_dict pos) in
4053 let env =
4054 Typing_coercion.coerce_type
4056 (Reason.index_class class_name)
4059 { et_type = ty_arraykey; et_enforced = true }
4060 Errors.unify_error
4062 (env, (te, ty))
4064 and check_parent_construct pos env el unpacked_element env_parent =
4065 let check_not_abstract = false in
4066 let (env, env_parent) = Phase.localize_with_self env env_parent in
4067 let (env, _tcid, _tal, tel, typed_unpack_element, parent, fty) =
4068 new_object
4069 ~expected:None
4070 ~check_parent:true
4071 ~check_not_abstract
4072 ~is_using_clause:false
4075 CIparent
4078 unpacked_element
4080 (* Not sure why we need to equate these types *)
4081 let env =
4082 Type.sub_type pos Reason.URnone env env_parent parent Errors.unify_error
4084 let env =
4085 Type.sub_type pos Reason.URnone env parent env_parent Errors.unify_error
4087 ( env,
4088 tel,
4089 typed_unpack_element,
4090 MakeType.void (Reason.Rwitness pos),
4091 parent,
4092 fty )
4094 and check_class_get env p def_pos cid mid ce e function_pointer =
4095 match e with
4096 | CIself when get_ce_abstract ce ->
4097 begin
4098 match get_class_type (Env.get_self env) with
4099 | Some ((_, self), _, _) ->
4100 (* at runtime, self:: in a trait is a call to whatever
4101 * self:: is in the context of the non-trait "use"-ing
4102 * the trait's code *)
4103 begin
4104 match Env.get_class env self with
4105 | Some cls when Ast_defs.(equal_class_kind (Cls.kind cls) Ctrait) ->
4106 (* Ban self::some_abstract_method() in a trait, if the
4107 * method is also defined in a trait.
4109 * Abstract methods from interfaces are fine: we'll check
4110 * in the child class that we actually have an
4111 * implementation. *)
4112 (match Decl_provider.get_class (Env.get_ctx env) ce.ce_origin with
4113 | Some meth_cls
4114 when Ast_defs.(equal_class_kind (Cls.kind meth_cls) Ctrait) ->
4115 Errors.self_abstract_call mid p def_pos
4116 | _ -> ())
4117 | _ ->
4118 (* Ban self::some_abstract_method() in a class. This will
4119 * always error. *)
4120 Errors.self_abstract_call mid p def_pos
4122 | None -> ()
4124 | CIparent when get_ce_abstract ce ->
4125 Errors.parent_abstract_call mid p def_pos
4126 | CI _ when get_ce_abstract ce && function_pointer ->
4127 Errors.abstract_function_pointer cid mid p def_pos
4128 | CI _ when get_ce_abstract ce ->
4129 Errors.classname_abstract_call cid mid p def_pos
4130 | CI (_, classname) when get_ce_synthesized ce ->
4131 Errors.static_synthetic_method classname mid p def_pos
4132 | _ -> ()
4134 and call_parent_construct pos env el unpacked_element =
4135 match Env.get_parent_ty env with
4136 | Some parent -> check_parent_construct pos env el unpacked_element parent
4137 | None ->
4138 (* continue here *)
4139 let ty = Typing_utils.mk_tany env pos in
4140 let default = (env, [], None, ty, ty, ty) in
4141 (match get_class_type (Env.get_self env) with
4142 | Some ((_, self), _, _) ->
4143 (match Env.get_class env self with
4144 | Some trait when Ast_defs.(equal_class_kind (Cls.kind trait) Ctrait) ->
4145 (match trait_most_concrete_req_class trait env with
4146 | None ->
4147 Errors.parent_in_trait pos;
4148 default
4149 | Some (_, parent_ty) ->
4150 check_parent_construct pos env el unpacked_element parent_ty)
4151 | Some self_tc ->
4152 if not (Cls.members_fully_known self_tc) then
4154 (* Don't know the hierarchy, assume it's correct *)
4155 else
4156 Errors.undefined_parent pos;
4157 default
4158 | None -> assert false)
4159 | None ->
4160 Errors.parent_outside_class pos;
4161 let ty = err_witness env pos in
4162 (env, [], None, ty, ty, ty))
4164 (* Depending on the kind of expression we are dealing with
4165 * The typing of call is different.
4167 and dispatch_call
4168 ~(expected : ExpectedTy.t option)
4169 ~is_using_clause
4170 ?in_await
4173 ((fpos, fun_expr) as e)
4174 explicit_targs
4176 unpacked_element =
4177 let make_call env te tal tel typed_unpack_element ty =
4178 make_result env p (Aast.Call (te, tal, tel, typed_unpack_element)) ty
4180 (* TODO: Avoid Tany annotations in TAST by eliminating `make_call_special` *)
4181 let make_call_special env id tel ty =
4182 make_call
4184 (Tast.make_typed_expr fpos (TUtils.mk_tany env fpos) (Aast.Id id))
4187 None
4190 (* For special functions and pseudofunctions with a definition in hhi. *)
4191 let make_call_special_from_def env id tel ty_ =
4192 let (env, fty, tal) = fun_type_of_id env id explicit_targs el in
4193 let ty =
4194 match get_node fty with
4195 | Tfun ft -> ft.ft_ret.et_type
4196 | _ -> ty_ (Reason.Rwitness p)
4198 make_call env (Tast.make_typed_expr fpos fty (Aast.Id id)) tal tel None ty
4200 let overload_function = overload_function make_call fpos in
4201 let check_disposable_in_return env fty =
4202 if is_return_disposable_fun_type env fty && not is_using_clause then
4203 Errors.invalid_new_disposable p
4205 match fun_expr with
4206 (* Special function `echo` *)
4207 | Id ((p, pseudo_func) as id)
4208 when String.equal pseudo_func SN.SpecialFunctions.echo ->
4209 let (env, tel, _) = exprs ~accept_using_var:true env el in
4210 make_call_special env id tel (MakeType.void (Reason.Rwitness p))
4211 (* Special function `isset` *)
4212 | Id ((_, pseudo_func) as id)
4213 when String.equal pseudo_func SN.PseudoFunctions.isset ->
4214 let (env, tel, _) =
4215 exprs ~accept_using_var:true ~check_defined:false env el
4217 if Option.is_some unpacked_element then
4218 Errors.unpacking_disallowed_builtin_function p pseudo_func;
4219 make_call_special_from_def env id tel MakeType.bool
4220 (* Special function `unset` *)
4221 | Id ((_, pseudo_func) as id)
4222 when String.equal pseudo_func SN.PseudoFunctions.unset ->
4223 let (env, tel, _) = exprs env el in
4224 if Option.is_some unpacked_element then
4225 Errors.unpacking_disallowed_builtin_function p pseudo_func;
4226 let checked_unset_error =
4227 if Partial.should_check_error (Env.get_mode env) 4135 then
4228 Errors.unset_nonidx_in_strict
4229 else
4230 fun _ _ ->
4233 let env =
4234 match (el, unpacked_element) with
4235 | ([(_, Array_get ((_, Class_const _), Some _))], None)
4236 when Partial.should_check_error (Env.get_mode env) 4011 ->
4237 Errors.const_mutation p Pos.none "";
4239 | ([(_, Array_get (ea, Some _))], None) ->
4240 let (env, _te, ty) = expr env ea in
4241 let r = Reason.Rwitness p in
4242 let tmixed = MakeType.mixed r in
4243 let super =
4245 ( Reason.Rnone,
4246 Tunion
4248 MakeType.dynamic r;
4249 MakeType.dict r tmixed tmixed;
4250 MakeType.keyset r tmixed;
4251 MakeType.darray r tmixed tmixed;
4254 SubType.sub_type_or_fail env ty super (fun () ->
4255 checked_unset_error
4257 (Reason.to_string
4258 ("This is " ^ Typing_print.error ~ignore_dynamic:true env ty)
4259 (get_reason ty)))
4260 | _ ->
4261 checked_unset_error p [];
4264 (match el with
4265 | [(p, Obj_get (_, _, OG_nullsafe, _))] ->
4266 Errors.nullsafe_property_write_context p;
4267 make_call_special_from_def env id tel (TUtils.terr env)
4268 | _ -> make_call_special_from_def env id tel MakeType.void)
4269 (* Special function `array_filter` *)
4270 | Id ((_, array_filter) as id)
4271 when String.equal array_filter SN.StdlibFunctions.array_filter
4272 && (not (List.is_empty el))
4273 && Option.is_none unpacked_element ->
4274 (* dispatch the call to typecheck the arguments *)
4275 let (env, fty, tal) = fun_type_of_id env id explicit_targs el in
4276 let (env, (tel, typed_unpack_element, res)) =
4277 call ~expected p env fty el unpacked_element
4279 (* but ignore the result and overwrite it with custom return type *)
4280 let x = List.hd_exn el in
4281 let (env, _tx, ty) = expr env x in
4282 let explain_array_filter ty =
4283 map_reason ty ~f:(fun r -> Reason.Rarray_filter (p, r))
4285 let get_value_type env tv =
4286 let (env, tv) =
4287 if List.length el > 1 then
4288 (env, tv)
4289 else
4290 Typing_solver.non_null env p tv
4292 (env, explain_array_filter tv)
4294 let rec get_array_filter_return_type env ty =
4295 let (env, ety) = Env.expand_type env ty in
4296 match deref ety with
4297 | (r, Tvarray tv) ->
4298 let (env, tv) = get_value_type env tv in
4299 (env, MakeType.varray r tv)
4300 | (r, Tunion tyl) ->
4301 let (env, tyl) = List.map_env env tyl get_array_filter_return_type in
4302 Typing_union.union_list env r tyl
4303 | (r, Tintersection tyl) ->
4304 let (env, tyl) = List.map_env env tyl get_array_filter_return_type in
4305 Inter.intersect_list env r tyl
4306 | (r, Tany _) -> (env, mk (r, Typing_utils.tany env))
4307 | (r, Terr) -> (env, TUtils.terr env r)
4308 | (r, _) ->
4309 let (env, tk) = Env.fresh_type env p in
4310 let (env, tv) = Env.fresh_type env p in
4311 Errors.try_
4312 (fun () ->
4313 let keyed_container_type =
4314 MakeType.keyed_container Reason.Rnone tk tv
4316 let env =
4317 SubType.sub_type env ety keyed_container_type Errors.unify_error
4319 let (env, tv) = get_value_type env tv in
4320 (env, MakeType.darray r (explain_array_filter tk) tv))
4321 (fun _ ->
4322 Errors.try_
4323 (fun () ->
4324 let container_type = MakeType.container Reason.Rnone tv in
4325 let env =
4326 SubType.sub_type env ety container_type Errors.unify_error
4328 let (env, tv) = get_value_type env tv in
4329 ( env,
4330 MakeType.darray
4332 (explain_array_filter (MakeType.arraykey r))
4333 tv ))
4334 (fun _ -> (env, res)))
4336 let (env, rty) = get_array_filter_return_type env ty in
4337 let fty =
4338 map_ty fty ~f:(function
4339 | Tfun ft -> Tfun { ft with ft_ret = MakeType.unenforced rty }
4340 | ty -> ty)
4342 make_call
4344 (Tast.make_typed_expr fpos fty (Aast.Id id))
4347 typed_unpack_element
4349 (* Special function `type_structure` *)
4350 | Id (p, type_structure)
4351 when String.equal type_structure SN.StdlibFunctions.type_structure
4352 && Int.equal (List.length el) 2
4353 && Option.is_none unpacked_element ->
4354 (match el with
4355 | [e1; e2] ->
4356 (match e2 with
4357 | (p, String cst) ->
4358 (* find the class constant implicitly defined by the typeconst *)
4359 let cid =
4360 match e1 with
4361 | (_, Class_const (cid, (_, x)))
4362 | (_, Class_get (cid, CGstring (_, x), _))
4363 when String.equal x SN.Members.mClass ->
4365 | _ -> (fst e1, CIexpr e1)
4367 class_const ~incl_tc:true env p (cid, (p, cst))
4368 | _ ->
4369 Errors.illegal_type_structure p "second argument is not a string";
4370 expr_error env (Reason.Rwitness p) e)
4371 | _ -> assert false)
4372 (* Special function `array_map` *)
4373 | Id ((_, array_map) as x)
4374 when String.equal array_map SN.StdlibFunctions.array_map
4375 && (not (List.is_empty el))
4376 && Option.is_none unpacked_element ->
4377 (* This uses the arity to determine a signature for array_map. But there
4378 * is more: for two-argument use of array_map, we specialize the return
4379 * type to the collection that's passed in, below. *)
4380 let (env, fty, tal) = fun_type_of_id env x explicit_targs el in
4381 let (env, fty) = Env.expand_type env fty in
4382 let r_fty = get_reason fty in
4384 Takes a Container type and returns a function that can "pack" a type
4385 into an array of appropriate shape, preserving the key type, i.e.:
4386 array -> f, where f R = array
4387 array<X> -> f, where f R = array<R>
4388 array<X, Y> -> f, where f R = array<X, R>
4389 Vector<X> -> f where f R = array<R>
4390 KeyedContainer<X, Y> -> f, where f R = array<X, R>
4391 Container<X> -> f, where f R = array<arraykey, R>
4392 X -> f, where f R = Y
4394 let rec build_output_container env (x : locl_ty) :
4395 env * (env -> locl_ty -> env * locl_ty) =
4396 let (env, x) = Env.expand_type env x in
4397 match deref x with
4398 | (r, Tvarray _) -> (env, (fun env tr -> (env, MakeType.varray r tr)))
4399 | (r, Tany _) -> (env, (fun env _ -> (env, mk (r, Typing_utils.tany env))))
4400 | (r, Terr) -> (env, (fun env _ -> (env, TUtils.terr env r)))
4401 | (r, Tunion tyl) ->
4402 let (env, builders) = List.map_env env tyl build_output_container in
4403 ( env,
4404 fun env tr ->
4405 let (env, tyl) =
4406 List.map_env env builders (fun env f -> f env tr)
4408 Typing_union.union_list env r tyl )
4409 | (r, Tintersection tyl) ->
4410 let (env, builders) = List.map_env env tyl build_output_container in
4411 ( env,
4412 fun env tr ->
4413 let (env, tyl) =
4414 List.map_env env builders (fun env f -> f env tr)
4416 Typing_intersection.intersect_list env r tyl )
4417 | (r, _) ->
4418 let (env, tk) = Env.fresh_type env p in
4419 let (env, tv) = Env.fresh_type env p in
4420 let try_vector env =
4421 let vector_type = MakeType.const_vector r_fty tv in
4422 let env = SubType.sub_type env x vector_type Errors.unify_error in
4423 (env, (fun env tr -> (env, MakeType.varray r tr)))
4425 let try_keyed_container env =
4426 let keyed_container_type = MakeType.keyed_container r_fty tk tv in
4427 let env =
4428 SubType.sub_type env x keyed_container_type Errors.unify_error
4430 (env, (fun env tr -> (env, MakeType.darray r tk tr)))
4432 let try_container env =
4433 let container_type = MakeType.container r_fty tv in
4434 let env = SubType.sub_type env x container_type Errors.unify_error in
4435 ( env,
4436 (fun env tr -> (env, MakeType.darray r (MakeType.arraykey r) tr)) )
4438 let (env, tr) =
4439 Errors.try_
4440 (fun () -> try_vector env)
4441 (fun _ ->
4442 Errors.try_
4443 (fun () -> try_keyed_container env)
4444 (fun _ ->
4445 Errors.try_
4446 (fun () -> try_container env)
4447 (fun _ ->
4448 (env, (fun env _ -> (env, Typing_utils.mk_tany env p))))))
4450 (env, tr)
4452 let (env, fty) =
4453 match (deref fty, el) with
4454 | ((_, Tfun funty), [_; x]) ->
4455 let (env, _tx, x) = expr env x in
4456 let (env, output_container) = build_output_container env x in
4457 begin
4458 match get_varray_inst funty.ft_ret.et_type with
4459 | None -> (env, fty)
4460 | Some elem_ty ->
4461 let (env, elem_ty) = output_container env elem_ty in
4462 let ft_ret = MakeType.unenforced elem_ty in
4463 (env, mk (r_fty, Tfun { funty with ft_ret }))
4465 | _ -> (env, fty)
4467 let (env, (tel, typed_unpack_element, ty)) =
4468 call ~expected p env fty el None
4470 make_call
4472 (Tast.make_typed_expr fpos fty (Aast.Id x))
4475 typed_unpack_element
4477 (* Special function `Shapes::idx` *)
4478 | Class_const (((_, CI (_, shapes)) as class_id), ((_, idx) as method_id))
4479 when String.equal shapes SN.Shapes.cShapes && String.equal idx SN.Shapes.idx
4481 overload_function
4484 class_id
4485 method_id
4487 unpacked_element
4488 (fun env fty res el ->
4489 match el with
4490 | [shape; field] ->
4491 let (env, _ts, shape_ty) = expr env shape in
4492 Typing_shapes.idx
4494 shape_ty
4495 field
4496 None
4497 ~expr_pos:p
4498 ~fun_pos:(get_reason fty)
4499 ~shape_pos:(fst shape)
4500 | [shape; field; default] ->
4501 let (env, _ts, shape_ty) = expr env shape in
4502 let (env, _td, default_ty) = expr env default in
4503 Typing_shapes.idx
4505 shape_ty
4506 field
4507 (Some (fst default, default_ty))
4508 ~expr_pos:p
4509 ~fun_pos:(get_reason fty)
4510 ~shape_pos:(fst shape)
4511 | _ -> (env, res))
4512 (* Special function `Shapes::at` *)
4513 | Class_const (((_, CI (_, shapes)) as class_id), ((_, at) as method_id))
4514 when String.equal shapes SN.Shapes.cShapes && String.equal at SN.Shapes.at
4516 overload_function
4519 class_id
4520 method_id
4522 unpacked_element
4523 (fun env _fty res el ->
4524 match el with
4525 | [shape; field] ->
4526 let (env, _te, shape_ty) = expr env shape in
4527 Typing_shapes.at env ~expr_pos:p ~shape_pos:(fst shape) shape_ty field
4528 | _ -> (env, res))
4529 (* Special function `Shapes::keyExists` *)
4530 | Class_const
4531 (((_, CI (_, shapes)) as class_id), ((_, key_exists) as method_id))
4532 when String.equal shapes SN.Shapes.cShapes
4533 && String.equal key_exists SN.Shapes.keyExists ->
4534 overload_function
4537 class_id
4538 method_id
4540 unpacked_element
4541 (fun env fty res el ->
4542 match el with
4543 | [shape; field] ->
4544 let (env, _te, shape_ty) = expr env shape in
4545 (* try accessing the field, to verify existence, but ignore
4546 * the returned type and keep the one coming from function
4547 * return type hint *)
4548 let (env, _) =
4549 Typing_shapes.idx
4551 shape_ty
4552 field
4553 None
4554 ~expr_pos:p
4555 ~fun_pos:(get_reason fty)
4556 ~shape_pos:(fst shape)
4558 (env, res)
4559 | _ -> (env, res))
4560 (* Special function `Shapes::removeKey` *)
4561 | Class_const
4562 (((_, CI (_, shapes)) as class_id), ((_, remove_key) as method_id))
4563 when String.equal shapes SN.Shapes.cShapes
4564 && String.equal remove_key SN.Shapes.removeKey ->
4565 overload_function
4568 class_id
4569 method_id
4571 unpacked_element
4572 (fun env _ res el ->
4573 match el with
4574 | [shape; field] ->
4575 begin
4576 match shape with
4577 | (_, Lvar (_, lvar))
4578 | (_, Callconv (Ast_defs.Pinout, (_, Lvar (_, lvar)))) ->
4579 let (env, _te, shape_ty) = expr env shape in
4580 let (env, shape_ty) =
4581 Typing_shapes.remove_key p env shape_ty field
4583 let env = set_valid_rvalue p env lvar shape_ty in
4584 (env, res)
4585 | _ ->
4586 Errors.invalid_shape_remove_key (fst shape);
4587 (env, res)
4589 | _ -> (env, res))
4590 (* Special function `Shapes::toArray` *)
4591 | Class_const (((_, CI (_, shapes)) as class_id), ((_, to_array) as method_id))
4592 when String.equal shapes SN.Shapes.cShapes
4593 && String.equal to_array SN.Shapes.toArray ->
4594 overload_function
4597 class_id
4598 method_id
4600 unpacked_element
4601 (fun env _ res el ->
4602 match el with
4603 | [shape] ->
4604 let (env, _te, shape_ty) = expr env shape in
4605 Typing_shapes.to_array env p shape_ty res
4606 | _ -> (env, res))
4607 (* Special function `Shapes::toDict` *)
4608 | Class_const (((_, CI (_, shapes)) as class_id), ((_, to_array) as method_id))
4609 when String.equal shapes SN.Shapes.cShapes
4610 && String.equal to_array SN.Shapes.toDict ->
4611 overload_function
4614 class_id
4615 method_id
4617 unpacked_element
4618 (fun env _ res el ->
4619 match el with
4620 | [shape] ->
4621 let (env, _te, shape_ty) = expr env shape in
4622 Typing_shapes.to_dict env p shape_ty res
4623 | _ -> (env, res))
4624 (* Special function `parent::__construct` *)
4625 | Class_const ((pos, CIparent), ((_, construct) as id))
4626 when String.equal construct SN.Members.__construct ->
4627 let (env, tel, typed_unpack_element, ty, pty, ctor_fty) =
4628 call_parent_construct p env el unpacked_element
4630 make_call
4632 (Tast.make_typed_expr
4633 fpos
4634 ctor_fty
4635 (Aast.Class_const (((pos, pty), Aast.CIparent), id)))
4636 [] (* tal: no type arguments to constructor *)
4638 typed_unpack_element
4640 (* Calling parent / class method *)
4641 | Class_const ((pos, e1), m) ->
4642 let (env, _tal, tcid, ty1) =
4643 static_class_id
4644 ~check_constraints:(not (Nast.equal_class_id_ e1 CIparent))
4650 let this_ty =
4651 mk (Reason.Rwitness fpos, TUtils.this_of (Env.get_self env))
4653 (* In static context, you can only call parent::foo() on static methods.
4654 * In instance context, you can call parent:foo() on static
4655 * methods as well as instance methods
4657 let is_static =
4658 (not (Nast.equal_class_id_ e1 CIparent))
4659 || Env.is_static env
4660 || class_contains_smethod env ty1 m
4662 let (env, (fty, tal)) =
4663 if is_static then
4664 class_get
4665 ~coerce_from_ty:None
4666 ~is_method:true
4667 ~is_const:false
4668 ~explicit_targs
4673 else
4674 (* parent::nonStaticFunc() is really weird. It's calling a method
4675 * defined on the parent class, but $this is still the child class.
4676 * We can deal with this by hijacking the continuation that
4677 * calculates the SN.Typehints.this type *)
4678 let k_lhs _ = this_ty in
4679 TOG.obj_get_
4680 ~inst_meth:false
4681 ~is_method:true
4682 ~nullsafe:None
4683 ~obj_pos:pos
4684 ~coerce_from_ty:None
4685 ~is_nonnull:false
4686 ~explicit_targs:[]
4691 k_lhs
4692 Errors.unify_error
4694 check_disposable_in_return env fty;
4695 let ty =
4696 if Nast.equal_class_id_ e1 CIparent then
4697 this_ty
4698 else
4701 let (env, (tel, typed_unpack_element, ty)) =
4702 call
4703 ~expected
4704 ~method_call_info:
4705 (TR.make_call_info
4706 ~receiver_is_self:(Nast.equal_class_id_ e1 CIself)
4707 ~is_static
4709 (snd m))
4714 unpacked_element
4716 make_call
4718 (Tast.make_typed_expr fpos fty (Aast.Class_const (tcid, m)))
4721 typed_unpack_element
4723 (* Call instance method *)
4724 | Obj_get (e1, (pos_id, Id m), nullflavor, false)
4725 when not (TypecheckerOptions.method_call_inference (Env.get_tcopt env)) ->
4726 let (env, te1, ty1) = expr ~accept_using_var:true env e1 in
4727 let nullsafe =
4728 match nullflavor with
4729 | OG_nullthrows -> None
4730 | OG_nullsafe -> Some p
4732 let (env, (tfty, tal)) =
4733 TOG.obj_get
4734 ~obj_pos:(fst e1)
4735 ~is_method:true
4736 ~nullsafe:(Option.map ~f:(fun p -> Reason.Rnullsafe_op p) nullsafe)
4737 ~coerce_from_ty:None
4738 ~explicit_targs
4741 (CIexpr e1)
4743 Errors.unify_error
4745 check_disposable_in_return env tfty;
4746 let (env, (tel, typed_unpack_element, ty)) =
4747 call
4748 ~nullsafe
4749 ~expected
4750 ~method_call_info:
4751 (TR.make_call_info
4752 ~receiver_is_self:false
4753 ~is_static:false
4755 (snd m))
4758 tfty
4760 unpacked_element
4762 make_call
4764 (Tast.make_typed_expr
4765 fpos
4766 tfty
4767 (Aast.Obj_get
4768 ( te1,
4769 Tast.make_typed_expr pos_id tfty (Aast.Id m),
4770 nullflavor,
4771 false )))
4774 typed_unpack_element
4776 (* Call instance method using new method call inference *)
4777 | Obj_get (receiver, (pos_id, Id meth), nullflavor, false) ->
4778 (*****
4779 Typecheck `Obj_get` by enforcing that:
4780 - `<instance_type>` <: `Thas_member(m, #1)`
4781 where #1 is a fresh type variable.
4782 *****)
4783 let (env, typed_receiver, receiver_ty) =
4784 expr ~accept_using_var:true env receiver
4786 let env = might_throw env in
4787 let nullsafe =
4788 match nullflavor with
4789 | OG_nullthrows -> None
4790 | OG_nullsafe -> Some p
4792 (* Generate a fresh type `method_ty` for the type of the
4793 instance method, i.e. #1 *)
4794 let (env, method_ty) = Env.fresh_type env p in
4795 (* Create `Thas_member` constraint type *)
4796 let reason = Reason.Rwitness (fst receiver) in
4797 let has_method_ty =
4798 MakeType.has_member
4799 reason
4800 ~name:meth
4801 ~ty:method_ty
4802 ~class_id:(CIexpr receiver)
4803 ~explicit_targs:(Some explicit_targs)
4805 let env = Env.set_tyvar_variance env method_ty in
4806 let (env, has_method_super_ty) =
4807 if Option.is_none nullsafe then
4808 (env, has_method_ty)
4809 else
4810 (* If null-safe, then `receiver_ty` <: `?Thas_member(m, #1)`,
4811 but *unlike* property access typing in `expr_`, we still use `#1` as
4812 our "result" if `receiver_ty` is nullable (as opposed to `?#1`),
4813 deferring null-safety handling to after `call` *)
4814 let r = Reason.Rnullsafe_op p in
4815 let null_ty = MakeType.null r in
4816 Union.union_i env r has_method_ty null_ty
4818 let env =
4819 Type.sub_type_i
4820 (fst receiver)
4821 Reason.URnone
4823 (LoclType receiver_ty)
4824 has_method_super_ty
4825 Errors.unify_error
4827 (* Perhaps solve for `method_ty`. Opening and closing a scope is too coarse
4828 here - type parameters are localised to fresh type variables over the
4829 course of subtyping above, and we do not want to solve these until later.
4830 Once we typecheck all function calls with a subtyping of function types,
4831 we should not need to solve early at all - transitive closure of
4832 subtyping should give enough information. *)
4833 let env =
4834 match get_var method_ty with
4835 | Some var ->
4836 Typing_solver.solve_to_equal_bound_or_wrt_variance
4838 Reason.Rnone
4840 Errors.unify_error
4841 | None -> env
4843 let localize_targ env (_, targ) = Phase.localize_targ env targ in
4844 let (env, typed_targs) =
4845 List.map_env env ~f:(localize_targ ~check_well_kinded:true) explicit_targs
4847 check_disposable_in_return env method_ty;
4848 let (env, (typed_params, typed_unpack_element, ret_ty)) =
4849 call
4850 ~nullsafe
4851 ~expected
4852 ~method_call_info:
4853 (TR.make_call_info
4854 ~receiver_is_self:false
4855 ~is_static:false
4856 receiver_ty
4857 (snd meth))
4858 ?in_await
4861 method_ty
4863 unpacked_element
4865 (* If the call is nullsafe AND the receiver is nullable,
4866 make the return type nullable too *)
4867 let (env, ret_ty) =
4868 if Option.is_some nullsafe then
4869 let r = Reason.Rnullsafe_op p in
4870 let null_ty = MakeType.null r in
4871 let (env, null_or_nothing_ty) =
4872 Inter.intersect env ~r null_ty receiver_ty
4874 let (env, ret_option_ty) = Union.union env null_or_nothing_ty ret_ty in
4875 (env, ret_option_ty)
4876 else
4877 (env, ret_ty)
4879 make_call
4881 (Tast.make_typed_expr
4882 fpos
4883 method_ty
4884 (Aast.Obj_get
4885 ( typed_receiver,
4886 Tast.make_typed_expr pos_id method_ty (Aast.Id meth),
4887 nullflavor,
4888 false )))
4889 typed_targs
4890 typed_params
4891 typed_unpack_element
4892 ret_ty
4893 (* Function invocation *)
4894 | Fun_id x ->
4895 let (env, fty, tal) = fun_type_of_id env x explicit_targs el in
4896 check_disposable_in_return env fty;
4897 let (env, (tel, typed_unpack_element, ty)) =
4898 call ~expected p env fty el unpacked_element
4900 make_call
4902 (Tast.make_typed_expr fpos fty (Aast.Fun_id x))
4905 typed_unpack_element
4907 | Id ((_, id) as x) ->
4908 let (env, fty, tal) = fun_type_of_id env x explicit_targs el in
4909 check_disposable_in_return env fty;
4910 let (env, (tel, typed_unpack_element, ty)) =
4911 call ~expected p env fty el unpacked_element
4913 let is_mutable = String.equal id SN.Rx.mutable_ in
4914 let is_move = String.equal id SN.Rx.move in
4915 let is_freeze = String.equal id SN.Rx.freeze in
4916 (* error when rx builtins are used in non-reactive context *)
4917 if not (Env.env_local_reactive env) then
4918 if is_mutable then
4919 Errors.mutable_in_nonreactive_context p
4920 else if is_move then
4921 Errors.move_in_nonreactive_context p
4922 else if is_freeze then
4923 Errors.freeze_in_nonreactive_context p;
4925 (* ban unpacking when calling builtings *)
4926 if (is_mutable || is_move || is_freeze) && Option.is_some unpacked_element
4927 then
4928 Errors.unpacking_disallowed_builtin_function p id;
4930 (* adjust env for Rx\freeze or Rx\move calls *)
4931 let env =
4932 if is_freeze then
4933 Typing_mutability.freeze_local p env tel
4934 else if is_move then
4935 Typing_mutability.move_local p env tel
4936 else
4939 make_call
4941 (Tast.make_typed_expr fpos fty (Aast.Id x))
4944 typed_unpack_element
4946 | _ ->
4947 let (env, te, fty) = expr env e in
4948 let (env, fty) =
4949 Typing_solver.expand_type_and_solve
4950 ~description_of_expected:"a function value"
4952 fpos
4954 Errors.unify_error
4956 check_disposable_in_return env fty;
4957 let (env, (tel, typed_unpack_element, ty)) =
4958 call ~expected p env fty el unpacked_element
4960 make_call
4963 (* tal: no type arguments to function values, as they are non-generic *)
4966 typed_unpack_element
4969 and fun_type_of_id env x tal el =
4970 match Env.get_fun env (snd x) with
4971 | None ->
4972 let (env, _, ty) = unbound_name env x (Pos.none, Aast.Null) in
4973 (env, ty, [])
4974 | Some { fe_type; fe_pos; fe_deprecated; _ } ->
4975 (match get_node fe_type with
4976 | Tfun ft ->
4977 let ft =
4978 Typing_special_fun.transform_special_fun_ty ft x (List.length el)
4980 let ety_env = Phase.env_with_self env in
4981 let (env, tal) =
4982 Phase.localize_targs
4983 ~check_well_kinded:true
4984 ~is_method:true
4985 ~def_pos:fe_pos
4986 ~use_pos:(fst x)
4987 ~use_name:(strip_ns (snd x))
4989 ft.ft_tparams
4990 (List.map ~f:snd tal)
4992 let ft =
4993 Typing_enforceability.compute_enforced_and_pessimize_fun_type env ft
4995 let use_pos = fst x in
4996 let def_pos = fe_pos in
4997 let (env, ft) =
4998 Phase.(
4999 localize_ft
5000 ~instantiation:
5001 { use_name = strip_ns (snd x); use_pos; explicit_targs = tal }
5002 ~def_pos
5003 ~ety_env
5007 let fty = mk (get_reason fe_type, Tfun ft) in
5008 TVis.check_deprecated ~use_pos ~def_pos fe_deprecated;
5009 (env, fty, tal)
5010 | _ -> failwith "Expected function type")
5013 * Checks if a class (given by cty) contains a given static method.
5015 * We could refactor this + class_get
5017 and class_contains_smethod env cty (_pos, mid) =
5018 let lookup_member ty =
5019 match get_class_type ty with
5020 | Some ((_, c), _, _) ->
5021 (match Env.get_class env c with
5022 | None -> false
5023 | Some class_ ->
5024 Option.is_some @@ Env.get_static_member true env class_ mid)
5025 | None -> false
5027 let (_env, tyl) = TUtils.get_concrete_supertypes env cty in
5028 List.exists tyl ~f:lookup_member
5030 and class_get
5031 ~is_method
5032 ~is_const
5033 ~coerce_from_ty
5034 ?(explicit_targs = [])
5035 ?(incl_tc = false)
5036 ?(function_pointer = false)
5039 (p, mid)
5040 cid =
5041 let (env, this_ty) =
5042 if is_method then
5043 this_for_method env cid cty
5044 else
5045 (env, cty)
5047 class_get_
5048 ~is_method
5049 ~is_const
5050 ~this_ty
5051 ~explicit_targs
5052 ~incl_tc
5053 ~coerce_from_ty
5054 ~function_pointer
5058 (p, mid)
5060 and class_get_
5061 ~is_method
5062 ~is_const
5063 ~this_ty
5064 ~coerce_from_ty
5065 ?(explicit_targs = [])
5066 ?(incl_tc = false)
5067 ?(function_pointer = false)
5071 (p, mid) =
5072 let (env, cty) = Env.expand_type env cty in
5073 match deref cty with
5074 | (r, Tany _) -> (env, (mk (r, Typing_utils.tany env), []))
5075 | (r, Terr) -> (env, (err_witness env (Reason.to_pos r), []))
5076 | (_, Tdynamic) -> (env, (cty, []))
5077 | (_, Tunion tyl) ->
5078 let (env, pairs) =
5079 List.map_env env tyl (fun env ty ->
5080 class_get
5081 ~is_method
5082 ~is_const
5083 ~explicit_targs
5084 ~incl_tc
5085 ~coerce_from_ty
5088 (p, mid)
5089 cid)
5091 let (env, ty) =
5092 Union.union_list env (get_reason cty) (List.map ~f:fst pairs)
5094 (env, (ty, []))
5095 | (_, Tintersection tyl) ->
5096 let (env, pairs) =
5097 TUtils.run_on_intersection env tyl ~f:(fun env ty ->
5098 class_get
5099 ~is_method
5100 ~is_const
5101 ~explicit_targs
5102 ~incl_tc
5103 ~coerce_from_ty
5106 (p, mid)
5107 cid)
5109 let (env, ty) =
5110 Inter.intersect_list env (get_reason cty) (List.map ~f:fst pairs)
5112 (env, (ty, []))
5113 | (_, Tnewtype (_, _, ty))
5114 | (_, Tdependent (_, ty)) ->
5115 class_get_
5116 ~is_method
5117 ~is_const
5118 ~this_ty
5119 ~explicit_targs
5120 ~incl_tc
5121 ~coerce_from_ty
5125 (p, mid)
5126 | (r, Tgeneric _) ->
5127 let (env, tyl) = TUtils.get_concrete_supertypes env cty in
5128 if List.is_empty tyl then begin
5129 Errors.non_class_member
5130 ~is_method
5133 (Typing_print.error env cty)
5134 (get_pos cty);
5135 (env, (err_witness env p, []))
5136 end else
5137 let (env, ty) = Typing_intersection.intersect_list env r tyl in
5138 class_get_
5139 ~is_method
5140 ~is_const
5141 ~this_ty
5142 ~explicit_targs
5143 ~incl_tc
5144 ~coerce_from_ty
5148 (p, mid)
5149 | (_, Tclass ((_, c), _, paraml)) ->
5150 let class_ = Env.get_class env c in
5151 (match class_ with
5152 | None -> (env, (Typing_utils.mk_tany env p, []))
5153 | Some class_ ->
5154 (* We need to instantiate generic parameters in the method signature *)
5155 let ety_env =
5157 type_expansions = [];
5158 this_ty;
5159 substs = TUtils.make_locl_subst_for_class_tparams class_ paraml;
5160 from_class = Some cid;
5161 quiet = true;
5162 on_error = Errors.unify_error_at p;
5165 let get_smember_from_constraints env class_info =
5166 let upper_bounds =
5167 Cls.upper_bounds_on_this_from_constraints class_info
5169 let (env, upper_bounds) =
5170 List.map_env env upper_bounds ~f:(fun env up ->
5171 Phase.localize ~ety_env env up)
5173 let (env, inter_ty) =
5174 Inter.intersect_list env (Reason.Rwitness p) upper_bounds
5176 class_get_
5177 ~is_method
5178 ~is_const
5179 ~this_ty
5180 ~explicit_targs
5181 ~incl_tc
5182 ~coerce_from_ty
5185 inter_ty
5186 (p, mid)
5188 let try_get_smember_from_constraints env class_info =
5189 Errors.try_with_error
5190 (fun () -> get_smember_from_constraints env class_info)
5191 (fun () ->
5192 TOG.smember_not_found
5194 ~is_const
5195 ~is_method
5196 class_info
5198 Errors.unify_error;
5199 (env, (TUtils.terr env Reason.Rnone, [])))
5201 if is_const then (
5202 let const =
5203 if incl_tc then
5204 Env.get_const env class_ mid
5205 else
5206 match Env.get_typeconst env class_ mid with
5207 | Some _ ->
5208 Errors.illegal_typeconst_direct_access p;
5209 None
5210 | None -> Env.get_const env class_ mid
5212 match const with
5213 | None when Cls.has_upper_bounds_on_this_from_constraints class_ ->
5214 try_get_smember_from_constraints env class_
5215 | None ->
5216 TOG.smember_not_found
5218 ~is_const
5219 ~is_method
5220 class_
5222 Errors.unify_error;
5223 (env, (TUtils.terr env Reason.Rnone, []))
5224 | Some { cc_type; cc_abstract; cc_pos; _ } ->
5225 let (env, cc_locl_type) = Phase.localize ~ety_env env cc_type in
5226 ( if cc_abstract then
5227 match cid with
5228 | CIstatic
5229 | CIexpr _ ->
5231 | _ ->
5232 let cc_name = Cls.name class_ ^ "::" ^ mid in
5233 Errors.abstract_const_usage p cc_pos cc_name );
5234 (env, (cc_locl_type, []))
5235 ) else
5236 let static_member_opt =
5237 Env.get_static_member is_method env class_ mid
5239 (match static_member_opt with
5240 | None when Cls.has_upper_bounds_on_this_from_constraints class_ ->
5241 try_get_smember_from_constraints env class_
5242 | None ->
5243 TOG.smember_not_found
5245 ~is_const
5246 ~is_method
5247 class_
5249 Errors.unify_error;
5250 (env, (TUtils.terr env Reason.Rnone, []))
5251 | Some
5253 ce_visibility = vis;
5254 ce_type = (lazy member_decl_ty);
5255 ce_deprecated;
5257 } as ce ) ->
5258 let def_pos = get_pos member_decl_ty in
5259 TVis.check_class_access
5260 ~use_pos:p
5261 ~def_pos
5263 (vis, get_ce_lsb ce)
5265 class_;
5266 TVis.check_deprecated ~use_pos:p ~def_pos ce_deprecated;
5267 check_class_get env p def_pos c mid ce cid function_pointer;
5268 let (env, member_ty, et_enforced, tal) =
5269 match deref member_decl_ty with
5270 (* We special case Tfun here to allow passing in explicit tparams to localize_ft. *)
5271 | (r, Tfun ft) when is_method ->
5272 let (env, explicit_targs) =
5273 Phase.localize_targs
5274 ~check_well_kinded:true
5275 ~is_method:true
5276 ~def_pos
5277 ~use_pos:p
5278 ~use_name:(strip_ns mid)
5280 ft.ft_tparams
5281 (List.map ~f:snd explicit_targs)
5283 let ft =
5284 Typing_enforceability.compute_enforced_and_pessimize_fun_type
5288 let (env, ft) =
5289 Phase.(
5290 localize_ft
5291 ~instantiation:
5292 { use_name = strip_ns mid; use_pos = p; explicit_targs }
5293 ~ety_env
5294 ~def_pos
5298 (env, mk (r, Tfun ft), false, explicit_targs)
5299 (* unused *)
5300 | _ ->
5301 let { et_type; et_enforced } =
5302 Typing_enforceability.compute_enforced_and_pessimize_ty
5304 member_decl_ty
5306 let (env, member_ty) = Phase.localize ~ety_env env et_type in
5307 (* TODO(T52753871) make function just return possibly_enforced_ty
5308 * after considering intersection case *)
5309 (env, member_ty, et_enforced, [])
5311 let (env, member_ty) =
5312 if Cls.has_upper_bounds_on_this_from_constraints class_ then
5313 let ((env, (member_ty', _)), succeed) =
5314 Errors.try_with_error
5315 (fun () -> (get_smember_from_constraints env class_, true))
5316 (fun () ->
5317 (* No eligible functions found in constraints *)
5318 ((env, (MakeType.mixed Reason.Rnone, [])), false))
5320 if succeed then
5321 Inter.intersect env (Reason.Rwitness p) member_ty member_ty'
5322 else
5323 (env, member_ty)
5324 else
5325 (env, member_ty)
5327 let env =
5328 match coerce_from_ty with
5329 | None -> env
5330 | Some (p, ur, ty) ->
5331 Typing_coercion.coerce_type
5336 { et_type = member_ty; et_enforced }
5337 Errors.unify_error
5339 (env, (member_ty, tal))))
5340 | (_, Tunapplied_alias _) ->
5341 Typing_defs.error_Tunapplied_alias_in_illegal_context ()
5342 | ( _,
5343 ( Tvar _ | Tnonnull | Tvarray _ | Tdarray _ | Tvarray_or_darray _
5344 | Toption _ | Tprim _ | Tfun _ | Ttuple _ | Tobject | Tshape _ | Taccess _
5345 ) ) ->
5346 Errors.non_class_member
5347 ~is_method
5350 (Typing_print.error env cty)
5351 (get_pos cty);
5352 (env, (err_witness env p, []))
5354 and class_id_for_new
5355 ~exact p env (cid : Nast.class_id_) (explicit_targs : Nast.targ list) :
5356 env * Tast.targ list * Tast.class_id * (sid * Cls.t * locl_ty) list =
5357 let (env, tal, te, cid_ty) =
5358 static_class_id
5359 ~check_targs_well_kinded:true
5360 ~check_explicit_targs:true
5361 ~exact
5362 ~check_constraints:false
5365 explicit_targs
5368 (* Need to deal with union case *)
5369 let rec get_info res tyl =
5370 match tyl with
5371 | [] -> (env, tal, te, res)
5372 | ty :: tyl ->
5373 (match get_node ty with
5374 | Tunion tyl'
5375 | Tintersection tyl' ->
5376 get_info res (tyl' @ tyl)
5377 | _ ->
5378 (* Instantiation on an abstract class (e.g. from classname<T>) is
5379 * via the base type (to check constructor args), but the actual
5380 * type `ty` must be preserved. *)
5381 (match get_class_type (TUtils.get_base_type env ty) with
5382 | Some (sid, _, _) ->
5383 let class_ = Env.get_class env (snd sid) in
5384 (match class_ with
5385 | None -> get_info res tyl
5386 | Some class_info ->
5387 (match (te, cid_ty) with
5388 (* When computing the classes for a new T() where T is a generic,
5389 * the class must be consistent (final, final constructor, or
5390 * <<__ConsistentConstruct>>) for its constructor to be considered *)
5391 | ((_, Aast.CI (_, c)), ty) when is_generic_equal_to c ty ->
5392 (* Only have this choosing behavior for new T(), not all generic types
5393 * i.e. new classname<T>, TODO: T41190512 *)
5394 if Tast_utils.valid_newable_class class_info then
5395 get_info ((sid, class_info, ty) :: res) tyl
5396 else
5397 get_info res tyl
5398 | _ -> get_info ((sid, class_info, ty) :: res) tyl))
5399 | None -> get_info res tyl))
5401 get_info [] [cid_ty]
5403 (* To be a valid trait declaration, all of its 'require extends' must
5404 * match; since there's no multiple inheritance, it follows that all of
5405 * the 'require extends' must belong to the same inheritance hierarchy
5406 * and one of them should be the child of all the others *)
5407 and trait_most_concrete_req_class trait env =
5408 List.fold
5409 (Cls.all_ancestor_reqs trait)
5411 begin
5412 fun acc (_p, ty) ->
5413 let (_r, (_p, name), _paraml) = TUtils.unwrap_class_type ty in
5414 let keep =
5415 match acc with
5416 | Some (c, _ty) -> Cls.has_ancestor c name
5417 | None -> false
5419 if keep then
5421 else
5422 let class_ = Env.get_class env name in
5423 match class_ with
5424 | None -> acc
5425 | Some c when Ast_defs.(equal_class_kind (Cls.kind c) Cinterface) ->
5427 | Some c when Ast_defs.(equal_class_kind (Cls.kind c) Ctrait) ->
5428 (* this is an error case for which Typing_check_decls spits out
5429 * an error, but does *not* currently remove the offending
5430 * 'require extends' or 'require implements' *)
5432 | Some c -> Some (c, ty)
5434 ~init:None
5436 (* When invoking a method the class_id is used to determine what class we
5437 * lookup the method in, but the type of 'this' will be the late bound type.
5438 * For example:
5440 * class C {
5441 * public static function get(): this { return new static(); }
5443 * public static function alias(): this { return self::get(); }
5446 * In C::alias, when we invoke self::get(), 'self' is resolved to the class
5447 * in the lexical scope (C), so call C::get. However the method is executed in
5448 * the current context, so static inside C::get will be resolved to the late
5449 * bound type (get_called_class() within C::alias).
5451 * This means when determining the type of this, CIparent and CIself should be
5452 * changed to CIstatic. For the other cases of C::get() or $c::get(), we only
5453 * look at the left hand side of the '::' and use the type type associated
5454 * with it.
5456 * Thus C::get() will return a type C, while $c::get() will return the same
5457 * type as $c.
5459 and this_for_method env cid default_ty =
5460 match cid with
5461 | CIparent
5462 | CIself
5463 | CIstatic ->
5464 let p = get_pos default_ty in
5465 let (env, _tal, _te, ty) =
5466 static_class_id ~check_constraints:false p env [] CIstatic
5468 ExprDepTy.make env CIstatic ty
5469 | _ -> (env, default_ty)
5471 (** Resolve class expressions like `parent`, `self`, `static`, classnames
5472 and others. *)
5473 and static_class_id
5474 ?(check_targs_well_kinded = false)
5475 ?(exact = Nonexact)
5476 ?(check_explicit_targs = false)
5477 ~(check_constraints : bool)
5478 (p : pos)
5479 (env : env)
5480 (tal : Nast.targ list) :
5481 Nast.class_id_ -> env * Tast.targ list * Tast.class_id * locl_ty =
5482 let make_result env tal te ty = (env, tal, ((p, ty), te), ty) in
5483 function
5484 | CIparent ->
5485 (match get_class_type (Env.get_self env) with
5486 | Some ((_, self), _, _) ->
5487 (match Env.get_class env self with
5488 | Some trait when Ast_defs.(equal_class_kind (Cls.kind trait) Ctrait) ->
5489 (match trait_most_concrete_req_class trait env with
5490 | None ->
5491 Errors.parent_in_trait p;
5492 make_result env [] Aast.CIparent (err_witness env p)
5493 | Some (_, parent_ty) ->
5494 (* inside a trait, parent is SN.Typehints.this, but with the
5495 * type of the most concrete class that the trait has
5496 * "require extend"-ed *)
5497 let r = Reason.Rwitness p in
5498 let (env, parent_ty) = Phase.localize_with_self env parent_ty in
5499 make_result env [] Aast.CIparent (mk (r, TUtils.this_of parent_ty)))
5500 | _ ->
5501 let parent =
5502 match Env.get_parent_ty env with
5503 | None ->
5504 Errors.parent_undefined p;
5505 mk (Reason.none, Typing_defs.make_tany ())
5506 | Some parent -> parent
5508 let r = Reason.Rwitness p in
5509 let (env, parent) = Phase.localize_with_self env parent in
5510 (* parent is still technically the same object. *)
5511 make_result
5514 Aast.CIparent
5515 (mk (r, TUtils.this_of (mk (r, get_node parent)))))
5516 | None ->
5517 let parent =
5518 match Env.get_parent_ty env with
5519 | None ->
5520 Errors.parent_undefined p;
5521 mk (Reason.none, Typing_defs.make_tany ())
5522 | Some parent -> parent
5524 let r = Reason.Rwitness p in
5525 let (env, parent) = Phase.localize_with_self env parent in
5526 (* parent is still technically the same object. *)
5527 make_result
5530 Aast.CIparent
5531 (mk (r, TUtils.this_of (mk (r, get_node parent)))))
5532 | CIstatic ->
5533 let this = mk (Reason.Rwitness p, TUtils.this_of (Env.get_self env)) in
5534 make_result env [] Aast.CIstatic this
5535 | CIself ->
5536 let self =
5537 match get_node (Env.get_self env) with
5538 | Tclass (c, _, tyl) -> Tclass (c, exact, tyl)
5539 | self -> self
5541 make_result env [] Aast.CIself (mk (Reason.Rwitness p, self))
5542 | CI ((p, id) as c) as e1 ->
5543 begin
5544 match Env.get_pos_and_kind_of_generic env id with
5545 | Some (def_pos, kind) ->
5546 let simple_kind = Typing_kinding_defs.Simple.from_full_kind kind in
5547 let param_nkinds =
5548 Typing_kinding_defs.Simple.get_named_parameter_kinds simple_kind
5550 let (env, tal) =
5551 Phase.localize_targs_with_kinds
5552 ~check_well_kinded:check_targs_well_kinded
5553 ~is_method:true
5554 ~def_pos
5555 ~use_pos:p
5556 ~use_name:(strip_ns (snd c))
5557 ~check_explicit_targs
5559 param_nkinds
5560 (List.map ~f:snd tal)
5562 let r = Reason.Rhint p in
5563 let type_args = List.map tal fst in
5564 let tgeneric = MakeType.generic ~type_args r id in
5565 make_result env tal (Aast.CI c) tgeneric
5566 | None ->
5567 (* Not a type parameter *)
5568 let class_ = Env.get_class env id in
5569 (match class_ with
5570 | None -> make_result env [] (Aast.CI c) (Typing_utils.mk_tany env p)
5571 | Some class_ ->
5572 let (env, ty, tal) =
5573 List.map ~f:snd tal
5574 |> Phase.localize_targs_and_check_constraints
5575 ~exact
5576 ~check_well_kinded:check_targs_well_kinded
5577 ~check_constraints
5578 ~def_pos:(Cls.pos class_)
5579 ~use_pos:p
5580 ~check_explicit_targs
5584 (Cls.tparams class_)
5586 make_result env tal (Aast.CI c) ty)
5588 | CIexpr ((p, _) as e) ->
5589 let (env, te, ty) = expr env e in
5590 let rec resolve_ety env ty =
5591 let (env, ty) =
5592 Typing_solver.expand_type_and_solve
5593 ~description_of_expected:"an object"
5597 Errors.unify_error
5599 let base_ty = TUtils.get_base_type env ty in
5600 match deref base_ty with
5601 | (_, Tnewtype (classname, [the_cls], _))
5602 when String.equal classname SN.Classes.cClassname ->
5603 resolve_ety env the_cls
5604 | (_, Tgeneric _)
5605 | (_, Tclass _) ->
5606 (env, ty)
5607 | (r, Tunion tyl) ->
5608 let (env, tyl) = List.map_env env tyl resolve_ety in
5609 (env, MakeType.union r tyl)
5610 | (r, Tintersection tyl) ->
5611 let (env, tyl) = TUtils.run_on_intersection env tyl ~f:resolve_ety in
5612 Inter.intersect_list env r tyl
5613 | (_, Tdynamic) -> (env, base_ty)
5614 | (_, (Tany _ | Tprim Tstring | Tobject)) when not (Env.is_strict env) ->
5615 (env, Typing_utils.mk_tany env p)
5616 | (_, Terr) -> (env, err_witness env p)
5617 | (r, Tvar _) ->
5618 Errors.unknown_type "an object" p (Reason.to_string "It is unknown" r);
5619 (env, err_witness env p)
5620 | (_, Tunapplied_alias _) ->
5621 Typing_defs.error_Tunapplied_alias_in_illegal_context ()
5622 | ( _,
5623 ( Tany _ | Tnonnull | Tvarray _ | Tdarray _ | Tvarray_or_darray _
5624 | Toption _ | Tprim _ | Tfun _ | Ttuple _ | Tnewtype _ | Tdependent _
5625 | Tobject | Tshape _ | Taccess _ ) ) ->
5626 Errors.expected_class
5627 ~suffix:(", but got " ^ Typing_print.error env base_ty)
5629 (env, err_witness env p)
5631 let (env, result_ty) = resolve_ety env ty in
5632 make_result env [] (Aast.CIexpr te) result_ty
5634 and call_construct p env class_ params el unpacked_element cid cid_ty =
5635 let (cid, cid_ty) =
5636 if Nast.equal_class_id_ cid CIparent then
5637 (CIstatic, mk (Reason.Rwitness p, TUtils.this_of (Env.get_self env)))
5638 else
5639 (cid, cid_ty)
5641 let ety_env =
5643 type_expansions = [];
5644 this_ty = cid_ty;
5645 substs = TUtils.make_locl_subst_for_class_tparams class_ params;
5646 from_class = Some cid;
5647 quiet = true;
5648 on_error = Errors.unify_error_at p;
5651 let env =
5652 Phase.check_tparams_constraints ~use_pos:p ~ety_env env (Cls.tparams class_)
5654 let env =
5655 Phase.check_where_constraints
5656 ~in_class:true
5657 ~use_pos:p
5658 ~definition_pos:(Cls.pos class_)
5659 ~ety_env
5661 (Cls.where_constraints class_)
5663 if Cls.is_xhp class_ then
5664 (env, [], None, TUtils.mk_tany env p)
5665 else
5666 let cstr = Env.get_construct env class_ in
5667 let mode = Env.get_mode env in
5668 match fst cstr with
5669 | None ->
5671 ((not (List.is_empty el)) || Option.is_some unpacked_element)
5672 && (FileInfo.is_strict mode || FileInfo.(equal_mode mode Mpartial))
5673 && Cls.members_fully_known class_
5674 then
5675 Errors.constructor_no_args p;
5676 let (env, tel, _tyl) = exprs env el in
5677 (env, tel, None, TUtils.terr env Reason.Rnone)
5678 | Some { ce_visibility = vis; ce_type = (lazy m); ce_deprecated; _ } ->
5679 let def_pos = get_pos m in
5680 TVis.check_obj_access ~use_pos:p ~def_pos env vis;
5681 TVis.check_deprecated ~use_pos:p ~def_pos ce_deprecated;
5682 (* Obtain the type of the constructor *)
5683 let (env, m) =
5684 match deref m with
5685 | (r, Tfun ft) ->
5686 let ft =
5687 Typing_enforceability.compute_enforced_and_pessimize_fun_type env ft
5689 let (env, ft) =
5690 Phase.(
5691 localize_ft
5692 ~instantiation:
5693 { use_name = "constructor"; use_pos = p; explicit_targs = [] }
5694 ~ety_env
5695 ~def_pos
5699 (env, mk (r, Tfun ft))
5700 | (r, _) ->
5701 Errors.internal_error p "Expected function type for constructor";
5702 let ty = TUtils.terr env r in
5703 (env, ty)
5705 let (env, (tel, typed_unpack_element, _ty)) =
5706 call ~expected:None p env m el unpacked_element
5708 (env, tel, typed_unpack_element, m)
5710 and check_arity ?(did_unpack = false) pos pos_def ft (arity : int) =
5711 let exp_min = Typing_defs.arity_min ft in
5712 if arity < exp_min then
5713 Errors.typing_too_few_args exp_min arity pos pos_def None;
5714 match ft.ft_arity with
5715 | Fstandard ->
5716 let exp_max = List.length ft.ft_params in
5717 let arity =
5718 if did_unpack then
5719 arity + 1
5720 else
5721 arity
5723 if arity > exp_max then
5724 Errors.typing_too_many_args exp_max arity pos pos_def None
5725 | Fvariadic _ -> ()
5727 and check_lambda_arity lambda_pos def_pos lambda_ft expected_ft =
5728 match (lambda_ft.ft_arity, expected_ft.ft_arity) with
5729 | (Fstandard, Fstandard) ->
5730 let expected_min = Typing_defs.arity_min expected_ft in
5731 let lambda_min = Typing_defs.arity_min lambda_ft in
5732 if lambda_min < expected_min then
5733 Errors.typing_too_few_args expected_min lambda_min lambda_pos def_pos None;
5734 if lambda_min > expected_min then
5735 Errors.typing_too_many_args
5736 expected_min
5737 lambda_min
5738 lambda_pos
5739 def_pos
5740 None
5741 | (_, _) -> ()
5743 (* The variadic capture argument is an array listing the passed
5744 * variable arguments for the purposes of the function body; callsites
5745 * should not unify with it *)
5746 and variadic_param env ft =
5747 match ft.ft_arity with
5748 | Fvariadic param -> (env, Some param)
5749 | Fstandard -> (env, None)
5751 and param_modes ?(is_variadic = false) ({ fp_pos; _ } as fp) (pos, e) =
5752 match (get_fp_mode fp, e) with
5753 | (FPnormal, Callconv _) ->
5754 Errors.inout_annotation_unexpected pos fp_pos is_variadic
5755 | (FPnormal, _) -> ()
5756 | (FPinout, Callconv (Ast_defs.Pinout, _)) -> ()
5757 | (FPinout, _) -> Errors.inout_annotation_missing pos fp_pos
5759 and inout_write_back env { fp_type; _ } (_, e) =
5760 match e with
5761 | Callconv (Ast_defs.Pinout, e1) ->
5762 (* Translate the write-back semantics of inout parameters.
5764 * This matters because we want to:
5765 * (1) make sure we can write to the original argument
5766 * (modifiable lvalue check)
5767 * (2) allow for growing of locals / Tunions (type side effect)
5768 * but otherwise unify the argument type with the parameter hint
5770 let (env, _te, _ty) =
5771 assign_ (fst e1) Reason.URparam_inout env e1 fp_type.et_type
5774 | _ -> env
5776 (** Typechecks a call.
5777 Returns in this order the typed expressions for the arguments, for the variadic arguments, and the return type. *)
5778 and call
5779 ~(expected : ExpectedTy.t option)
5780 ?(method_call_info : TR.method_call_info option)
5781 ?(nullsafe : Pos.t option = None)
5782 ?in_await
5786 (el : Nast.expr list)
5787 (unpacked_element : Nast.expr option) :
5788 env * (Tast.expr list * Tast.expr option * locl_ty) =
5789 let resl =
5790 TUtils.try_over_concrete_supertypes env fty (fun env fty ->
5791 let (env, efty) =
5792 if TypecheckerOptions.method_call_inference (Env.get_tcopt env) then
5793 Env.expand_type env fty
5794 else
5795 Typing_solver.expand_type_and_solve
5796 ~description_of_expected:"a function value"
5800 Errors.unify_error
5802 match deref efty with
5803 | (r, ((Tprim Tnull | Tdynamic | Terr | Tany _ | Tunion []) as ty))
5804 when match ty with
5805 | Tprim Tnull -> Option.is_some nullsafe
5806 | _ -> true ->
5807 let el =
5808 Option.value_map ~f:(fun u -> el @ [u]) ~default:el unpacked_element
5810 let expected_arg_ty =
5811 (* Note: We ought to be using 'mixed' here *)
5812 ExpectedTy.make pos Reason.URparam (Typing_utils.mk_tany env pos)
5814 let (env, tel) =
5815 List.map_env env el (fun env elt ->
5816 let (env, te, ty) = expr ~expected:expected_arg_ty env elt in
5817 let env =
5818 if TCO.global_inference (Env.get_tcopt env) then
5819 match get_node efty with
5820 | Terr
5821 | Tany _
5822 | Tdynamic ->
5823 Typing_coercion.coerce_type
5825 Reason.URparam
5828 (MakeType.unenforced efty)
5829 Errors.unify_error
5830 | _ -> env
5831 else
5834 let env =
5835 match elt with
5836 | (_, Callconv (Ast_defs.Pinout, e1)) ->
5837 let (env, _te, _ty) =
5838 assign_ (fst e1) Reason.URparam_inout env e1 efty
5841 | _ -> env
5843 (env, te))
5845 let env =
5846 call_untyped_unpack env (Reason.to_pos r) unpacked_element
5848 let ty =
5849 match ty with
5850 | Tprim Tnull -> mk (r, Tprim Tnull)
5851 | Tdynamic -> MakeType.dynamic (Reason.Rdynamic_call pos)
5852 | Terr
5853 | Tany _ ->
5854 Typing_utils.mk_tany env pos
5855 | Tunion []
5856 | _ (* _ should not happen! *) ->
5857 mk (r, Tunion [])
5859 (env, (tel, None, ty))
5860 | (_, Tunion [ty]) ->
5861 call ~expected ~nullsafe ?in_await pos env ty el unpacked_element
5862 | (r, Tunion tyl) ->
5863 let (env, resl) =
5864 List.map_env env tyl (fun env ty ->
5865 call
5866 ~expected
5867 ~nullsafe
5868 ?in_await
5873 unpacked_element)
5875 let retl = List.map resl ~f:(fun (_, _, x) -> x) in
5876 let (env, ty) = Union.union_list env r retl in
5877 (* We shouldn't be picking arbitrarily for the TAST here, as TAST checks
5878 * depend on the types inferred. Here's we're preserving legacy behaviour
5879 * by picking the last one.
5880 * TODO: don't do this, instead use subtyping to push unions
5881 * through function types
5883 let (tel, typed_unpack_element, _) = List.hd_exn (List.rev resl) in
5884 (env, (tel, typed_unpack_element, ty))
5885 | (r, Tintersection tyl) ->
5886 let (env, resl) =
5887 TUtils.run_on_intersection env tyl ~f:(fun env ty ->
5888 call
5889 ~expected
5890 ~nullsafe
5891 ?in_await
5896 unpacked_element)
5898 let retl = List.map resl ~f:(fun (_, _, x) -> x) in
5899 let (env, ty) = Inter.intersect_list env r retl in
5900 (* We shouldn't be picking arbitrarily for the TAST here, as TAST checks
5901 * depend on the types inferred. Here we're preserving legacy behaviour
5902 * by picking the last one.
5903 * TODO: don't do this, instead use subtyping to push intersections
5904 * through function types
5906 let (tel, typed_unpack_element, _) = List.hd_exn (List.rev resl) in
5907 (env, (tel, typed_unpack_element, ty))
5908 | (r2, Tfun ft) ->
5909 (* Typing of format string functions. It is dependent on the arguments (el)
5910 * so it cannot be done earlier.
5912 let pos_def = Reason.to_pos r2 in
5913 let (env, ft) = Typing_exts.retype_magic_func env ft el in
5914 let (env, var_param) = variadic_param env ft in
5915 (* Force subtype with expected result *)
5916 let env =
5917 check_expected_ty "Call result" env ft.ft_ret.et_type expected
5919 let env = Env.set_tyvar_variance env ft.ft_ret.et_type in
5920 let is_lambda e =
5921 match snd e with
5922 | Efun _
5923 | Lfun _ ->
5924 true
5925 | _ -> false
5927 let get_next_param_info paraml =
5928 match paraml with
5929 | param :: paraml -> (false, Some param, paraml)
5930 | [] -> (true, var_param, paraml)
5932 (* TODO: We're using plain String at the moment, until we
5933 * introduce actual atom notation (#A instead of "A")
5935 let expand_atom_in_enum enum_name atom_name expected_ty =
5936 let cls = Env.get_class env enum_name in
5937 let open Option in
5938 cls >>= fun cls ->
5939 let constants = Cls.consts cls in
5940 if List.Assoc.mem ~equal:String.equal constants atom_name then
5941 let hi = (pos, expected_ty) in
5942 let te = (hi, EnumAtom atom_name) in
5943 Some (te, expected_ty)
5944 else (
5945 Errors.atom_unknown pos atom_name enum_name;
5946 None
5949 let check_arg env ((pos, arg) as e) opt_param ~is_variadic =
5950 match opt_param with
5951 | Some param ->
5952 (* First check if __Atom is used *)
5953 let atom_type =
5954 let is_atom = get_fp_is_atom param in
5955 let ety = param.fp_type.et_type in
5956 match arg with
5957 | EnumAtom atom_name when is_atom ->
5958 (match get_node ety with
5959 (* Uncomment this if we want to support atoms for normal
5960 * enums
5962 (* | Tnewtype (enum_name, _, _) when Env.is_enum env enum_name -> *)
5963 (* expand_atom_in_enum enum_name atom_name ety *)
5964 | Tclass ((_, name), _, [ty_enum; _ty_interface])
5965 when String.equal name SN.Classes.cElt ->
5966 (match get_node ty_enum with
5967 | Tclass ((_, enum_name), _, _)
5968 when Env.is_enum_class env enum_name ->
5969 expand_atom_in_enum enum_name atom_name ety
5970 | Tgeneric (name, _) ->
5971 let upper_bounds =
5972 Typing_utils.collect_enum_class_upper_bounds env name
5974 (* To avoid ambiguity, we only support the case where
5975 * there is a single upper bound that is an EnumClass.
5976 * We might want to relax that later (e.g. with the
5977 * support for intersections.
5978 * See Typing_check_decls.check_atom_on_param.
5980 if SSet.cardinal upper_bounds = 1 then
5981 let enum_name = SSet.choose upper_bounds in
5982 expand_atom_in_enum enum_name atom_name ety
5983 else
5984 None
5985 | _ ->
5986 (* Already reported, see Typing_check_decls *)
5987 None)
5988 | _ ->
5989 (* Already reported, see Typing_check_decls *)
5990 None)
5991 | Class_const _ when is_atom ->
5992 Errors.atom_invalid_argument pos;
5993 None
5994 | _ -> None
5996 let (env, te, ty) =
5997 match atom_type with
5998 | Some (te, ty) -> (env, te, ty)
5999 | None ->
6000 let expected =
6001 ExpectedTy.make_and_allow_coercion
6003 Reason.URparam
6004 param.fp_type
6006 expr
6007 ~accept_using_var:(get_fp_accept_disposable param)
6008 ~expected
6012 let env = call_param env param (e, ty) ~is_variadic in
6013 (env, Some (te, ty))
6014 | None ->
6015 let expected =
6016 ExpectedTy.make
6018 Reason.URparam
6019 (Typing_utils.mk_tany env pos)
6021 let (env, te, ty) = expr ~expected env e in
6022 (env, Some (te, ty))
6024 let set_tyvar_variance_from_lambda_param env opt_param =
6025 match opt_param with
6026 | Some param ->
6027 let rec set_params_variance env ty =
6028 let (env, ty) = Env.expand_type env ty in
6029 match get_node ty with
6030 | Tunion [ty] -> set_params_variance env ty
6031 | Toption ty -> set_params_variance env ty
6032 | Tfun { ft_params; ft_ret; _ } ->
6033 let env =
6034 List.fold
6035 ~init:env
6036 ~f:(fun env param ->
6037 Env.set_tyvar_variance env param.fp_type.et_type)
6038 ft_params
6040 Env.set_tyvar_variance env ft_ret.et_type ~flip:true
6041 | _ -> env
6043 set_params_variance env param.fp_type.et_type
6044 | None -> env
6046 (* Given an expected function type ft, check types for the non-unpacked
6047 * arguments. Don't check lambda expressions if check_lambdas=false *)
6048 let rec check_args check_lambdas env el paraml =
6049 match el with
6050 (* We've got an argument *)
6051 | (e, opt_result) :: el ->
6052 (* Pick up next parameter type info *)
6053 let (is_variadic, opt_param, paraml) =
6054 get_next_param_info paraml
6056 let (env, one_result) =
6057 match (check_lambdas, is_lambda e) with
6058 | (false, false)
6059 | (true, true) ->
6060 check_arg env e opt_param ~is_variadic
6061 | (false, true) ->
6062 let env =
6063 set_tyvar_variance_from_lambda_param env opt_param
6065 (env, opt_result)
6066 | (true, false) -> (env, opt_result)
6068 let (env, rl, paraml) = check_args check_lambdas env el paraml in
6069 (env, (e, one_result) :: rl, paraml)
6070 | [] -> (env, [], paraml)
6072 (* Same as above, but checks the types of the implicit arguments, which are
6073 * read from the context *)
6074 let check_implicit_args env =
6075 let (env, capability) =
6076 Typing_coeffects.get_type env ft.ft_implicit_params.capability
6078 if not (TypecheckerOptions.coeffects (Env.get_tcopt env)) then
6080 else
6081 let env_capability =
6082 Env.get_local_check_defined
6084 (pos, Typing_coeffects.capability_id)
6086 Type.sub_type
6088 Reason.URnone
6090 env_capability
6091 capability
6092 (fun ?code:_c _subtype_error_list ->
6093 Errors.call_coeffect_error
6095 ~available_incl_unsafe:
6096 (Typing_print.coeffects env env_capability)
6097 ~available_pos:(Typing_defs.get_pos env_capability)
6098 ~required_pos:(Typing_defs.get_pos capability)
6099 ~required:(Typing_print.coeffects env capability))
6102 (* First check the non-lambda arguments. For generic functions, this
6103 * is likely to resolve type variables to concrete types *)
6104 let rl = List.map el (fun e -> (e, None)) in
6105 let (env, rl, _) = check_args false env rl ft.ft_params in
6106 (* Now check the lambda arguments, hopefully with type variables resolved *)
6107 let (env, rl, paraml) = check_args true env rl ft.ft_params in
6108 (* We expect to see results for all arguments after this second pass *)
6109 let get_param opt =
6110 match opt with
6111 | Some x -> x
6112 | None -> failwith "missing parameter in check_args"
6114 let (tel, tys) =
6115 let l = List.map rl (fun (_, opt) -> get_param opt) in
6116 List.unzip l
6118 let env = check_implicit_args env in
6119 let env = TR.check_call env method_call_info pos r2 ft tys in
6120 let (env, typed_unpack_element, arity, did_unpack) =
6121 match unpacked_element with
6122 | None -> (env, None, List.length el, false)
6123 | Some e ->
6124 (* Now that we're considering an splat (Some e) we need to construct a type that
6125 * represents the remainder of the function's parameters. `paraml` represents those
6126 * remaining parameters, and the variadic parameter is stored in `var_param`. For example, given
6128 * function f(int $i, string $j, float $k = 3.14, mixed ...$m): void {}
6129 * function g((string, float, bool) $t): void {
6130 * f(3, ...$t);
6133 * the constraint type we want is splat([#1], [opt#2], #3).
6135 let (consumed, required_params, optional_params) =
6136 split_remaining_params_required_optional ft paraml
6138 let (env, (d_required, d_optional, d_variadic)) =
6139 generate_splat_type_vars
6141 (fst e)
6142 required_params
6143 optional_params
6144 var_param
6146 let destructure_ty =
6147 ConstraintType
6148 (mk_constraint_type
6149 ( Reason.Runpack_param (fst e, pos_def, consumed),
6150 Tdestructure
6152 d_required;
6153 d_optional;
6154 d_variadic;
6155 d_kind = SplatUnpack;
6156 } ))
6158 let (env, te, ty) = expr env e in
6159 (* Populate the type variables from the expression in the splat *)
6160 let env =
6161 Type.sub_type_i
6162 (fst e)
6163 Reason.URparam
6165 (LoclType ty)
6166 destructure_ty
6167 Errors.unify_error
6169 (* Use the type variables for the remaining parameters *)
6170 let env =
6171 List.fold2_exn
6172 ~init:env
6173 d_required
6174 required_params
6175 ~f:(fun env elt param ->
6176 call_param env param (e, elt) ~is_variadic:false)
6178 let env =
6179 List.fold2_exn
6180 ~init:env
6181 d_optional
6182 optional_params
6183 ~f:(fun env elt param ->
6184 call_param env param (e, elt) ~is_variadic:false)
6186 let env =
6187 Option.map2 d_variadic var_param ~f:(fun v vp ->
6188 call_param env vp (e, v) ~is_variadic:true)
6189 |> Option.value ~default:env
6191 ( env,
6192 Some te,
6193 List.length el + List.length d_required,
6194 Option.is_some d_variadic )
6196 (* If we unpacked an array, we don't check arity exactly. Since each
6197 * unpacked array consumes 1 or many parameters, it is nonsensical to say
6198 * that not enough args were passed in (so we don't do the min check).
6200 let () = check_arity ~did_unpack pos pos_def ft arity in
6201 (* Variadic params cannot be inout so we can stop early *)
6202 let env = wfold_left2 inout_write_back env ft.ft_params el in
6203 let (env, ret_ty) =
6204 TR.get_adjusted_return_type env method_call_info ft.ft_ret.et_type
6206 (env, (tel, typed_unpack_element, ret_ty))
6207 | (r, Tvar _)
6208 when TypecheckerOptions.method_call_inference (Env.get_tcopt env) ->
6210 Typecheck calls with unresolved function type by constructing a
6211 suitable function type from the arguments and invoking subtyping.
6213 let (env, typed_el, type_of_el) =
6214 exprs ~accept_using_var:true env el
6216 let (env, typed_unpacked_element, type_of_unpacked_element) =
6217 match unpacked_element with
6218 | Some unpacked ->
6219 let (env, typed_unpacked, type_of_unpacked) =
6220 expr ~accept_using_var:true env unpacked
6222 (env, Some typed_unpacked, Some type_of_unpacked)
6223 | None -> (env, None, None)
6225 let mk_function_supertype
6226 env pos (type_of_el, type_of_unpacked_element) =
6227 let mk_fun_param ty =
6228 let flags =
6229 (* Keep supertype as permissive as possible: *)
6230 make_fp_flags
6231 ~mode:FPnormal (* TODO: deal with `inout` parameters *)
6232 ~accept_disposable:false (* TODO: deal with disposables *)
6233 ~mutability:(Some Param_maybe_mutable)
6234 ~has_default:false
6235 ~ifc_external:false
6236 ~ifc_can_call:false
6237 ~is_atom:false
6240 fp_pos = pos;
6241 fp_name = None;
6242 fp_type = MakeType.enforced ty;
6243 fp_rx_annotation = None;
6244 fp_flags = flags;
6247 let ft_arity =
6248 match type_of_unpacked_element with
6249 | Some type_of_unpacked ->
6250 let fun_param = mk_fun_param type_of_unpacked in
6251 Fvariadic fun_param
6252 | None -> Fstandard
6254 (* TODO: ensure `ft_params`/`ft_where_constraints` don't affect subtyping *)
6255 let ft_tparams = [] in
6256 let ft_where_constraints = [] in
6257 let ft_params = List.map ~f:mk_fun_param type_of_el in
6258 let ft_implicit_params =
6260 capability =
6261 CapDefaults pos
6262 (* TODO(coeffects) should this be a different type? *);
6265 let (env, return_ty) = Env.fresh_type env pos in
6266 let return_ty =
6267 match in_await with
6268 | None -> return_ty
6269 | Some r -> MakeType.awaitable r return_ty
6271 let ft_ret = MakeType.enforced return_ty in
6272 (* A non-reactive supertype is most permissive: *)
6273 let ft_reactive = Nonreactive in
6274 let ft_flags =
6275 (* Keep supertype as permissive as possible: *)
6276 make_ft_flags
6277 Ast_defs.FSync (* `FSync` fun can still return `Awaitable<_>` *)
6278 (Some Param_maybe_mutable)
6279 ~return_disposable:false (* TODO: deal with disposable return *)
6280 ~returns_mutable:false
6281 ~returns_void_to_rx:false
6283 let ft_ifc_decl = Typing_defs_core.default_ifc_fun_decl in
6284 let fun_locl_type =
6286 ft_arity;
6287 ft_tparams;
6288 ft_where_constraints;
6289 ft_params;
6290 ft_implicit_params;
6291 ft_ret;
6292 ft_reactive;
6293 ft_flags;
6294 ft_ifc_decl;
6297 let fun_type = mk (r, Tfun fun_locl_type) in
6298 let env = Env.set_tyvar_variance env fun_type in
6299 (env, fun_type, return_ty)
6301 let (env, fun_type, return_ty) =
6302 mk_function_supertype env pos (type_of_el, type_of_unpacked_element)
6304 let env =
6305 Type.sub_type pos Reason.URnone env efty fun_type Errors.unify_error
6307 (env, (typed_el, typed_unpacked_element, return_ty))
6308 | _ ->
6309 bad_call env pos efty;
6310 let env = call_untyped_unpack env (get_pos efty) unpacked_element in
6311 (env, ([], None, err_witness env pos)))
6313 match resl with
6314 | [res] -> res
6315 | _ ->
6316 bad_call env pos fty;
6317 let env = call_untyped_unpack env (get_pos fty) unpacked_element in
6318 (env, ([], None, err_witness env pos))
6320 and split_remaining_params_required_optional ft remaining_params =
6321 (* Same example as above
6323 * function f(int $i, string $j, float $k = 3.14, mixed ...$m): void {}
6324 * function g((string, float, bool) $t): void {
6325 * f(3, ...$t);
6328 * `remaining_params` will contain [string, float] and there has been 1 parameter consumed. The min_arity
6329 * of this function is 2, so there is 1 required parameter remaining and 1 optional parameter.
6331 let min_arity =
6332 List.count
6333 ~f:(fun fp -> not (Typing_defs.get_fp_has_default fp))
6334 ft.ft_params
6336 let original_params = ft.ft_params in
6337 let consumed = List.length original_params - List.length remaining_params in
6338 let required_remaining = Int.max (min_arity - consumed) 0 in
6339 let (required_params, optional_params) =
6340 List.split_n remaining_params required_remaining
6342 (consumed, required_params, optional_params)
6344 and generate_splat_type_vars
6345 env p required_params optional_params variadic_param =
6346 let (env, d_required) =
6347 List.map_env env required_params ~f:(fun env _ -> Env.fresh_type env p)
6349 let (env, d_optional) =
6350 List.map_env env optional_params ~f:(fun env _ -> Env.fresh_type env p)
6352 let (env, d_variadic) =
6353 match variadic_param with
6354 | None -> (env, None)
6355 | Some _ ->
6356 let (env, ty) = Env.fresh_type env p in
6357 (env, Some ty)
6359 (env, (d_required, d_optional, d_variadic))
6361 and call_param env param (((pos, _) as e), arg_ty) ~is_variadic =
6362 param_modes ~is_variadic param e;
6364 (* When checking params, the type 'x' may be expression dependent. Since
6365 * we store the expression id in the local env for Lvar, we want to apply
6366 * it in this case.
6368 let (env, dep_ty) =
6369 match snd e with
6370 | Lvar _ -> ExprDepTy.make env (CIexpr e) arg_ty
6371 | _ -> (env, arg_ty)
6373 Typing_coercion.coerce_type
6375 Reason.URparam
6377 dep_ty
6378 param.fp_type
6379 Errors.unify_error
6381 and call_untyped_unpack env f_pos unpacked_element =
6382 match unpacked_element with
6383 (* In the event that we don't have a known function call type, we can still
6384 * verify that any unpacked arguments (`...$args`) are something that can
6385 * be actually unpacked. *)
6386 | None -> env
6387 | Some e ->
6388 let (env, _, ety) = expr env e in
6389 let (env, ty) = Env.fresh_type env (fst e) in
6390 let destructure_ty =
6391 MakeType.simple_variadic_splat (Reason.Runpack_param (fst e, f_pos, 0)) ty
6393 Type.sub_type_i
6394 f_pos
6395 Reason.URnone
6397 (LoclType ety)
6398 destructure_ty
6399 Errors.unify_error
6401 and bad_call env p ty = Errors.bad_call p (Typing_print.error env ty)
6403 and make_a_local_of env e =
6404 match e with
6405 | (p, Class_get ((_, cname), CGstring (_, member_name), _)) ->
6406 let (env, local) = Env.FakeMembers.make_static env cname member_name p in
6407 (env, Some (p, local))
6408 | ( p,
6409 Obj_get
6410 ((((_, This) | (_, Lvar _)) as obj), (_, Id (_, member_name)), _, _) )
6412 let (env, local) = Env.FakeMembers.make env obj member_name p in
6413 (env, Some (p, local))
6414 | (_, Lvar x)
6415 | (_, Dollardollar x) ->
6416 (env, Some x)
6417 | _ -> (env, None)
6419 (* This function captures the common bits of logic behind refinement
6420 * of the type of a local variable or a class member variable as a
6421 * result of a dynamic check (e.g., nullity check, simple type check
6422 * using functions like is_int, is_string, is_array etc.). The
6423 * argument refine is a function that takes the type of the variable
6424 * and returns a refined type (making necessary changes to the
6425 * environment, which is threaded through).
6427 * All refinement functions return, in addition to the updated
6428 * environment, a (conservative) set of all the locals that got
6429 * refined. This set is used to construct AssertEnv statmements in
6430 * the typed AST.
6432 and refine_lvalue_type env (((_p, ty), _) as te) ~refine =
6433 let (env, ty) = refine env ty in
6434 let e = Tast.to_nast_expr te in
6435 let (env, localopt) = make_a_local_of env e in
6436 (* TODO TAST: generate an assignment to the fake local in the TAST *)
6437 match localopt with
6438 | Some lid -> (set_local env lid ty, Local_id.Set.singleton (snd lid))
6439 | None -> (env, Local_id.Set.empty)
6441 and condition_nullity ~nonnull (env : env) te =
6442 match te with
6443 (* assignment: both the rhs and lhs of the '=' must be made null/non-null *)
6444 | (_, Aast.Binop (Ast_defs.Eq None, var, te)) ->
6445 let (env, lset1) = condition_nullity ~nonnull env te in
6446 let (env, lset2) = condition_nullity ~nonnull env var in
6447 (env, Local_id.Set.union lset1 lset2)
6448 (* case where `Shapes::idx(...)` must be made null/non-null *)
6449 | ( _,
6450 Aast.Call
6451 ( (_, Aast.Class_const ((_, Aast.CI (_, shapes)), (_, idx))),
6453 [shape; field],
6454 _ ) )
6455 when String.equal shapes SN.Shapes.cShapes && String.equal idx SN.Shapes.idx
6457 let field = Tast.to_nast_expr field in
6458 let refine env shape_ty =
6459 if nonnull then
6460 Typing_shapes.shapes_idx_not_null env shape_ty field
6461 else
6462 (env, shape_ty)
6464 refine_lvalue_type env shape ~refine
6465 | ((p, _), _) ->
6466 let refine env ty =
6467 if nonnull then
6468 Typing_solver.non_null env p ty
6469 else
6470 let r = Reason.Rwitness (get_pos ty) in
6471 Inter.intersect env r ty (MakeType.null r)
6473 refine_lvalue_type env te ~refine
6475 and condition_isset env = function
6476 | (_, Aast.Array_get (x, _)) -> condition_isset env x
6477 | v -> condition_nullity ~nonnull:true env v
6480 * Build an environment for the true or false branch of
6481 * conditional statements.
6483 and condition
6484 ?lhs_of_null_coalesce env tparamet ((((p, ty) as pty), e) as te : Tast.expr)
6486 let condition = condition ?lhs_of_null_coalesce in
6487 match e with
6488 | Aast.True when not tparamet ->
6489 (LEnv.drop_cont env C.Next, Local_id.Set.empty)
6490 | Aast.False when tparamet -> (LEnv.drop_cont env C.Next, Local_id.Set.empty)
6491 | Aast.Call ((_, Aast.Id (_, func)), _, [param], None)
6492 when String.equal SN.PseudoFunctions.isset func
6493 && tparamet
6494 && not (Env.is_strict env) ->
6495 condition_isset env param
6496 | Aast.Call ((_, Aast.Id (_, func)), _, [te], None)
6497 when String.equal SN.StdlibFunctions.is_null func ->
6498 condition_nullity ~nonnull:(not tparamet) env te
6499 | Aast.Binop ((Ast_defs.Eqeq | Ast_defs.Eqeqeq), (_, Aast.Null), e)
6500 | Aast.Binop ((Ast_defs.Eqeq | Ast_defs.Eqeqeq), e, (_, Aast.Null)) ->
6501 condition_nullity ~nonnull:(not tparamet) env e
6502 | Aast.Lvar _
6503 | Aast.Obj_get _
6504 | Aast.Class_get _
6505 | Aast.Binop (Ast_defs.Eq None, _, _) ->
6506 let (env, ety) = Env.expand_type env ty in
6507 (match get_node ety with
6508 | Tprim Tbool -> (env, Local_id.Set.empty)
6509 | _ -> condition_nullity ~nonnull:tparamet env te)
6510 | Aast.Binop (((Ast_defs.Diff | Ast_defs.Diff2) as op), e1, e2) ->
6511 let op =
6512 if Ast_defs.(equal_bop op Diff) then
6513 Ast_defs.Eqeq
6514 else
6515 Ast_defs.Eqeqeq
6517 condition env (not tparamet) (pty, Aast.Binop (op, e1, e2))
6518 | Aast.Id (_, s) when SN.Rx.is_enabled s ->
6519 (* when Rx\IS_ENABLED is false - switch env to non-reactive *)
6520 let env =
6521 if not tparamet then
6522 Env.set_env_reactive env Nonreactive
6523 else
6526 (env, Local_id.Set.empty)
6527 (* Conjunction of conditions. Matches the two following forms:
6528 if (cond1 && cond2)
6529 if (!(cond1 || cond2))
6531 | Aast.Binop (((Ast_defs.Ampamp | Ast_defs.Barbar) as bop), e1, e2)
6532 when Bool.equal tparamet Ast_defs.(equal_bop bop Ampamp) ->
6533 let (env, lset1) = condition env tparamet e1 in
6534 (* This is necessary in case there is an assignment in e2
6535 * We essentially redo what has been undone in the
6536 * `Binop (Ampamp|Barbar)` case of `expr` *)
6537 let (env, _, _) = expr env (Tast.to_nast_expr e2) in
6538 let (env, lset2) = condition env tparamet e2 in
6539 (env, Local_id.Set.union lset1 lset2)
6540 (* Disjunction of conditions. Matches the two following forms:
6541 if (cond1 || cond2)
6542 if (!(cond1 && cond2))
6544 | Aast.Binop (((Ast_defs.Ampamp | Ast_defs.Barbar) as bop), e1, e2)
6545 when Bool.equal tparamet Ast_defs.(equal_bop bop Barbar) ->
6546 let (env, lset1, lset2) =
6547 branch
6549 (fun env ->
6550 (* Either cond1 is true and we don't know anything about cond2... *)
6551 condition env tparamet e1)
6552 (fun env ->
6553 (* ... Or cond1 is false and therefore cond2 must be true *)
6554 let (env, _lset) = condition env (not tparamet) e1 in
6555 (* Similarly to the conjunction case, there might be an assignment in
6556 cond2 which we must account for. Again we redo what has been undone in
6557 the `Binop (Ampamp|Barbar)` case of `expr` *)
6558 let (env, _, _) = expr env (Tast.to_nast_expr e2) in
6559 condition env tparamet e2)
6561 (env, Local_id.Set.union lset1 lset2)
6562 | Aast.Call (((p, _), Aast.Id (_, f)), _, [lv], None)
6563 when tparamet && String.equal f SN.StdlibFunctions.is_dict_or_darray ->
6564 safely_refine_is_array env `HackDictOrDArray p f lv
6565 | Aast.Call (((p, _), Aast.Id (_, f)), _, [lv], None)
6566 when tparamet && String.equal f SN.StdlibFunctions.is_vec_or_varray ->
6567 safely_refine_is_array env `HackVecOrVArray p f lv
6568 | Aast.Call (((p, _), Aast.Id (_, f)), _, [lv], None)
6569 when tparamet && String.equal f SN.StdlibFunctions.is_any_array ->
6570 safely_refine_is_array env `AnyArray p f lv
6571 | Aast.Call (((p, _), Aast.Id (_, f)), _, [lv], None)
6572 when tparamet && String.equal f SN.StdlibFunctions.is_php_array ->
6573 safely_refine_is_array env `PHPArray p f lv
6574 | Aast.Call
6575 ( (_, Aast.Class_const ((_, Aast.CI (_, class_name)), (_, method_name))),
6577 [shape; field],
6578 None )
6579 when tparamet
6580 && String.equal class_name SN.Shapes.cShapes
6581 && String.equal method_name SN.Shapes.keyExists ->
6582 key_exists env p shape field
6583 | Aast.Unop (Ast_defs.Unot, e) -> condition env (not tparamet) e
6584 | Aast.Is (ivar, h) when is_instance_var (Tast.to_nast_expr ivar) ->
6585 let ety_env =
6586 { (Phase.env_with_self env) with from_class = Some CIstatic }
6588 let (env, hint_ty) = Phase.localize_hint ~ety_env env h in
6589 let reason = Reason.Ris (fst h) in
6590 let refine_type env hint_ty =
6591 let (ivar_pos, ivar_ty) = fst ivar in
6592 let (env, ivar) = get_instance_var env (Tast.to_nast_expr ivar) in
6593 let (env, hint_ty) =
6594 class_for_refinement env p reason ivar_pos ivar_ty hint_ty
6596 let (env, refined_ty) = Inter.intersect env reason ivar_ty hint_ty in
6597 (set_local env ivar refined_ty, Local_id.Set.singleton (snd ivar))
6599 let (env, hint_ty) =
6600 if not tparamet then
6601 Inter.non env reason hint_ty ~approx:TUtils.ApproxUp
6602 else
6603 (env, hint_ty)
6605 refine_type env hint_ty
6606 | _ -> (env, Local_id.Set.empty)
6608 (** Transform a hint like `A<_>` to a localized type like `A<T#1>` for refinment of
6609 an instance variable. ivar_ty is the previous type of that instance variable. *)
6610 and class_for_refinement env p reason ivar_pos ivar_ty hint_ty =
6611 let (env, hint_ty) = Env.expand_type env hint_ty in
6612 match (get_node ivar_ty, get_node hint_ty) with
6613 | (_, Tclass (((_, cid) as _c), _, tyl)) ->
6614 begin
6615 match Env.get_class env cid with
6616 | Some class_info ->
6617 let (env, tparams_with_new_names, tyl_fresh) =
6618 generate_fresh_tparams env class_info reason tyl
6620 safely_refine_class_type
6624 class_info
6625 ivar_ty
6626 hint_ty
6627 reason
6628 tparams_with_new_names
6629 tyl_fresh
6630 | None -> (env, mk (Reason.Rwitness ivar_pos, Tobject))
6632 | (Ttuple ivar_tyl, Ttuple hint_tyl)
6633 when Int.equal (List.length ivar_tyl) (List.length hint_tyl) ->
6634 let (env, tyl) =
6635 List.map2_env env ivar_tyl hint_tyl (fun env ivar_ty hint_ty ->
6636 class_for_refinement env p reason ivar_pos ivar_ty hint_ty)
6638 (env, MakeType.tuple reason tyl)
6639 | _ -> (env, hint_ty)
6641 (** If we are dealing with a refinement like
6642 $x is MyClass<A, B>
6643 then class_info is the class info of MyClass and hint_tyl corresponds
6644 to A, B. *)
6645 and generate_fresh_tparams env class_info reason hint_tyl =
6646 let tparams_len = List.length (Cls.tparams class_info) in
6647 let hint_tyl = List.take hint_tyl tparams_len in
6648 let pad_len = tparams_len - List.length hint_tyl in
6649 let hint_tyl =
6650 List.map hint_tyl (fun x -> Some x) @ List.init pad_len (fun _ -> None)
6652 let replace_wildcard env hint_ty tp =
6653 let {
6654 tp_name = (_, tparam_name);
6655 tp_reified = reified;
6656 tp_user_attributes;
6661 let enforceable =
6662 Attributes.mem SN.UserAttributes.uaEnforceable tp_user_attributes
6664 let newable =
6665 Attributes.mem SN.UserAttributes.uaNewable tp_user_attributes
6667 match hint_ty with
6668 | Some ty ->
6669 begin
6670 match get_node ty with
6671 | Tgeneric (name, _targs) when Env.is_fresh_generic_parameter name ->
6672 (* TODO(T69551141) handle type arguments above and below *)
6673 (env, (Some (tp, name), MakeType.generic reason name))
6674 | _ -> (env, (None, ty))
6676 | None ->
6677 let (env, new_name) =
6678 Env.add_fresh_generic_parameter
6680 tparam_name
6681 ~reified
6682 ~enforceable
6683 ~newable
6685 (* TODO(T69551141) handle type arguments for Tgeneric *)
6686 (env, (Some (tp, new_name), MakeType.generic reason new_name))
6688 let (env, tparams_and_tyl) =
6689 List.map2_env env hint_tyl (Cls.tparams class_info) ~f:replace_wildcard
6691 let (tparams_with_new_names, tyl_fresh) = List.unzip tparams_and_tyl in
6692 (env, tparams_with_new_names, tyl_fresh)
6694 and safely_refine_class_type
6697 class_name
6698 class_info
6699 ivar_ty
6700 obj_ty
6701 reason
6702 (tparams_with_new_names : (decl_tparam * string) option list)
6703 tyl_fresh =
6704 (* Type of variable in block will be class name
6705 * with fresh type parameters *)
6706 let obj_ty =
6707 mk (get_reason obj_ty, Tclass (class_name, Nonexact, tyl_fresh))
6709 let tparams = Cls.tparams class_info in
6710 (* Add in constraints as assumptions on those type parameters *)
6711 let ety_env =
6713 type_expansions = [];
6714 substs = Subst.make_locl tparams tyl_fresh;
6715 this_ty = obj_ty;
6716 (* In case `this` appears in constraints *)
6717 from_class = None;
6718 quiet = true;
6719 on_error = Errors.unify_error_at p;
6722 let add_bounds env (t, ty_fresh) =
6723 List.fold_left t.tp_constraints ~init:env ~f:(fun env (ck, ty) ->
6724 (* Substitute fresh type parameters for
6725 * original formals in constraint *)
6726 let (env, ty) = Phase.localize ~ety_env env ty in
6727 SubType.add_constraint p env ck ty_fresh ty)
6729 let env =
6730 List.fold_left (List.zip_exn tparams tyl_fresh) ~f:add_bounds ~init:env
6732 (* Finally, if we have a class-test on something with static classish type,
6733 * then we can chase the hierarchy and decompose the types to deduce
6734 * further assumptions on type parameters. For example, we might have
6735 * class B<Tb> { ... }
6736 * class C extends B<int>
6737 * and have obj_ty = C and x_ty = B<T> for a generic parameter Aast.
6738 * Then SubType.add_constraint will deduce that T=int and add int as
6739 * both lower and upper bound on T in env.lenv.tpenv
6741 let (env, supertypes) = TUtils.get_concrete_supertypes env ivar_ty in
6742 let env =
6743 List.fold_left supertypes ~init:env ~f:(fun env ty ->
6744 SubType.add_constraint p env Ast_defs.Constraint_as obj_ty ty)
6746 (* It's often the case that the fresh name isn't necessary. For
6747 * example, if C<T> extends B<T>, and we have $x:B<t> for some type t
6748 * then $x is C should refine to $x:C<t>.
6749 * We take a simple approach:
6750 * For a fresh type parameter T#1, if
6751 * - There is an eqality constraint T#1 = t,
6752 * - T#1 is covariant, and T#1 has upper bound t (or mixed if absent)
6753 * - T#1 is contravariant, and T#1 has lower bound t (or nothing if absent)
6754 * then replace T#1 with t.
6755 * This is done in Type_parameter_env_ops.simplify_tpenv
6757 let (env, tparam_substs) =
6758 Type_parameter_env_ops.simplify_tpenv
6760 (List.zip_exn tparams_with_new_names tyl_fresh)
6761 reason
6763 let tyl_fresh =
6764 List.map2_exn tyl_fresh tparams_with_new_names ~f:(fun orig_ty tparam_opt ->
6765 match tparam_opt with
6766 | None -> orig_ty
6767 | Some (_tp, name) -> SMap.find name tparam_substs)
6769 let obj_ty_simplified =
6770 mk (get_reason obj_ty, Tclass (class_name, Nonexact, tyl_fresh))
6772 (env, obj_ty_simplified)
6774 and is_instance_var = function
6775 | (_, (Lvar _ | This | Dollardollar _)) -> true
6776 | (_, Obj_get ((_, This), (_, Id _), _, _)) -> true
6777 | (_, Obj_get ((_, Lvar _), (_, Id _), _, _)) -> true
6778 | (_, Class_get (_, _, _)) -> true
6779 | _ -> false
6781 and get_instance_var env = function
6782 | (p, Class_get ((_, cname), CGstring (_, member_name), _)) ->
6783 let (env, local) = Env.FakeMembers.make_static env cname member_name p in
6784 (env, (p, local))
6785 | ( p,
6786 Obj_get
6787 ((((_, This) | (_, Lvar _)) as obj), (_, Id (_, member_name)), _, _) )
6789 let (env, local) = Env.FakeMembers.make env obj member_name p in
6790 (env, (p, local))
6791 | (_, Dollardollar (p, x))
6792 | (_, Lvar (p, x)) ->
6793 (env, (p, x))
6794 | (p, This) -> (env, (p, this))
6795 | _ -> failwith "Should only be called when is_instance_var is true"
6797 (* Refine type for is_array, is_vec, is_keyset and is_dict tests
6798 * `pred_name` is the function name itself (e.g. 'is_vec')
6799 * `p` is position of the function name in the source
6800 * `arg_expr` is the argument to the function
6802 and safely_refine_is_array env ty p pred_name arg_expr =
6803 refine_lvalue_type env arg_expr ~refine:(fun env arg_ty ->
6804 let r = Reason.Rpredicated (p, pred_name) in
6805 let (env, tarrkey_name) =
6806 Env.add_fresh_generic_parameter
6808 "Tk"
6809 ~reified:Erased
6810 ~enforceable:false
6811 ~newable:false
6813 (* TODO(T69551141) handle type arguments for Tgeneric *)
6814 let tarrkey = MakeType.generic r tarrkey_name in
6815 let env =
6816 SubType.add_constraint
6819 Ast_defs.Constraint_as
6820 tarrkey
6821 (MakeType.arraykey r)
6823 let (env, tfresh_name) =
6824 Env.add_fresh_generic_parameter
6827 ~reified:Erased
6828 ~enforceable:false
6829 ~newable:false
6831 (* TODO(T69551141) handle type arguments for Tgeneric *)
6832 let tfresh = MakeType.generic r tfresh_name in
6833 (* If we're refining the type for `is_array` we have a slightly more
6834 * involved process. Let's separate out that logic so we can re-use it.
6836 let array_ty =
6837 let safe_isarray_enabled =
6838 TypecheckerOptions.experimental_feature_enabled
6839 (Env.get_tcopt env)
6840 TypecheckerOptions.experimental_isarray
6842 let tk = MakeType.arraykey Reason.(Rvarray_or_darray_key (to_pos r)) in
6843 let tv =
6844 if safe_isarray_enabled then
6845 tfresh
6846 else
6847 mk (r, TUtils.tany env)
6849 MakeType.varray_or_darray r tk tv
6851 (* This is the refined type of e inside the branch *)
6852 let hint_ty =
6853 match ty with
6854 | `HackDict -> MakeType.dict r tarrkey tfresh
6855 | `HackVec -> MakeType.vec r tfresh
6856 | `HackKeyset -> MakeType.keyset r tarrkey
6857 | `PHPArray -> array_ty
6858 | `AnyArray -> MakeType.any_array r tarrkey tfresh
6859 | `HackDictOrDArray ->
6860 MakeType.union
6862 [MakeType.dict r tarrkey tfresh; MakeType.darray r tarrkey tfresh]
6863 | `HackVecOrVArray ->
6864 MakeType.union r [MakeType.vec r tfresh; MakeType.varray r tfresh]
6866 let ((arg_pos, _), _) = arg_expr in
6867 let (env, hint_ty) =
6868 class_for_refinement env p r arg_pos arg_ty hint_ty
6870 (* Add constraints on generic parameters that must
6871 * hold for refined_ty <:arg_ty. For example, if arg_ty is Traversable<T>
6872 * and refined_ty is keyset<T#1> then we know T#1 <: T.
6873 * See analogous code in safely_refine_class_type.
6875 let (env, supertypes) = TUtils.get_concrete_supertypes env arg_ty in
6876 let env =
6877 List.fold_left supertypes ~init:env ~f:(fun env ty ->
6878 SubType.add_constraint p env Ast_defs.Constraint_as hint_ty ty)
6880 Inter.intersect ~r env hint_ty arg_ty)
6882 and key_exists env pos shape field =
6883 let field = Tast.to_nast_expr field in
6884 refine_lvalue_type env shape ~refine:(fun env shape_ty ->
6885 match TUtils.shape_field_name env field with
6886 | None -> (env, shape_ty)
6887 | Some field_name ->
6888 Typing_shapes.refine_shape field_name pos env shape_ty)
6890 and string2 env idl =
6891 let (env, tel) =
6892 List.fold_left idl ~init:(env, []) ~f:(fun (env, tel) x ->
6893 let (env, te, ty) = expr env x in
6894 let p = fst x in
6895 let env = Typing_substring.sub_string p env ty in
6896 (env, te :: tel))
6898 (env, List.rev tel)
6900 and user_attribute env ua =
6901 let (env, typed_ua_params) =
6902 List.map_env env ua.ua_params (fun env e ->
6903 let (env, te, _) = expr env e in
6904 (env, te))
6906 (env, { Aast.ua_name = ua.ua_name; Aast.ua_params = typed_ua_params })
6908 and file_attributes env file_attrs =
6909 (* Disable checking of error positions, as file attributes have spans that
6910 * aren't subspans of the class or function into which they are copied *)
6911 Errors.run_with_span Pos.none @@ fun () ->
6912 let uas = List.concat_map ~f:(fun fa -> fa.fa_user_attributes) file_attrs in
6913 let env = attributes_check_def env SN.AttributeKinds.file uas in
6914 List.map_env env file_attrs (fun env fa ->
6915 let (env, user_attributes) =
6916 List.map_env env fa.fa_user_attributes user_attribute
6918 let env = set_tcopt_unstable_features env fa in
6919 ( env,
6921 Aast.fa_user_attributes = user_attributes;
6922 Aast.fa_namespace = fa.fa_namespace;
6923 } ))
6925 and type_param env t =
6926 let env =
6927 attributes_check_def env SN.AttributeKinds.typeparam t.tp_user_attributes
6929 let (env, user_attributes) =
6930 List.map_env env t.tp_user_attributes user_attribute
6932 let (env, tp_parameters) = List.map_env env t.tp_parameters type_param in
6933 ( env,
6935 Aast.tp_variance = t.tp_variance;
6936 Aast.tp_name = t.tp_name;
6937 Aast.tp_parameters;
6938 Aast.tp_constraints = t.tp_constraints;
6939 Aast.tp_reified = reify_kind t.tp_reified;
6940 Aast.tp_user_attributes = user_attributes;
6943 and typedef_def ctx typedef =
6944 let env = EnvFromDef.typedef_env ~origin:Decl_counters.TopLevel ctx typedef in
6945 let env =
6946 Phase.localize_and_add_ast_generic_parameters_and_where_constraints
6947 (fst typedef.t_name)
6949 typedef.t_tparams
6952 Typing_check_decls.typedef env typedef;
6953 Typing_variance.typedef env (snd typedef.t_name);
6954 let {
6955 t_annotation = ();
6956 t_name = (t_pos, t_name);
6957 t_tparams = _;
6958 t_constraint = tcstr;
6959 t_kind = hint;
6960 t_user_attributes = _;
6961 t_vis = _;
6962 t_mode = _;
6963 t_namespace = _;
6964 t_span = _;
6965 t_emit_id = _;
6967 typedef
6969 let ty = Decl_hint.hint env.decl_env hint in
6970 (* We want to report cycles through the definition *)
6971 let (env, ty) =
6972 Phase.localize_with_self env ~pos:t_pos ~report_cycle:(t_pos, t_name) ty
6974 let env =
6975 match tcstr with
6976 | Some tcstr ->
6977 let cstr = Decl_hint.hint env.decl_env tcstr in
6978 let (env, cstr) = Phase.localize_with_self ~pos:t_pos env cstr in
6979 Typing_ops.sub_type
6980 t_pos
6981 Reason.URnewtype_cstr
6984 cstr
6985 Errors.newtype_alias_must_satisfy_constraint
6986 | _ -> env
6988 let env =
6989 match hint with
6990 | (pos, Hshape { nsi_allows_unknown_fields = _; nsi_field_map }) ->
6991 let get_name sfi = sfi.sfi_name in
6992 check_shape_keys_validity env pos (List.map ~f:get_name nsi_field_map)
6993 | _ -> env
6995 let env =
6996 attributes_check_def
6998 SN.AttributeKinds.typealias
6999 typedef.t_user_attributes
7001 let (env, tparams) = List.map_env env typedef.t_tparams type_param in
7002 let (env, user_attributes) =
7003 List.map_env env typedef.t_user_attributes user_attribute
7006 Aast.t_annotation = Env.save (Env.get_tpenv env) env;
7007 Aast.t_name = typedef.t_name;
7008 Aast.t_mode = typedef.t_mode;
7009 Aast.t_vis = typedef.t_vis;
7010 Aast.t_user_attributes = user_attributes;
7011 Aast.t_constraint = typedef.t_constraint;
7012 Aast.t_kind = typedef.t_kind;
7013 Aast.t_tparams = tparams;
7014 Aast.t_namespace = typedef.t_namespace;
7015 Aast.t_span = typedef.t_span;
7016 Aast.t_emit_id = typedef.t_emit_id;
7019 (* Calls the method of a class, but allows the f callback to override the
7020 * return value type *)
7021 and overload_function
7022 make_call fpos p env (cpos, class_id) method_id el unpacked_element f =
7023 let (env, _tal, tcid, ty) =
7024 static_class_id ~check_constraints:false cpos env [] class_id
7026 let (env, _tel, _) = exprs env el in
7027 let (env, (fty, tal)) =
7028 class_get
7029 ~is_method:true
7030 ~is_const:false
7031 ~coerce_from_ty:None
7034 method_id
7035 class_id
7037 let (env, (tel, typed_unpack_element, res)) =
7038 call ~expected:None p env fty el unpacked_element
7040 let (env, ty) = f env fty res el in
7041 let (env, fty) = Env.expand_type env fty in
7042 let fty =
7043 map_ty fty ~f:(function
7044 | Tfun ft -> Tfun { ft with ft_ret = MakeType.unenforced ty }
7045 | ty -> ty)
7047 let te = Tast.make_typed_expr fpos fty (Aast.Class_const (tcid, method_id)) in
7048 make_call env te tal tel typed_unpack_element ty
7050 and update_array_type ?lhs_of_null_coalesce p env e1 valkind =
7051 match valkind with
7052 | `lvalue
7053 | `lvalue_subexpr ->
7054 let (env, te1, ty1) =
7055 raw_expr ~valkind:`lvalue_subexpr ~check_defined:true env e1
7057 begin
7058 match e1 with
7059 | (_, Lvar (_, x)) ->
7060 (* type_mapper has updated the type in ty1 typevars, but we
7061 need to update the local variable type too *)
7062 let env = set_valid_rvalue p env x ty1 in
7063 (env, te1, ty1)
7064 | _ -> (env, te1, ty1)
7066 | _ -> raw_expr ?lhs_of_null_coalesce env e1
7068 (* External API *)
7069 let expr ?expected env e =
7070 Typing_env.with_origin2 env Decl_counters.Body (fun env ->
7071 expr ?expected env e)
7073 let stmt env st =
7074 Typing_env.with_origin env Decl_counters.Body (fun env -> stmt env st)