2 * Copyright (c) 2015, Facebook, Inc.
5 * This source code is licensed under the MIT license found in the
6 * LICENSE file in the "hack" directory of this source tree.
13 open Typing_dependent_type
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
28 (* A trail of all the type constants we have expanded. Used primarily for
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 *)
43 (* A list of generics we've seen while expanding. *)
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
= {
57 gen_seen
= TySet.empty
;
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
65 let rec expand_with_env ety_env env ?
(as_tyvar_with_cnstr
= false) reason root ids
=
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
76 | Reason.Rexpr_dep_type
(_
, p
, e
) ->
77 Reason.Rexpr_dep_type
(root_r
, p
, e
)
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
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
, []), _
)),
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)
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
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` *)
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
139 dep_tys
= (root_reason
, dep_ty)::env.dep_tys
;
140 gen_seen
= TySet.add root
env.gen_seen
;
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
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
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
)
166 { env with dep_tys
= [] }, ty
168 | Tabstract
(AKdependent
dep_ty, Some
ty) ->
171 dep_tys
= (root_reason
, dep_ty)::env.dep_tys
} in
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
183 { env with dep_tys
= [] } , (root_reason
, Tunresolved tyl
)
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
198 | name
::tys
-> `cls name
, tys
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
) ->
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
225 from_class
= None
; } in
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
240 List.map
env.dep_tys (fun (r, (d
, s
)) -> r, (d
, s
@ [tconst
])) in
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
248 { env with dep_tys = dep_ty::dep_tys; 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
;
256 let reason = Reason.Rwitness
(fst typeconst
.ttc_name
) in
257 let ty = (reason, Tabstract
(AKgeneric
(class_name^
"::"^tconst
), None
)) in
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
271 Errors.unbound_name_typing class_pos class_name
;
274 let typeconst = match Env.get_typeconst
env.tenv class_ tconst
with
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
;
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
292 let seen = List.rev_map
type_expansions snd
in
293 Errors.cyclic_typeconst
(fst
typeconst.ttc_name
) seen;
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
301 ety_env = { env.ety_env with type_expansions };
302 dep_tys = if (Cls.final
class_) then [] else env.dep_tys;
304 Some
(env, typeconst)
308 (*****************************************************************************)
310 (*****************************************************************************)
312 let () = Typing_utils.expand_typeconst_ref
:= expand_with_env