Remove side-effecting variants from `typing_phase`
[hiphop-php.git] / hphp / hack / src / typing / typing_param.ml
bloba077592e123b9b0f7aa3073aba7e30b2ea668605
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 open Hh_prelude
11 open Aast
12 open Common
13 open Typing_defs
14 open Typing_helpers
15 module Reason = Typing_reason
16 module MakeType = Typing_make_type
17 module Phase = Typing_phase
18 module TUtils = Typing_utils
19 module Env = Typing_env
21 let enforce_param_not_disposable env param ty =
22 if has_accept_disposable_attribute param then
23 None
24 else
25 Option.map
26 (Typing_disposable.is_disposable_type env ty)
27 ~f:(fun class_name ->
28 Typing_error.Primary.Invalid_disposable_hint
29 { pos = param.param_pos; class_name = Utils.strip_ns class_name })
31 let check_param_has_hint env param ty =
32 let prim_err_opt =
33 if Env.is_hhi env then
34 None
35 else if Option.is_none (hint_of_type_hint param.param_type_hint) then
36 Some
37 (if param.param_is_variadic then
38 Typing_error.Primary.Expecting_type_hint_variadic param.param_pos
39 else
40 Typing_error.Primary.Expecting_type_hint param.param_pos)
41 else
42 (* We do not permit hints to implement IDisposable or IAsyncDisposable *)
43 enforce_param_not_disposable env param ty
45 Option.iter prim_err_opt ~f:(fun err ->
46 Errors.add_typing_error @@ Typing_error.primary err)
48 (* This function is used to determine the type of an argument.
49 * When we want to type-check the body of a function, we need to
50 * introduce the type of the arguments of the function in the environment
51 * Let's take an example, we want to check the code of foo:
53 * function foo(int $x): int {
54 * // CALL TO make_param_type on (int $x)
55 * // Now we know that the type of $x is int
57 * return $x; // in the environment $x is an int, the code is correct
58 * }
60 * When we localize, we want to resolve to "static" or "$this" depending on
61 * the context. Even though we are passing in CIstatic, resolve_with_class_id
62 * is smart enough to know what to do. Why do this? Consider the following
64 * abstract class C {
65 * abstract const type T;
67 * private this::T $val;
69 * final public function __construct(this::T $x) {
70 * $this->val = $x;
71 * }
73 * public static function create(this::T $x): this {
74 * return new static($x);
75 * }
76 * }
78 * class D extends C { const type T = int; }
80 * In __construct() we want to be able to assign $x to $this->val. The type of
81 * $this->val will expand to '$this::T', so we need $x to also be '$this::T'.
82 * We can do this soundly because when we construct a new class such as,
83 * 'new D(0)' we can determine the late static bound type (D) and resolve
84 * 'this::T' to 'D::T' which is int.
86 * A similar line of reasoning is applied for the static method create.
88 let make_param_local_ty ~dynamic_mode env decl_hint param =
89 let r = Reason.Rwitness param.param_pos in
90 let ((env, ty_err_opt), ty) =
91 match decl_hint with
92 | None -> ((env, None), mk (r, TUtils.tany env))
93 | Some ty ->
94 let { et_type = ty; et_enforced } =
95 Typing_enforceability.compute_enforced_and_pessimize_ty
96 ~explicitly_untrusted:param.param_is_variadic
97 env
100 (* If a non-inout parameter hint has the form ~t, where t is enforced,
101 * then we know that the parameter has type t after enforcement.
103 let ty =
104 match (get_node ty, et_enforced, param.param_callconv) with
105 | (Tlike ty, Enforced, Ast_defs.Pnormal) -> ty
106 | _ -> ty
108 Phase.localize_no_subst env ~ignore_errors:false ty
110 Option.iter ty_err_opt ~f:Errors.add_typing_error;
111 let ty =
112 match get_node ty with
113 | t when param.param_is_variadic ->
114 (* when checking the body of a function with a variadic
115 * argument, "f(C ...$args)", $args is a varray<C> *)
116 let r = Reason.Rvar_param param.param_pos in
117 let arr_values = mk (r, t) in
118 MakeType.varray r arr_values
119 | _ -> ty
121 (* Don't check (again) for existence of hint in dynamic mode *)
122 if not dynamic_mode then check_param_has_hint env param ty;
123 (env, ty)
125 let make_param_local_tys ~dynamic_mode env decl_tys params =
126 List.zip_exn params decl_tys
127 |> List.map_env env ~f:(fun env (param, hint) ->
128 let ty =
129 if dynamic_mode then
130 let dyn_ty =
131 Typing_make_type.dynamic
132 (Reason.Rsupport_dynamic_type
133 (Pos_or_decl.of_raw_pos param.param_pos))
135 match hint with
136 | Some ty when Typing_enforceability.is_enforceable env ty ->
137 Some
138 (Typing_make_type.intersection
139 (Reason.Rsupport_dynamic_type Pos_or_decl.none)
140 [ty; dyn_ty])
141 | _ -> Some dyn_ty
142 else
143 hint
145 make_param_local_ty ~dynamic_mode env ty param)