Introduce __ReturnsVoidToRx
[hiphop-php.git] / hphp / hack / src / typing / typing_taccess.ml
blob736f22e8d0a20a8be8e64f70de0174bdfc477b05
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_core
11 open Typing_defs
12 open Typing_dependent_type
13 open Utils
15 module Reason = Typing_reason
16 module Env = Typing_env
17 module Phase = Typing_phase
18 module TySet = Typing_set
20 type env = {
21 tenv : Env.env;
22 ety_env : Phase.env;
23 (* A trail of all the type constants we have expanded. Used primarily for
24 * error reporting
26 trail : dependent_type list;
28 (* A list of dependent we have encountered while expanding a type constant.
29 * After expanding a type constant we can choose either the assigned type or
30 * the constrained type. If we choose the assigned type, the result will not
31 * be expression dependent so this list will be set to empty. However, if it
32 * is the constrained type then the final type will also be a dependent type.
34 dep_tys : (Reason.t * dependent_type) list;
35 (* The remaining type constants we need to expand *)
36 ids : Nast.sid list;
38 (* A list of generics we've seen while expanding. *)
39 gen_seen : TySet.t;
41 (* The identifiers for each class and typeconst pair seen while expanding,
42 * along with the location where the typeconst was referenced. *)
43 typeconsts_seen : (string * string * Pos.t) list;
46 let empty_env env ety_env ids = {
47 tenv = env;
48 ety_env = ety_env;
49 trail = [];
50 dep_tys = [];
51 ids = ids;
52 gen_seen = TySet.empty;
53 typeconsts_seen = [];
56 let rec expand_with_env ety_env env reason root ids =
57 let tenv, env, ty = expand_with_env_ ety_env env reason root ids in
58 tenv, (env.ety_env, ty)
60 and expand_with_env_ ety_env env reason root ids =
61 let env = empty_env env ety_env ids in
62 let env, (root_r, root_ty) = expand env root in
63 let trail = List.rev_map env.trail (compose strip_ns ExprDepTy.to_string) in
64 let reason_func r =
65 let r = match r with
66 | Reason.Rexpr_dep_type (_, p, e) ->
67 Reason.Rexpr_dep_type (root_r, p, e)
68 | _ -> r in
69 Reason.Rtype_access(reason, trail, r) in
70 let ty = reason_func root_r, root_ty in
71 let deps = List.map env.dep_tys (fun (x, y) -> reason_func x, y) in
72 let tenv, ty = ExprDepTy.apply env.tenv deps ty in
73 tenv, env, ty
75 and referenced_typeconsts tenv ety_env r (root, ids) =
76 let tenv, (ety_env, root) = Phase.localize_with_env ~ety_env tenv root in
77 let _, env, _ = expand_with_env_ ety_env tenv r root ids in
78 List.rev env.typeconsts_seen
80 (* The root of a type access is a type. When expanding a type access this type
81 * needs to resolve to the name of a class so we can look up if a given type
82 * constant is defined in the class.
84 * We also need to track what expansions have already taken place to make sure
85 * we do not recurse infinitely.
87 and expand env (root_reason, root_ty as root) =
88 match env.ids with
89 | [] ->
90 env, root
91 | head::tail -> begin match root_ty with
92 | Tany | Terr -> env, root
93 | Tabstract (AKdependent (`cls _, []), Some ty)
94 | Tabstract (AKnewtype (_, _), Some ty) | Toption ty -> expand env ty
95 | Tclass ((class_pos, class_name), _) ->
96 let env, ty =
97 create_root_from_type_constant
98 env class_pos class_name root head in
99 expand { env with ids = tail } ty
100 | Tabstract (AKgeneric s, _) ->
101 let dep_ty = generic_to_dep_ty s in
102 let env =
103 { env with
104 dep_tys = (root_reason, dep_ty)::env.dep_tys;
105 gen_seen = TySet.add root env.gen_seen;
106 } in
107 let upper_bounds = Env.get_upper_bounds env.tenv s in
108 (* Ignore upper bounds that are equal to ones we've seen, to avoid
109 an infinite loop
111 let upper_bounds = upper_bounds - env.gen_seen
113 let upper_bounds = TySet.diff upper_bounds env.gen_seen in
114 let env, tyl = List.map_env env (TySet.elements upper_bounds)
115 begin fun prev_env ty ->
116 let env, ty = expand env ty in
117 (* If ty here involves a type access, we have to use
118 the current environment's dependent types. Otherwise,
119 we throw away type access information.
121 let tenv, ty = ExprDepTy.apply env.tenv env.dep_tys ty in
122 { prev_env with tenv }, ty
123 end in
124 begin match tyl with
125 | [] ->
126 let pos, tconst = head in
127 let ty = Typing_print.error root_ty in
128 Errors.non_object_member tconst (Reason.to_pos root_reason) ty pos;
129 env, (root_reason, Terr)
130 | ty::_ ->
131 { env with dep_tys = [] }, ty
133 | Tabstract (AKdependent dep_ty, Some ty) ->
134 let env =
135 { env with
136 dep_tys = (root_reason, dep_ty)::env.dep_tys } in
137 expand env ty
138 | Tunresolved tyl ->
139 let env, tyl = List.map_env env tyl begin fun prev_env ty ->
140 let env, ty = expand env ty in
141 (* If ty here involves a type access, we have to use
142 the current environment's dependent types. Otherwise,
143 we throw away type access information.
145 let tenv, ty = ExprDepTy.apply env.tenv env.dep_tys ty in
146 { prev_env with tenv }, ty
147 end in
148 { env with dep_tys = [] } , (root_reason, Tunresolved tyl)
149 | Tvar _ ->
150 let tenv, ty =
151 Env.expand_type env.tenv root in
152 let env = { env with tenv = tenv } in
153 expand env ty
154 | Tanon _ | Tobject | Tmixed | Tnonnull | Tprim _ | Tshape _ | Ttuple _
155 | Tarraykind _ | Tfun _ | Tabstract (_, _) | Tdynamic ->
156 let pos, tconst = head in
157 let ty = Typing_print.error root_ty in
158 Errors.non_object_member tconst (Reason.to_pos root_reason) ty pos;
159 env, (root_reason, Terr)
161 and generic_to_dep_ty s =
162 let regexp = Str.regexp "::" in
163 let res = Str.split regexp s in
164 match res with
165 | name::tys -> `cls name, tys
166 | [] -> `cls s, []
167 (* The function takes a "step" forward in the expansion. We look up the type
168 * constant associated with the given class_name and create a new root type.
169 * A type constant has both a constraint type and assigned type. Which one we
170 * choose depends on if there are any dependent types set in the environment.
171 * If the root type is not a dependent type then we choose the assigned type,
172 * otherwise we choose the constraint type. If there is no constraint type then
173 * we choose the assigned type.
175 and create_root_from_type_constant env class_pos class_name root_ty (pos, tconst) =
176 match get_typeconst env class_pos class_name pos tconst with
177 | None -> env, (fst root_ty, Typing_utils.tany env.tenv)
178 | Some (env, typeconst) ->
179 let env =
180 { env with
181 trail = (`cls class_name, List.map env.ids snd)::env.trail } in
182 (* The type constant itself may contain a 'this' type so we need to
183 * change the this_ty in the environment to be the root as an
184 * expression dependent type.
186 let tenv, new_this = ExprDepTy.apply env.tenv env.dep_tys root_ty in
187 let env = { env with tenv } in
188 let ety_env =
189 { env.ety_env with
190 this_ty = new_this;
191 from_class = None; } in
192 begin
193 match typeconst with
194 | { ttc_type = Some ty; _ }
195 when typeconst.ttc_constraint = None || env.dep_tys = [] ->
196 let tenv, ty = Phase.localize ~ety_env env.tenv ty in
197 { env with dep_tys = []; tenv = tenv }, ty
198 | {ttc_constraint = Some cstr; _} ->
199 let tenv, cstr = Phase.localize ~ety_env env.tenv cstr in
200 let dep_ty = Reason.Rwitness (fst typeconst.ttc_name),
201 (`cls class_name, [tconst]) in
202 (* Append the name of the expanded type constant to each dependent
203 * type.
205 let dep_tys =
206 List.map env.dep_tys (fun (r, (d, s)) -> r, (d, s @ [tconst])) in
207 { env with dep_tys = dep_ty::dep_tys; tenv = tenv }, cstr
208 | _ ->
209 let ty =
210 Reason.Rwitness (fst typeconst.ttc_name),
211 Tabstract (AKgeneric (class_name^"::"^tconst), None) in
212 let dep_tys =
213 List.map env.dep_tys (fun (r, (d, s)) -> r, (d, s @ [tconst])) in
214 { env with dep_tys = dep_tys }, ty
217 (* Looks up the type constant within the given class. This also checks for
218 * potential cycles by examining the expansions we have already performed.
220 and get_typeconst env class_pos class_name pos tconst =
222 let class_ = match Env.get_class env.tenv class_name with
223 | None ->
224 Errors.unbound_name_typing class_pos class_name;
225 raise Exit
226 | Some c -> c in
227 let typeconst = match Env.get_typeconst env.tenv class_ tconst with
228 | None ->
229 Errors.smember_not_found
230 `class_typeconst pos (class_.tc_pos, class_name) tconst `no_hint;
231 raise Exit
232 | Some tc -> tc in
233 let tc_tuple = (class_.tc_name, snd typeconst.ttc_name, pos) in
234 let env = {env with typeconsts_seen = tc_tuple :: env.typeconsts_seen} in
235 (* Check for cycles. We do this by combining the name of the current class
236 * with the remaining ids that we need to expand. If we encounter the same
237 * class name + ids that means we have entered a cycle.
239 let cur_tconst = `cls class_name, List.map env.ids snd in
240 let seen = ExprDepTy.to_string cur_tconst in
241 let type_expansions = (pos, seen)::env.ety_env.type_expansions in
242 if List.mem (List.map env.ety_env.type_expansions snd) seen then
243 begin
244 let seen = List.rev_map type_expansions snd in
245 Errors.cyclic_typeconst (fst typeconst.ttc_name) seen;
246 raise Exit
247 end;
248 (* If the class is final then we do not need to create dependent types
249 * because the type constant cannot be overridden by a child class
251 let env =
252 { env with
253 ety_env = { env.ety_env with type_expansions };
254 dep_tys = if class_.tc_final then [] else env.dep_tys;
255 } in
256 Some (env, typeconst)
257 with
258 Exit -> None
260 (*****************************************************************************)
261 (* Exporting *)
262 (*****************************************************************************)
264 let () = Typing_utils.expand_typeconst_ref := expand_with_env