log various type variable creation
[hiphop-php.git] / hphp / hack / src / typing / typing_taccess.ml
blob8164a75dce565ee302ab7d6a3ee3b569a9e0c93b
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 Core_kernel
11 open Common
12 open Typing_defs
13 open Typing_dependent_type
14 open Utils
16 module Reason = Typing_reason
17 module Env = Typing_env
18 module Log = Typing_log
19 module Phase = Typing_phase
20 module TySet = Typing_set
21 module TR = Typing_reactivity
22 module CT = Typing_subtype.ConditionTypes
23 module Cls = Typing_classes_heap
25 type env = {
26 tenv : Env.env;
27 ety_env : Phase.env;
28 (* A trail of all the type constants we have expanded. Used primarily for
29 * error reporting
31 trail : dependent_type list;
33 (* A list of dependent we have encountered while expanding a type constant.
34 * After expanding a type constant we can choose either the assigned type or
35 * the constrained type. If we choose the assigned type, the result will not
36 * be expression dependent so this list will be set to empty. However, if it
37 * is the constrained type then the final type will also be a dependent type.
39 dep_tys : (Reason.t * dependent_type) list;
40 (* The remaining type constants we need to expand *)
41 ids : Nast.sid list;
43 (* A list of generics we've seen while expanding. *)
44 gen_seen : TySet.t;
46 (* The identifiers for each class and typeconst pair seen while expanding,
47 * along with the location where the typeconst was referenced. *)
48 typeconsts_seen : (string * string * Pos.t) list;
51 let empty_env env ety_env ids = {
52 tenv = env;
53 ety_env = ety_env;
54 trail = [];
55 dep_tys = [];
56 ids = ids;
57 gen_seen = TySet.empty;
58 typeconsts_seen = [];
61 (** Expand a type constant access like A::T
62 If as_tyvar_with_cnstr is set, then return a fresh type variable which has
63 the same constraints as type constant T in A. Otherwise, return an
64 AKGeneric("A::T"). *)
65 let rec expand_with_env ety_env env ?(as_tyvar_with_cnstr = false) reason root ids =
66 let tenv, env, ty =
67 expand_with_env_ ety_env env ~as_tyvar_with_cnstr reason root ids in
68 tenv, (env.ety_env, ty)
70 and expand_with_env_ ety_env env ~as_tyvar_with_cnstr reason root ids =
71 let env = empty_env env ety_env ids in
72 let env, (root_r, root_ty) = expand env ~as_tyvar_with_cnstr root in
73 let trail = List.rev_map env.trail (compose strip_ns ExprDepTy.to_string) in
74 let reason_func r =
75 let r = match r with
76 | Reason.Rexpr_dep_type (_, p, e) ->
77 Reason.Rexpr_dep_type (root_r, p, e)
78 | _ -> r in
79 Reason.Rtype_access(reason, trail, r) in
80 let ty = reason_func root_r, root_ty in
81 let deps = List.map env.dep_tys (fun (x, y) -> reason_func x, y) in
82 let tenv, ty = ExprDepTy.apply env.tenv deps ty in
83 let tenv, ty =
84 (* if type constant has type this::ID and method has associated condition type ROOTCOND_TY
85 for the receiver - check if condition type has type constant at the same path.
86 If yes - attach a condition type ROOTCOND_TY::ID to a result type *)
87 begin match root, ids, TR.condition_type_from_reactivity (Env.env_reactivity tenv) with
88 | (_, Tabstract (AKdependent (`this, []), _)),
89 [_, id],
90 Some cond_ty ->
91 begin match CT.try_get_class_for_condition_type tenv cond_ty with
92 | Some (_, cls) when Cls.has_typeconst cls id ->
93 let cond_ty = (Reason.none, Taccess (cond_ty, ids)) in
94 Option.value (TR.try_substitute_type_with_condition tenv cond_ty ty)
95 ~default:(tenv, ty)
96 | _ -> tenv, ty
97 end
98 | _ -> tenv, ty
99 end in
101 tenv, env, ty
103 and referenced_typeconsts tenv ety_env r (root, ids) =
104 let tenv, (ety_env, root) = Phase.localize_with_env ~ety_env tenv root in
105 let _, env, _ = expand_with_env_ ety_env ~as_tyvar_with_cnstr:false tenv r root ids in
106 List.rev env.typeconsts_seen
108 (* The root of a type access is a type. When expanding a type access this type
109 * needs to resolve to the name of a class so we can look up if a given type
110 * constant is defined in the class.
112 * We also need to track what expansions have already taken place to make sure
113 * we do not recurse infinitely.
115 and expand env ~as_tyvar_with_cnstr root =
116 let expand = expand ~as_tyvar_with_cnstr in
117 let tenv, (root_reason, root_ty as root) = Env.expand_type env.tenv root in
118 let env = { env with tenv = tenv } in
119 match env.ids with
120 | [] ->
121 env, root
122 | head::tail -> begin match root_ty with
123 | Tany | Terr -> env, root
124 | Tabstract (AKdependent (`cls _, []), Some ty)
125 | Tabstract (AKnewtype (_, _), Some ty) | Toption ty -> expand env ty
126 | Tclass ((class_pos, class_name), _, tyl) ->
127 (* Legacy behaviour is to preserve exactness only on `this`
128 * and not through `this::T` *)
129 let env, ty =
130 create_root_from_type_constant
131 env class_pos class_name
132 (root_reason, Tclass ((class_pos, class_name), Nonexact, tyl))
133 head ~as_tyvar_with_cnstr in
134 expand { env with ids = tail } ty
135 | Tabstract (AKgeneric s, _) ->
136 let dep_ty = generic_to_dep_ty s in
137 let env =
138 { env with
139 dep_tys = (root_reason, dep_ty)::env.dep_tys;
140 gen_seen = TySet.add root env.gen_seen;
141 } in
142 let upper_bounds = Env.get_upper_bounds env.tenv s in
143 (* Ignore upper bounds that are equal to ones we've seen, to avoid
144 an infinite loop
146 let upper_bounds = upper_bounds - env.gen_seen
148 let upper_bounds = TySet.diff upper_bounds env.gen_seen in
149 let env, tyl = List.map_env env (TySet.elements upper_bounds)
150 begin fun prev_env ty ->
151 let env, ty = expand env ty in
152 (* If ty here involves a type access, we have to use
153 the current environment's dependent types. Otherwise,
154 we throw away type access information.
156 let tenv, ty = ExprDepTy.apply env.tenv env.dep_tys ty in
157 { prev_env with tenv }, ty
158 end in
159 begin match tyl with
160 | [] ->
161 let pos, tconst = head in
162 let ty = Typing_print.error env.tenv root in
163 Errors.non_object_member tconst (Reason.to_pos root_reason) ty pos;
164 env, (root_reason, Terr)
165 | ty::_ ->
166 { env with dep_tys = [] }, ty
168 | Tabstract (AKdependent dep_ty, Some ty) ->
169 let env =
170 { env with
171 dep_tys = (root_reason, dep_ty)::env.dep_tys } in
172 expand env ty
173 | Tunresolved tyl ->
174 let env, tyl = List.map_env env tyl begin fun prev_env ty ->
175 let env, ty = expand env ty in
176 (* If ty here involves a type access, we have to use
177 the current environment's dependent types. Otherwise,
178 we throw away type access information.
180 let tenv, ty = ExprDepTy.apply env.tenv env.dep_tys ty in
181 { prev_env with tenv }, ty
182 end in
183 { env with dep_tys = [] } , (root_reason, Tunresolved tyl)
184 | Tvar n ->
185 let tenv, ty = Typing_subtype_tconst.get_tyvar_type_const env.tenv n head in
186 expand { env with ids = tail; tenv } ty
187 | Tanon _ | Tobject | Tnonnull | Tprim _ | Tshape _ | Ttuple _
188 | Tarraykind _ | Tfun _ | Tabstract (_, _) | Tdynamic ->
189 let pos, tconst = head in
190 let ty = Typing_print.error env.tenv root in
191 Errors.non_object_member tconst (Reason.to_pos root_reason) ty pos;
192 env, (root_reason, Terr)
194 and generic_to_dep_ty s =
195 let regexp = Str.regexp "::" in
196 let res = Str.split regexp s in
197 match res with
198 | name::tys -> `cls name, tys
199 | [] -> `cls s, []
200 (* The function takes a "step" forward in the expansion. We look up the type
201 * constant associated with the given class_name and create a new root type.
202 * A type constant has both a constraint type and assigned type. Which one we
203 * choose depends on if there are any dependent types set in the environment.
204 * If the root type is not a dependent type then we choose the assigned type,
205 * otherwise we choose the constraint type. If there is no constraint type then
206 * we choose the assigned type.
208 and create_root_from_type_constant env class_pos class_name root_ty
209 (pos, tconst) ~as_tyvar_with_cnstr =
210 match get_typeconst env class_pos class_name pos tconst ~as_tyvar_with_cnstr with
211 | None -> env, (fst root_ty, Typing_utils.tany env.tenv)
212 | Some (env, typeconst) ->
213 let env =
214 { env with
215 trail = (`cls class_name, List.map env.ids snd)::env.trail } in
216 (* The type constant itself may contain a 'this' type so we need to
217 * change the this_ty in the environment to be the root as an
218 * expression dependent type.
220 let tenv, new_this = ExprDepTy.apply env.tenv env.dep_tys root_ty in
221 let env = { env with tenv } in
222 let ety_env =
223 { env.ety_env with
224 this_ty = new_this;
225 from_class = None; } in
226 begin
227 match typeconst with
228 | { ttc_type = Some ty; _ }
229 when typeconst.ttc_constraint = None || env.dep_tys = [] ->
230 let tenv, ty = Phase.localize ~ety_env env.tenv ty in
231 { env with dep_tys = []; tenv = tenv }, ty
232 | {ttc_constraint = Some cstr; _} ->
233 let tenv, cstr = Phase.localize ~ety_env env.tenv cstr in
234 let dep_ty = Reason.Rwitness (fst typeconst.ttc_name),
235 (`cls class_name, [tconst]) in
236 (* Append the name of the expanded type constant to each dependent
237 * type.
239 let dep_tys =
240 List.map env.dep_tys (fun (r, (d, s)) -> r, (d, s @ [tconst])) in
241 let tenv, ty =
242 if as_tyvar_with_cnstr then
243 let tenv, tvar = Env.fresh_invariant_type_var tenv pos in
244 Log.log_new_tvar_for_tconst_access tenv pos tvar class_name tconst;
245 let tenv = Typing_utils.sub_type tenv tvar cstr in
246 tenv, tvar
247 else tenv, cstr in
248 { env with dep_tys = dep_ty::dep_tys; tenv }, ty
249 | _ ->
250 let tenv, ty =
251 if as_tyvar_with_cnstr then
252 let tenv, tvar = Env.fresh_invariant_type_var tenv pos in
253 Log.log_new_tvar_for_tconst_access tenv pos tvar class_name tconst;
254 tenv, tvar
255 else
256 let reason = Reason.Rwitness (fst typeconst.ttc_name) in
257 let ty = (reason, Tabstract (AKgeneric (class_name^"::"^tconst), None)) in
258 tenv, ty in
259 let dep_tys =
260 List.map env.dep_tys (fun (r, (d, s)) -> r, (d, s @ [tconst])) in
261 { env with dep_tys; tenv }, ty
264 (* Looks up the type constant within the given class. This also checks for
265 * potential cycles by examining the expansions we have already performed.
267 and get_typeconst env class_pos class_name pos tconst ~as_tyvar_with_cnstr =
269 let class_ = match Env.get_class env.tenv class_name with
270 | None ->
271 Errors.unbound_name_typing class_pos class_name;
272 raise Exit
273 | Some c -> c in
274 let typeconst = match Env.get_typeconst env.tenv class_ tconst with
275 | None ->
276 if not as_tyvar_with_cnstr then
277 Errors.smember_not_found
278 `class_typeconst pos ((Cls.pos class_), class_name) tconst `no_hint;
279 raise Exit
280 | Some tc -> tc in
281 let tc_tuple = ((Cls.name class_), snd typeconst.ttc_name, pos) in
282 let env = {env with typeconsts_seen = tc_tuple :: env.typeconsts_seen} in
283 (* Check for cycles. We do this by combining the name of the current class
284 * with the remaining ids that we need to expand. If we encounter the same
285 * class name + ids that means we have entered a cycle.
287 let cur_tconst = `cls class_name, List.map env.ids snd in
288 let seen = ExprDepTy.to_string cur_tconst in
289 let type_expansions = (pos, seen)::env.ety_env.type_expansions in
290 if List.mem ~equal:(=) (List.map env.ety_env.type_expansions snd) seen then
291 begin
292 let seen = List.rev_map type_expansions snd in
293 Errors.cyclic_typeconst (fst typeconst.ttc_name) seen;
294 raise Exit
295 end;
296 (* If the class is final then we do not need to create dependent types
297 * because the type constant cannot be overridden by a child class
299 let env =
300 { env with
301 ety_env = { env.ety_env with type_expansions };
302 dep_tys = if (Cls.final class_) then [] else env.dep_tys;
303 } in
304 Some (env, typeconst)
305 with
306 Exit -> None
308 (*****************************************************************************)
309 (* Exporting *)
310 (*****************************************************************************)
312 let () = Typing_utils.expand_typeconst_ref := expand_with_env