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.
12 open Typing_dependent_type
15 module Reason
= Typing_reason
16 module Env
= Typing_env
17 module Phase
= Typing_phase
18 module TySet
= Typing_set
23 (* A trail of all the type constants we have expanded. Used primarily for
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 *)
38 (* A list of generics we've seen while expanding. *)
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
= {
52 gen_seen
= TySet.empty
;
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
66 | Reason.Rexpr_dep_type
(_
, p
, e
) ->
67 Reason.Rexpr_dep_type
(root_r
, p
, e
)
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
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
) =
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
), _) ->
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
104 dep_tys
= (root_reason
, dep_ty)::env.dep_tys
;
105 gen_seen
= TySet.add root
env.gen_seen
;
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
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
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
)
131 { env with dep_tys
= [] }, ty
133 | Tabstract
(AKdependent
dep_ty, Some
ty) ->
136 dep_tys
= (root_reason
, dep_ty)::env.dep_tys
} in
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
148 { env with dep_tys
= [] } , (root_reason
, Tunresolved tyl
)
151 Env.expand_type
env.tenv root
in
152 let env = { env with tenv = tenv } in
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
165 | name
::tys
-> `cls name
, tys
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
) ->
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
191 from_class
= None
; } in
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
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
210 Reason.Rwitness
(fst typeconst
.ttc_name
),
211 Tabstract
(AKgeneric
(class_name^
"::"^tconst
), None
) in
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
224 Errors.unbound_name_typing class_pos class_name
;
227 let typeconst = match Env.get_typeconst
env.tenv class_ tconst
with
229 Errors.smember_not_found
230 `class_typeconst
pos (class_.tc_pos
, class_name
) tconst `no_hint
;
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
244 let seen = List.rev_map
type_expansions snd
in
245 Errors.cyclic_typeconst
(fst
typeconst.ttc_name
) seen;
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
253 ety_env = { env.ety_env with type_expansions };
254 dep_tys = if class_.tc_final
then [] else env.dep_tys;
256 Some
(env, typeconst)
260 (*****************************************************************************)
262 (*****************************************************************************)
264 let () = Typing_utils.expand_typeconst_ref
:= expand_with_env